服务器-Servlet3
Servlet 比较早的版本是 2.5,然后 3.0 改动比较大 3.0 改动比较大。Servlet 容器的组件大致可以分为以下几类:
Servlet 3.0 组件
├── 组件申明注解
| ├── @javax.servlet.annotation.WebServlet
| ├── @javax.servlet.annotation.WebFilter
| ├── @javax.servlet.annotation.WebListener
| ├── @javax.servlet.annotation.ServletSecurity
| ├── @javax.servlet.annotation.HttpMethodConstraint
| └── @javax.servlet.annotation.HttpConstraint
|
├── 配置申明
| └── @javax.servlet.annotation.WebInitParam
|
├── 上下文
| └── @javax.servlet.AsyncContext
├── 事件
| └── @javax.servlet.AsyncEvent
├── 监听器
| └── @javax.servlet.AsyncListener
|
├── Servlet 组件注册
| ├── javax.servlet.ServletContext#addServlet()
| └── javax.servlet.ServletRegistration
|
├── Filter 组件注册
| ├── javax.servlet.ServletContext#addFilter()
| └── javax.servlet.FilterRegistration
|
├── 监听器注册
| ├── javax.servlet.ServletContext#addListener()
| └── javax.servlet.AsyncListener
|
└── 自动装配
├── javax.servlet.ServletContainerInitializer
└── @javax.servlet.annotation.HandlesTypes
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 通过注解定义三大组件
servlet3.0 之前,定义 servlet、filter、listener,都需在 web.xml 中进行配置,而 3.0 及后面的版本中 web.xml 不是必须的了,可有可无,可以采用注解的方式来定义这些组件。
Servlet 3.0 的部署描述文件 web.xml 的顶层标签有一个 metadata-complete 属性,该属性指定当前的部署描述文件是否是完全的,如果设置为 true,就是说整个 web 的配置信息都在 web.xml 中指定,则容器在部署时将只依赖 web.xml 文件,忽略所有的注解(同时也会跳过 web-fragment.xml 的扫描,亦即禁用可插性支持,具体请看后文关于可插性支持可插性支持) 的讲解);如果不配置该属性,或者将其设置为 false,则表示启用注解支持(和可插性支持)
# @WebServlet
属性名 | 类型 | 描述 |
---|---|---|
name | String | 指定 Servlet 的 name 属性,如果没有显式指定,则该 Servlet 的取值即为类的全限定名。 |
value | String[] | 该属性等价于 urlPatterns 属性,value 和 urlPatterns 两个属性不能同时使用。 |
urlPatterns | String[] | 指定一组 Servlet 的 URL 匹配模式 |
loadOnStartup | int | 指定 Servlet 的加载顺序 |
initParams | WebInitParam[] | 指定一组 Servlet 初始化参数 |
asyncSupported | boolean | 声明 Servlet 是否支持异步操作模式,默认是 false,后面会在 servlet 异步处理的部分讲解 |
description | String | 该 Servlet 的描述信息 |
displayName | String | 该 Servlet 的显示名,通常配合工具使用 |
@WebServlet(
name = "myServlet", // servlet 名称
urlPatterns = "/myServlet", // 哪些请求会被这个servlet处理
// value 参数同 urlPatterns,二者选其一
loadOnStartup = 1, // 设置servlet加载属性,当值为0或者大于0时,表示容器在应用启动时就加载这个servlet;当是一个负数时或者没有指定时,则指示容器在该servlet被选择时才加载;正数的值越小,启动该servlet的优先级越高。
initParams = {
@WebInitParam(name = "param1", value = "value1"),
@WebInitParam(name = "param2", value = "value2")
}, // 定义servlet初始化参数
asyncSupported = false // 是否支持异步
)
2
3
4
5
6
7
8
9
10
11
12
13
14
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
@WebServlet(
name = "myServlet", // servlet 名称
urlPatterns = "/myServlet", // 哪些请求会被这个servlet处理
// value 参数同 urlPatterns,二者选其一
loadOnStartup = 1, // 设置servlet加载属性,当值为0或者大于0时,表示容器在应用启动时就加载这个servlet;当是一个负数时或者没有指定时,则指示容器在该servlet被选择时才加载;正数的值越小,启动该servlet的优先级越高。
initParams = {
@WebInitParam(name = "param1", value = "value1"),
@WebInitParam(name = "param2", value = "value2")
}, // 定义servlet初始化参数
asyncSupported = false // 是否支持异步
)
public class MyServlet extends HttpServlet {
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Enumeration initParameterNames = this.getInitParameterNames();
while (initParameterNames.hasMoreElements()) {
String initParamName = initParameterNames.nextElement();
String initParamValue = this.getInitParameter(initParamName);
try {
resp.getWriter().append(String.format("%s:%s", initParamName, initParamValue));
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
运行程序,通常我们会将 web 项目部署到 tomcat 中,tomcat 目前已经到 10 了,每个版本支持的 servlet 不一样,对应关系如下表
Servlet Spec | JSP Spec | EL Spec | WebSocket Spec | Authentication (JASIC) Spec | Apache Tomcat Version | Latest Released Version | Supported Java Versions |
---|---|---|---|---|---|---|---|
5.0 | 3.0 | 4.0 | 2.0 | 2.0 | 10.0.x | 10.0.2 | 8 and later |
4.0 | 2.3 | 3.0 | 1.1 | 1.1 | 9.0.x | 9.0.43 | 8 and later |
3.1 | 2.3 | 3.0 | 1.1 | 1.1 | 8.5.x | 8.5.63 | 7 and later |
3.1 | 2.3 | 3.0 | 1.1 | N/A | 8.0.x (superseded) | 8.0.53 (superseded) | 7 and later |
3.0 | 2.2 | 2.2 | 1.1 | N/A | 7.0.x | 7.0.108 | 6 and later (7 and later for WebSocket) |
2.5 | 2.1 | 2.1 | N/A | N/A | 6.0.x (archived) | 6.0.53 (archived) | 5 and later |
2.4 | 2.0 | N/A | N/A | N/A | 5.5.x (archived) | 5.5.36 (archived) | 1.4 and later |
2.3 | 1.2 | N/A | N/A | N/A | 4.1.x (archived) | 4.1.40 (archived) | 1.3 and later |
2.2 | 1.1 | N/A | N/A | N/A | 3.3.x (archived) | 3.3.2 (archived) | 1.1 and later |
tomcat版本和servlet版本对应关系:https://tomcat.apache.org/whichversion.html
# @WebFilter
属性名 | 类型 | 描述 |
---|---|---|
filterName | String | 指定过滤器的 name 属性 |
value | String[] | 该属性等价于 urlPatterns 属性,value 和 urlPatterns 两个属性不能同时使用。 |
urlPatterns | String[] | 指定一组过滤器的 URL 匹配模式 |
servletNames | String[] | 指定过滤器将应用于哪些 Servlet,取值是 @WebServlet 中的 name 属性的取值,或者是 web.xml 中 的取值。 |
dispatcherTypes | DispatcherType | 指定过滤器的转发模式,具体取值包括:ASYNC、ERROR、FORWARD、INCLUDE、REQUEST。 |
initParams | WebInitParam[] | 指定一组过滤器初始化参数,等价于 标签。 |
asyncSupported | boolean | 声明过滤器是否支持异步操作模式 |
description | String | 该过滤器的描述信息 |
displayName | String | 该过滤器的显示名,通常配合工具使用 |
dispatcherTypes 参数有 4 个值
值 | 说明 |
---|---|
REQUEST | 默认值,通过前端发送过来的请求会被拦截 |
FOWARD | 通过 forward 转发过来的请求会被拦截 |
INCLUDE | 通过 include 过来的请求会被拦截 |
ERROR | 这个可能开发者不是很熟悉,意思是当触发了一次 error 的时候,就会走一次指定的过滤器 |
ASYNC | 会拦截异步的请求 |
@WebFilter(
filterName = "myFilter", // filter名称
urlPatterns = "/*", //拦截的地址
servletNames = "myServlet", //拦截的servlet名称列表
dispatcherTypes = {DispatcherType.REQUEST},//拦截的请求类型
initParams = {@WebInitParam(name = "p1", value = "v1")}, //定义Filter初始化参数列表
asyncSupported = false // 是否支持异步
)
2
3
4
5
6
7
8
9
import javax.servlet.DispatcherType;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.http.HttpFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebFilter(
filterName = "myFilter", // filter名称
urlPatterns = "/*", //拦截的地址
servletNames = "myServlet", //拦截的servlet名称列表
dispatcherTypes = {DispatcherType.REQUEST},//拦截的请求类型
initParams = {@WebInitParam(name = "p1", value = "v1")}, //定义Filter初始化参数列表
asyncSupported = false // 是否支持异步
)
public class MyFilter extends HttpFilter {
@Override
protected void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
System.out.println(req.getRequestURL()); // 输出请求的地址
super.doFilter(req, res, chain);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# @WebListener
属性名 | 类型 | 是否可选 | 描述 |
---|---|---|---|
value | String | 是 | 该监听器的描述信息 |
监听器类 | 说明 |
---|---|
ServletContextListener | 监听 servlet 上下文的创建和销毁,web 容器启动和销毁的时候会被调用 |
ServletContextAttributeListener | 监听 ServletContext 中属性变化 |
HttpSessionListener | 监听 session 的创建和销毁 |
HttpSessionActivationListener | Activate 与 Passivate 是用于置换对象的动作,当 session 对象为了资源利用或负载平衡等原因而必须暂时储存至硬盘或其它储存器时(透 过对象序列化),所作的动作称之为 Passivate,而硬盘或储存器上的 session 对象重新加载 JVM 时所采的动作称之为 Activate,所以容 易理解的,sessionDidActivate()与 sessionWillPassivate()分别于 Activeate 后与将 Passivate 前呼叫。 |
HttpSessionAttributeListener | session 中添加值或者移除值的时候会被调用 |
HttpSessionBindingListener | 实现了 HttpSessionBindingListener 的类,在和 session 绑定、解除绑定时触发其事件 |
ServletRequestListener | 监听 request 的创建和销毁 |
ServletRequestAttributeListener | requet 中添加值和删除值的时候会被调用 |
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
/**
* 通过 @WebListener 注解定义监听器
*/
@WebListener("自定义的ServletContextListener")
public class MyServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println(this.getClass().getName() + ",监听servlet容器的启动!!!!!");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println(this.getClass().getName() + ",监听servlet容器的销毁!!!!!");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# @MultipartConfig
属性名 | 类型 | 是否可选 | 描述 |
---|---|---|---|
fileSizeThershold | int | 是 | 当前数据量大于该值时,内容将被写入文件。 |
location | String | 是 | 存放生成文件的地址 |
maxFileSize | long | 是 | 允许上传的文件最大值,默认为-1,表示没有限制 |
maxRequestSize | long | 是 | 针对 multipart/form-data 请求的最大数量,默认为-1,表示没有限制 |
<!--index.html文件部分代码-->
<form action="/HelloWorld/UpLoad" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit" name="upload" value="上传">
</form>
2
3
4
5
6
/*上传文件的Servlet类*/
//设置访问活着调用这个Servlet的路径
@WebServlet("/UpLoad")
//说明该Servlet处理的是multipart/form-data类型的请求
@MultipartConfig
public class UpLoad extends HttpServlet{
private static final long serialVersionUID = 1L;
public UpLoad(){
super();
}
protected void doGet(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException{
this.doPost(request, response);
}
protected void doPost(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException{
//说明输入的请求信息采用UTF-8编码方式
request.setCharacterEncoding("utf-8");
response.setContentType("text/html; charset=UTF-8");
PrintWriter out = response.getWriter();
//Servlet3.0中新引入的方法,用来处理multipart/form-data类型编码的表单
Part part = request.getPart("file");
//获取HTTP头信息headerInfo=(form-data; name="file" filename="文件名")
String headerInfo = part.getHeader("content-disposition");
//从HTTP头信息中获取文件名fileName=(文件名)
String fileName = headerInfo.substring(headerInfo.lastIndexOf("=") + 2, headerInfo.length() - 1);
//获得存储上传文件的文件夹路径
String fileSavingFolder = this.getServletContext().getRealPath("/UpLoad");
//获得存储上传文件的完整路径(文件夹路径+文件名)
//文件夹位置固定,文件夹采用与上传文件的原始名字相同
String fileSavingPath = fileSavingFolder + File.separator + fileName;
//如果存储上传文件的文件夹不存在,则创建文件夹
File f = new File(fileSavingFolder + File.separator);
if(!f.exists()){
f.mkdirs();
}
//将上传的文件内容写入服务器文件中
part.write(fileSavingPath);
//输出上传成功信息
out.println("文件上传成功~!");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/*下载文件*/
@WebServlet("/DownLoad")
public class DownLoad extends HttpServlet{
protected void doGet(HttpServletRequest request ,HttpServletResponse response)throws ServletException,IOException{
this.doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException,IOException{
try{
//服务器相对路径
String filepath = "WEB-INF/web.xml";
//服务器绝对路径
String fullFilePath = getServletContext().getRealPath(filepath);
System.out.println(fullFilePath);
/*打开文件,创建File类型的文件对象*/
File file = new File(fullFilePath);
/*如果文件存在*/
if(file.exists()){
System.out.println("文件存在");
/*获得文件名,并采用UTF-8编码方式进行编码,以解决中文问题*/
String filename = URLEncoder.encode(file.getName(), "UTF-8");
System.out.println(filename);
/*重置response对象*/
response.reset();
//设置文件的类型,xml文件采用text/xml类型,详见MIME类型的说明
response.setContentType("text/xml");
//设置HTTP头信息中内容
response.addHeader("Content-Disposition","attachment:filename=\"" + filename + "\"" );
//设置文件的长度
int fileLength = (int)file.length();
System.out.println(fileLength);
response.setContentLength(fileLength);
/*如果文件长度大于0*/
if(fileLength!=0){
//创建输入流
InputStream inStream = new FileInputStream(file);
byte[] buf = new byte[4096];
//创建输出流
ServletOutputStream servletOS = response.getOutputStream();
int readLength;
//读取文件内容并写到response的输出流当中
while(((readLength = inStream.read(buf))!=-1)){
servletOS.write(buf, 0, readLength);
}
//关闭输入流
inStream.close();
//刷新输出缓冲
servletOS.flush();
//关闭输出流
servletOS.close();
}
}else {
System.out.println("文件不存在~!");
PrintWriter out = response.getWriter();
out.println("文件 \"" + fullFilePath + "\" 不存在");
}
}catch(Exception e){
System.out.println(e);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# ServletContainerInitializer
在web容器启动时为提供给第三方组件机会做一些初始化的工作,例如注册servlet或者filtes等,servlet规范中通过ServletContainerInitializer实现此功能。Servlet 容器启动会扫描当前应用的每一个 jar 包 ServletContainerInitializer 的实现
每个框架要使用ServletContainerInitializer就必须在对应的jar包的META-INF/services 目录创建一个名为javax.servlet.ServletContainerInitializer的文件,文件内容指定具体的ServletContainerInitializer实现类,那么,当web容器启动时就会运行这个初始化器做一些组件内的初始化工作。
一般伴随着ServletContainerInitializer一起使用的还有HandlesTypes注解,通过HandlesTypes可以将感兴趣的一些类注入到ServletContainerInitializerde的onStartup方法作为参数传入。
在 Servlet 3.0 时支持注解启动,其中 ServletContainerInitializer 和 HandlesTypes 都是 Servlet 3.0 的规范
ServletContainerInitializer 只有一个方法 onStartup
HandlesTypes 感兴趣的类,启动时会通过 onStartup 传递给 clazzs 参数。HandlesTypes 会找到 HelloServert 所有的子类(不包括 HelloServert 自己)
@HandlesTypes(HelloServert.class)
public class MyServletContainerInitializer implements ServletContainerInitializer {
/**
* @param c @HandlesTypes 指定,HelloServert 子类
* @param ServletContext 注册三大组件(Servlet Filter Listener)
*/
@Override
public void onStartup(Set<Class<?>> set, ServletContext ctx) throws ServletException {
// 1. 处理感兴趣的类
System.out.println(set);
// 2.1. 注册 Servert
ServletRegistration.Dynamic servlet = ctx.addServlet("myServlet", HelloServert.class);
servlet.addMapping("/*");
// 2.2. 注册 Listener
ctx.addListener(MyServletContextListener.class);
// 2.3. 注册 Filter
FilterRegistration.Dynamic filter = ctx.addFilter("myFileter", MyFilter.class);
filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST),true, "/*");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
在org/springframework/spring-web/5.3.1/spring-web-5.3.1.jar!/META-INF/services/下就配置了文件javax.servlet.ServletContainerInitializer