Servlet3.0新特性
tomcat 7以上的版本都支持Servlet 3.0。
Servlet3.0新特性
- 注解支持;Servlet、Filter、Listener无需在web.xml中进行配置(使用时将web.xml删除即可),可以通过对应注解进行配置;
- 支持Web模块;
- Servlet异步处理;
- 文件上传API简化;
Servlet3.0的注解
- @WebServlet :修饰Servlet类,用于部署该Servlet类。
- @WebFilter:修饰Filter类,用于部署该Filter类
- @WebInitParam:与@WebServlet或@WebFilter注解连用,为它们配置参数
- @MultipartConfig:修饰Servlet类,指定该Servlet类负责处理multipart/form-data类型的请求(主要用于处理上传文件)
- @ServletSecurity:修饰Servlet类,与JAAS(Java验证和授权API)有关的注解
- @HttpConstrait:与@ServletSecurity连用
- @HttpMethodConstrait:与@ServletSecurity连用
ServletContainerInitializer
在web容器启动时为提供给第三方组件机会做一些初始化的工作,例如注册servlet或者filtes等,servlet规范中通过ServletContainerInitializer实现此功能。每个框架要使用ServletContainerInitializer就必须在对应的jar包的META-INF/services 目录创建一个名为javax.servlet.ServletContainerInitializer的文件,文件内容指定具体的ServletContainerInitializer实现类,那么,当web容器启动时就会运行这个初始化器做一些组件内的初始化工作。
一般伴随着ServletContainerInitializer一起使用的还有HandlesTypes注解,通过HandlesTypes可以将感兴趣的一些类注入到ServletContainerInitializerde的onStartup方法作为参数传入。
Tomcat容器的ServletContainerInitializer机制的实现,主要交由Context容器和ContextConfig监听器共同实现,ContextConfig监听器负责在容器启动时读取每个web应用的WEB-INF/lib目录下包含的jar包的META-INF/services/javax.servlet.ServletContainerInitializer,以及web根目录下的META-INF/services/javax.servlet.ServletContainerInitializer,通过反射完成这些ServletContainerInitializer的实例化,然后再设置到Context容器中,最后Context容器启动时就会分别调用每个ServletContainerInitializer的onStartup方法,并将感兴趣的类作为参数传入。
基本的实现机制如图,首先通过ContextConfig监听器遍历每个jar包或web根目录的META-INF/services/javax.servlet.ServletContainerInitializer文件,根据读到的类路径实例化每个ServletContainerInitializer;然后再分别将实例化好的ServletContainerInitializer设置进Context容器中;最后Context容器启动时分别调用所有ServletContainerInitializer对象的onStartup方法。
假如读出来的内容为com.seaboat.mytomcat.CustomServletContainerInitializer,则通过反射实例化一个CustomServletContainerInitializer对象,这里涉及到一个@HandlesTypes注解的处理,被它标明的类需要作为参数值传入到onStartup方法。如下例子:
@HandlesTypes({ HttpServlet.class,Filter.class })
public class CustomServletContainerInitializer implements
ServletContainerInitializer {
public void onStartup(Set<Class<?>> classes, ServletContext servletContext)
throws ServletException {
for(Class c : classes)
System.out.println(c.getName());
}
}
其中@HandlesTypes标明的HttpServlet和Filter两个class被注入到了onStartup方法。所以这个注解也是需要在ContextConfig监听器中处理。由于有了编译器的协助,我们可以方便地通过ServletContainerInitializer的class对象中获取到HandlesTypes对象,进而再获取到注解声明的类数组,如
HandlesTypes ht =servletContainerInitializer.getClass().getAnnotation(HandlesTypes.class);
Class<?>[] types = ht.value();
即可获取到HttpServlet和Filter的class对象数组,后面Context容器调用CustomServletContainerInitializer对象的onStartup方法时作为参数传入。至此,即完成了servlet规范的ServletContainerInitializer初始化器机制。
@WebServlet
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;
@WebServlet(urlPatterns = "/AServlet", initParams = {
@WebInitParam(name = "p1", value = "v1"),
@WebInitParam(name = "p2", value = "v2")
}, loadOnStartup = 1)
public class AServlet extends HttpServlet {
@Override
public void init() throws ServletException {
System.out.println("Servlet3.0 初始化...");
}
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.getWriter().print("hello servlet3.0!!");
}
}
@WebServlet属性:
- name:servlet-name,如果没有显示指定,该Servlet的取值为全限定名。
- value:String[],等价于 urlPatterns 属性,与urlPatterns不能同时使用。
- urlPatterns:String[],指定Servlet url的匹配模式,等价于value。
- loadOnStartup:int,指定Servlet的加载顺序。值代表优先级,正数的值越小,优先级越高,启动时就越先加载(init方法)。
- initParams:webInitParam[],指定初始化参数。
- asyncSupported:boolean,是否支持异步操作。
- displayName:servlet显示名。
@WebFilter
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter(urlPatterns = "/*")
public class AFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("过滤器。。。。。。。。。。。。");
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
}
@WebFilter属性说明:
@WebListener
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
@WebListener
public class AListener implements ServletContextListener {
@Override
public void contextDestroyed(ServletContextEvent arg0) {
System.out.println("死掉了");
}
@Override
public void contextInitialized(ServletContextEvent arg0) {
System.out.println("出生了");
}
}
说明:Servlet、Listener、Filter的执行顺序。
启动的顺序为listener->Filter->servlet
简单记为:理(Listener)发(Filter)师(servlet)
Servlet异步处理
Servlet异步处理就是让Servlet在处理费时的请求时不要阻塞,而是一部分一部分的显示。也就是说,在使用Servlet异步处理之后,页面可以一部分一部分的显示数据,而不是一直卡,等到请求响应结束后一起显示。
在使用异步处理之前,一定要在@WebServlet注解中给出asyncSupported=true,不然默认Servlet是不支持异步处理的。如果存在过滤器,也要设置@WebFilter的asyncSupportedt=true。注意,响应类型必须是text/html,所以要设置:response.setContentType(“text/html;charset=utf-8”);
使用异步处理大致可以分为两步:Servlet正常响应数据,Servlet异常响应数据。
在Servlet正常响应数据时,没什么可说的,可通知response.getWriter().print()来向客户端输出,但输出后要使用response.getWriter().flush()刷新,不然数据只是在缓冲区中,不能向客户端发送数据的。
异步响应数据需要使用request.startAsync()方法获取AsyncContext对象。然后调用AsyncContext对象的start()方法启动异步响应,start()方法需要一个Runnable类型的参数。在Runnable的run()方法中给出异步响应的代码。注意在异步处理线程中使用response做响应后,要使用response.getWriter().flush()来刷新流,不然数据是不能响应到客户端浏览器的。
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(urlPatterns = "/AServlet", asyncSupported = true)
public class AServlet extends HttpServlet {
public void doGet(final HttpServletRequest req, final HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
//兼容IE!如果输出不足512B,IE没有异步效果!
for (int i = 0; i <= 512; i++) {
resp.getWriter().print("a");
}
resp.getWriter().flush();
//得到异步上下文对象
final AsyncContext ac = req.startAsync(req, resp);
/**
* 设置超时时间为10秒,Tomcat需要知道异步响应是否结束,如果响应不结束,虽然客户端浏览器会看到响应的数据,但是鼠标上只是有个圈圈的不行的转啊转的,表示还没有结束响应。Tomcat会等待到超时为止,这个超时的时间可以通过AsyncContext类的getTimeout()方法获取,Tomcat默认为20000毫秒。当然也可以通过此方法方法设置
*/
ac.setTimeout(1000 * 10);
//给上下文对象一个Runnable对象,让它执行这个任务
ac.start(new Runnable() {
public void run() {
println("现在马上开始<br/>", resp);
sleep(2000);
// 2s后,页面逐渐显示字母
for (char c = 'A'; c <= 'Z'; c++) {
println(c + "", resp);
sleep(250);
}
ac.complete();// 通知Tomcat我们已经执行结束了!
}
});
}
public void println(String text, HttpServletResponse resp) {
try {
resp.getWriter().print(text);
resp.getWriter().flush();
} catch (IOException e) {
}
}
public void sleep(long ms) {
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
}
}
}
文件上传
Servlet3.0提供了文件上传的处理方案。只需要在Servlet上添加@MultipartConfig注解即可。
当然也可以为@MultipartConfig注解指定属性值,它有四个属性:
- int filesizeThreshold:指定缓存的大小,当超出这个大小后,文件会保存到磁盘上;
- String location:指定临时文件的目录;
- long maxFilesize:指定上传单个文件的大小限制,如果上传的谁的超出了这个大小,那么就会抛出异常;
- long maxRequestSize:指定整个表单的大小限制。
当在Servlet上使用了@MultipartConfig注解后,那么就可以使用request.getPart(“fieldName”)来获取<input:file>的内容,其中Part表示一个文件表单项。
index.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>测试</title>
</head>
<body>
<form action="/AServlet" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="username"/><br/>
简 历:<input type="file" name="resume"/><br/>
<input type="submit" value="注册"/>
</form>
</body>
</html>
上传文件的Servlet:
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.IOException;
@WebServlet(urlPatterns = "/AServlet")
@MultipartConfig(maxFileSize = 1024 * 1024)
public class AServlet extends HttpServlet {
@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
//getParameter()方法可以使用了!!!
String username = req.getParameter("username");//可以使用了!!!
//获取文件表单字段,对应的Part对象
Part part = req.getPart("resume");
//从Part中获取需要的数据
System.out.println(part.getContentType());//获取上传文件的MIME类型
System.out.println(part.getSize());// 获取上传文件的字节数
System.out.println(part.getName());// 获取文件字段名称
System.out.println(part.getHeader("content-disposition"));// 获取头,这个头中包含了上传文件的名称
part.write("C:/xxx.jpg");// 保存上传文件
// 截取上传文件名称
String filename = part.getHeader("content-disposition");
int start = filename.lastIndexOf("filename=\"") + 10;
int end = filename.length() - 1;
filename = filename.substring(start, end);
System.out.println(filename);
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!