Servlet3.0新特性

前言

Servlet3.0使用了注解来配置web应用,极大缩减了web.xml的配置。

新增注解

注解 作用
@WebServlet 修饰一个Servlet
@WebFilter 修饰一个Filter
@WebListener 修饰一个Listener
@WebInitParam ServletFilter类配置参数
@MultipartConfig 指定Servlet处理文件上传

对Web模块支持

Servlet3.0不再要求所有Web组件全部写在web.xml中。

需要在META-INF中添加Web模块部署描述符web-fragment.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="GBK" ?>
<web-fragment xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd
version="3.0">
<name>模块名</name>
<ordering>
<after><!-- 在哪些模块后加载 -->
<name>模块1</name>
<others/>
<!-- others -->
</after>
<before><!-- 在哪些模块前加载 -->
<name>模块2</name>
<others/>
<!-- others -->
</before>
</ordering>
<!-- 配置Servlet、Filter、Listener -->
</web-fragment>

也可以在web.xml中指定加载顺序

1
2
3
4
5
6
<web-app>
<absolute-ordering>
<name>模块名</name>
<others/>
</absolute-ordering>
</web-app>

注解配置Web组件

不能在web.xml<web-app/>指定metadata-complete="true"

如果metadata-complete设置为true,部署工具必须必须忽略存在于应用的类文件中的所有servlet注解和web fragments
如果metadata-complete属性没有指定或设置为false,部署工具必须检查应用的类文件的注解,并扫描web fragments

1
2
3
4
5
6
7
8
9
@Servlet
public class MyServlet extends HttpServlet{
}
@WebFilter
public class MyFilter implements Filter{
}
@WebListener
public class MyListener implements ServletContextAttributeListener{
}

动态注册Web组件

Servlet3.0提供了ServletContext添加组件的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/** 添加Servlet */ 
public ServletRegistration.Dynamic addServlet(String servletName,
String className);
public ServletRegistration.Dynamic addServlet(String servletName,
Servlet servlet);
public ServletRegistration.Dynamic addServlet(String servletName,
Class<? extends Servlet> servletClass);

/** 添加Filter */
public FilterRegistration.Dynamic addFilter(String filterName,
String className);
public FilterRegistration.Dynamic addFilter(String filterName, Filter filter);
public FilterRegistration.Dynamic addFilter(String filterName,
Class<? extends Filter> filterClass);

/** 添加Listener */
public void addListener(String className);
public <T extends EventListener> void addListener(T t);
public void addListener(Class<? extends EventListener> listenerClass);

通过ServletContextListener注册

1
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
//这里不用@Servlet注解配置
public class MyServlet extends HttpServlet{
}

public class InitListener implements ServletContextListener{
public void contextDestroyed(ServletContextEvent event) {
System.out.println("服务器关闭时会调用该方法");
}

@Override
public void contextInitialized(ServletContextEvent event) {
System.out.println("服务器启动时会调用该方法");
ServletContext context = contextEvent.getServletContext();

//注册一个没有使用@WebServlet注解的类为Servlet
ServletRegistration register = context.addServlet("helloServlet", HelloServlet.class);

//为动态注册的Servlet设定访问URL(可设定多个)
register.addMapping("/hello", "/servlet/hello");

//为动态注册的Servlet设定初始参数
//相当于以前的<init-param>
register.setInitParameter("logPath", "/app/log");
register.setInitParameter("savePath", "/app/upload");
}
}

通过ServletContainerInitializer注册

添加META-INF/services/javax.servlet.ServletContainerInitializer文件。
文件内容为

1
com.ahao.demo.MyServletContainerInitializer

指定自定义的ServletContainerInitializer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@HandlesTypes({ WebApplicationInitializer.class }) 
//@HandlesTypes({ HttpServlet.class })
//实现或者继承HandlesTypes注解中的类的都会被加载
public class MyServletContainerInitializer implements ServletContainerInitializer {

//以Set集合的方式传递注解中指定的类型的所有子类(包括子接口、实现类等)的class对象
public void onStartup(Set<Class<?>> c, ServletContext servletContext)
throws ServletException {
// 动态注册Servlet
ServletContext context = sce.getServletContext();
ServletRegistration.Dynamic servlet = context.addServlet("myServlet", MyServlet.class);
dynamicServlet.addMapping("/myServlet");
dynamicServlet.setAsyncSupported(true);
dynamicServlet.setLoadOnStartup(1);
// 动态注册Filter
FilterRegistration.Dynamic filter = context.addFilter("MyFilter", MyFilter.class);
// 动态注册Listener
context.addListener("com.ahao.demo.MyListener");
}
}

文件上传

HttpServletRequest提供处理文件上传的支持。

  1. Part getPart(String name)
  2. Collection<Part> getParts()

Part对应一个文件上传域,支持访问文件类型、大小、输入流等。
文件上传需要给form表单添加enctype属性,该属性有三个值:

属性值 说明
application/x-www-form-urlencoded 默认编码方式,对formvalue属性进行URL编码
multipart/form-data 二进制方式处理表单数据,上传文件用
text/plain 适用于直接通过表单发送邮件
1
2
3
4
<form action="upload" method="post" enctype="multipart/form-data">
<input type="file" name="myfile" />
<input type="submit"/>
</form>

Servlet中处理上传数据,使用@MultipartConfig修饰,或者在web.xml<servlet>标签中添加<multipart-config/>子标签

1
2
3
4
5
6
7
8
9
10
@WebServlet(name="upload",urlPatterns={"/upload"})
@MultipartConfig
public class UploadServlet extends HttpServlet{
public void doPost(HttpServletRequest request,HttpServletResponse response){
Part part = request.getPart("myfile");
out.println("文件类型:"+part.getContentType()+"<br/>");
out.println("文件大小:"+part.getSize()+"<br/>");
part.write(getServletContext().getRealPath("/uploadFiles")+"/"+"myfile");
}
}

异步处理

支持ServletFilter

Servlet执行耗时操作时,必须等完成操作才能生成响应。
Servlet3.0允许Servlet创建一个线程去执行耗时操作。

先要给Servlet配置允许异步操作

  • xml中配置:
    1
    2
    3
    4
    <servlet>
    <servlet-name>MyServlet</servlet-name>
    <async-supported>true</async-supported>
    </servlet>
  • 在注解中配置:
    1
    2
    3
    @WebServlet(urlPattern="/async",asyncSupported=true)
    public class AsyncServlet extends HttpServlet{
    }

通过ServletRequest创建AsyncContext对象

通过AsyncContext类实现,重复调用创建方法得到同一个AsyncContext对象。

1
2
3
4
5
6
7
8
9
@WebServlet(urlPattern="/async",asyncSupported=true)
public class AsyncServlet extends HttpServlet{
public void doGet(HttpServletRequest request,HttpServletResponse response){
AsyncContext actx = request.startAsync();
//AsyncContext actx = reuqets.startAsync(request,response);
actx.setTimeout(3000);//设置超时时间
actx.start(new Executor(actx));//启用线程
}
}

异步监听

当需要了解异步操作执行细节时,可以使用AsyncListener监听器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//actx.addListener(new MyAsyncListener());
public class MyAsyncListener implements AsyncListener{
public void onStartAsync(AsyncContext event){
System.out.println("异步调用开始");
}
public void onComplete(AsyncContext event){
System.out.println("异步调用完成");
}
public void onError(AsyncContext event){
System.out.println("异步调用异常");
}
public void onTimeout(AsyncContext event){
System.out.println("异步调用超时");
}
}