本文要点
这里主要是总结我在学习Servlet这块时的基础知识。其中,实践代码以及用途介绍较多,而原理性的分析则较少,在学习JavaWeb开发这块,我也渐渐了解到,能够把技术用在开发上了才是首要的,之后再深入了解它其中的原理然后举一反三。技术实践与原理研究两个方面我们都要兼顾,技术实践更加重要,但原理研究我们也不能懈怠。
Servlet基础要点
接下来要介绍的是Servlet最基础的知识,都是平日开发中经常要用到的Servlet技术。
第一个Servlet,编写、部署和运行
这里,通过最简单的代码和步骤来演示如何编写Servlet,同时将它部署到Tomcat上再运行它。
使用Java代码编写一个Servlet
1 | package com.makwan.javaee.a_first; |
这个Servlet会通过字节流向浏览器发送当前的时间信息,并显示在页面上。
编写配置文件web.xml
程序代码就是如此简单,没有其他复杂的功能。但我们还需要到web.xml
配置文件中针对该Servlet进行配置。
1 | <?xml version="1.0" encoding="UTF-8"?> |
编译Servlet类
使用控制台的javac
命令进行这项工作。首先进入到Servlet类所在的路径,然后输入以下指令
1 | set classpath=tomcat所在路径\lib\servlet-api.jar |
打包并部署
- 新建一个文件夹,给它一个名称,比如:
testproj
- 然后在
testproj
里面建立一个文件夹并命名为WEB-INF
- 接着在
WEB-INF
新建一个文件夹classes
- 将包含编译后的.class文件整个目录复制到
classes
目录中, 而web.xml就复制到WEB-INF
目录中 - 最后将
testproj
整个文件夹复制到Tomcat目录的webapps
文件夹中 - 启动Tomcat
访问映射路径
Tomcat启动完毕后,我们就可以打开浏览器输入映射路径访问Servlet了。输入路径http://localhost:8080/testproj/test
,就能看到效果了。
以上是在没有IDE情况下进行Servlet开发,需要手动做更多的工作,也比较繁琐,但是如果用Eclipse/MyEclipse/IntelliJ IDEA这些IDE那就会轻松很多。
在web.xml
中配置Servlet的一点小细节:
- 映射路径必须以
/
或*
开头,但不可以/*.do
这种形式 - 一个Servlet可以被多个url路径映射
- 如果一个url路径映射多个Servlet,那访问url路径访问的肯定是最后一个Servlet
Servlet类型层次
我们首先要了解的是Servlet的层次,这些接口和类就是我们要重点关注的对象。类型层次如下图:
我们可以通过使用这些Servlet类型来创建自定义Servlet。
Servlet接口
1 | public interface Servlet { |
Servlet
中的其中3个方法是生命周期方法:init
,service
,destroy
。所以,这3个方法至关重要,而它们作用就如方法名所示。
顺便简单地说明Servlet的生命周期:
- 客户端请求服务器某个Servlet,Servlet实例被服务器创建
- Servlet实例创建后就立刻调用
init
方法做初始化工作 - 然后Servlet就会调用
service
方法处理客户端的请求 - 当服务器认为Servlet没必要存在时,就会调用
destroy
销毁该Servlet, 比如:关闭服务器
我们想要自定义一个属于自己的Servlet,只要实现该Servlet
接口即可。
1 | package com.makwan.javaee.b_life_cycle; |
当然,在方法里纯粹的只是输出一些消息,这样可以让我们知道这些方法的执行流程而已。
GenericServlet抽象类
1 | public abstract class GenericServlet implements Servlet, ServletConfig, |
GenericServlet
抽象类实现Servlet
接口,也额外实现了ServletConfig
接口,并额外增加了一些重要的方法。
实现Servlet
接口来创建自定义Servlet是比较麻烦的,但继承GenericServlet
抽象类就更加方便。
1 | package com.makwan.javaee.b_life_cycle; |
这里重写了父类的无参init
方法,这样,我们自己可以在生命周期方法init(ServletConfig)
执行时根据我们自身的需求作出一些特殊的初始化工作。
HttpServlet抽象类
首先来看看这个类源码中比较重要的部分。
1 | public abstract class HttpServlet extends GenericServlet { |
HttpServlet
最重要的就是这两个service
方法:
- 参数类型为
ServletRequest
和ServletResponse
的service
我们最清楚不过了,它是Servlet
接口用于处理用户请求的生命周期方法 - 参数类型为
HttpServletRequest
和HttpServletResponse
的service
是HttpServlet
中根据用户的请求方式进行相应的处理的方法
官方提供HttpServlet
更加方便于开发者进行Web开发,一般情况下,我们只要继承该类并重写相应的doXXX
方法即可。
1 | public class HttpServletSubClass extends HttpServlet { |
自启动Servlet
自启动Servlet指的是在Tomcat服务器启动的时候,这个Servlet就会被自动实例化,并执行Servlet的生命周期方法init
执行初始化操作。当我们有这些需求:在Web项目启动的时候预先加载资源或者做一些比较耗时的初始化操作等等,自启动Servlet就非常有用了。
在web.xml
将某个Servlet配置上一个load-on-startup
元素,那么它就是一个自启动Servlet。其中,给load-on-startup
所指定的值必须是正整数,比如:<load-on-startup>1</load-on-startup>
,并且,数值越小表示启动顺序越靠前或优先级越高。
更详细权威的描述那就得看官方文档的描述:
Instantiate an instance of each servlet identified by a <servlet>
element that
includes a <load-on-startup>
element in the order defined by the load-on-startup
element values, and call each servlet instance’s init()
method.
the load-on-startup element specifies the order in which the component is
initialized relative to other Web components in the Web container
官方描述大意就是说:
在<servlet>
元素中配置了load-on-startup
元素并在里头指定其顺序值,
在启动服务器时, 那就会按照在load-on-startup
所指定的顺序值对这些servlet会实例化并且调用init
方法
并且不只有servlet可以这样做,Web容器中的其他Web组件通过配置load-on-startup
也可以达到这种效果
给两个Servlet都配置上load-on-startup
,一个指定10
,一个指定3
。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<servlet>
<servlet-name>AutoStartUpServletA</servlet-name>
<servlet-class>com.makwan.javaee.c_auto_start_up.AutoStartUpServletA</servlet-class>
<load-on-startup>10</load-on-startup>
</servlet>
<servlet>
<servlet-name>AutoStartUpServletB</servlet-name>
<servlet-class>com.makwan.javaee.c_auto_start_up.AutoStartUpServletB</servlet-class>
<load-on-startup>3</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>AutoStartUpServletA</servlet-name>
<url-pattern>/asu_servlet_a</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>AutoStartUpServletB</servlet-name>
<url-pattern>/asu_servlet_b</url-pattern>
</servlet-mapping>
两个在同一个包内做相同的事情的Servlet。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22package com.makwan.javaee.c_auto_start_up;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import java.text.SimpleDateFormat;
import java.util.Date;
public class AutoStartUpServletA extends HttpServlet {
public void init() throws ServletException {
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())
+ " - AutoStartUpServletA init....");
}
}
public class AutoStartUpServletB extends HttpServlet {
public void init() throws ServletException {
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())
+ " - AutoStartUpServletA init....");
}
}
最终启动服务器,控制台上会输出:1
22017-10-21 00:17:39 - AutoStartUpServletB init....
2017-10-21 00:17:39 - AutoStartUpServletA init....
从这可以更清楚地看出:给load-on-startup
配置的正整数数值越小,该Servlet的实例化并执行初始化的顺序就越靠前。
通过自启动Servlet来加载同一个包内的db.properties
:
db.properties
的内容如下1
2
3
4driver=com.mysql.Driver
url=jdbc:mysql//localhost:3306/day05-servlet
name=root
password=root
Servlet在web.xml
中对应的配置:1
2
3
4
5
6
7
8
9
10<servlet>
<servlet-name>LoadPropertiesServlet</servlet-name>
<servlet-class>com.makwan.javaee.c_auto_start_up.LoadPropertiesServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>LoadPropertiesServlet</servlet-name>
<url-pattern>/lpServlet</url-pattern>
</servlet-mapping>
Servlet具体实现:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21package com.makwan.javaee.c_auto_start_up;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import java.io.IOException;
import java.util.Properties;
public class LoadPropertiesServlet extends HttpServlet {
public void init() throws ServletException {
try {
Properties p = new Properties();
// 加载配置文件
p.load(getClass().getResourceAsStream("db.properties"));
// 加载成功后输出
p.list(System.out);
} catch (IOException e) {
e.printStackTrace();
}
}
}
默认Servlet
当用户请求的资源不存在,而我们想要项目使用某个Servlet对这种情况作出统一处理时,默认Servlet就能发挥它的用处了。
配置默认Servlet步骤:
- 将Servlet映射的url路径设置为
/
- 将Servlet配置为自启动Servlet
这两步
1 | <servlet> |
1 | package com.makwan.javaee.d_default; |
ServletConfig接口
这个接口的主要作用是获取Servlet的各种信息。
1 | <servlet> |
在配置web.xml
配置该Servlet的时候,同时配置属于该Servlet的初始化参数,这样在Servlet代码中就可以通过ServletConfig
对象获取。
1 | package com.makwan.javaee.d_config; |
每一个Servlet都可以配置属于自身的参数,这就要根据我们的需求而定了。
ServletContext接口
ServletContext
从字面意思来讲就是Servlet上下文,更通俗地讲这个接口代表整个网站应用。它有很多用途,很多功能在开发中都是经常用到的。
如何获取ServletContext对象?
要使用ServletContext
所附带的功能,我们首先就要搞清楚这个对象是如何获取的。常规方法有以下3种,当然还有其他方法就不一一列举。
GenericServlet
抽象类中的getServletContext
方法ServletRequest
接口中的getServletContext
方法,而HttpServletRequest
接口是它的子接口ServletCongig
接口中的getServletContext
方法
作用1:作为域对象使用
域对象是用于存取数据的,而ServletContext
对象也是4大域对象之一。
这个域对象通常称它为application
域,它的作用域是最大的,所以这个存储在这个域的数据是可以被整个网站应用所共享的。
如果你需要某些公共数据能被整个网站应用的各个部分访问到,那用这个域来存取是最合适不过了。
StoreDataServlet
这个Servlet是把数据存储到application
域中。
1 | <servlet> |
1 | package com.makwan.javaee.e_servlet_context; |
而在GetDataServlet
里则是取出之前在StoreDataServlet
里存储于application
域的数据并显示在页面上。
1 | <servlet> |
1 | package com.makwan.javaee.e_servlet_context; |
作用2:获取网站资源
既然ServletContext
是代表整个网站应用,这样我们就可以通过它来获取网站中的某些资源。
这个接口中有两个方法是经常用于获取网站资源的:
URL getResource(String path)
InputStream getResourceAsStream(String path)
以下是一个文件下载的例子,这里我先在WEB-INF
目录下新建一个文件夹download_resources
,然后在里面存放一个文件即可,这里我放的是一张图片。
1 | <servlet> |
1 | package com.makwan.javaee.e_servlet_context; |
作用3:获取网站路径
有时候我们要构造重定向路径,这时候就需要知道网站应用的名字,这样路径就不会写死了,重定向路径会根据我们所更改的网站名而变化。
1 | <servlet> |
1 | package com.makwan.javaee.e_servlet_context; |
作用4:获取全局参数
有时候,我们需要多个Servlet使用同一个配置参数,这样就可以配置全局参数了,通过ServletContext
对象进行获取。
1 | <!-- 配置全局参数 --> |
1 | package com.makwan.javaee.e_servlet_context; |
作用5:转发
通过ServletContext
可以实现转发的效果。
1 | <servlet> |
1 | package com.makwan.javaee.e_servlet_context; |
以上都是Servlet基本的内容,但很多需求都是可以通过这些技术来实现的,虽然它们简单,但我们更应该重视。
小细节
Q:如何修改MyEclipse生成Servlet源码所用的模版?
- A:到MyEclipse的安装目录中,找到plugins这个文件夹,然后进去在里面找到
com.genuitec.eclipse.wizards_xxxxxx.yyyyyyyyyy.jar
这个文件,你可以查找以com.genuitec.eclipse.wizards_
开头的文件,接着用rar的形式打开这个文件,最后按照你自己所想修改里面的Servlet.java
模版文件即可。
Q:MyEclipse中如何修改网站应用的网站名?
- A:直接修改网站项目的名字是没用的,发布到Tomcat还是原来的名字;这时,只需要
右键项目
-Properties
-在MyEclipse下找到Web
,在这里便可以真正地修改网站名了。
Q:Web项目中的访问路径是如何正确书写得?
- A:访问路径在是Web项目中经常出现的,我们只要遵循一个原则即可:书写任何访问路径时首先以
/
开头,然后再分析这个路径是给哪里用的。
1.如果这个路径是给服务器用的,那么/
就是代表当前网站路径:/网站名
;
2.如果这个路径是给浏览器用的,那么/
就是代表服务器里存放网站应用的目录
,比如使用的是Tomcat,/
则代表/webapps
。