Servlet Spec | JSP Spec | EL Spec | WebSocket Spec | Apache Tomcat version | Actual release revision Support | Java Versions |
---|---|---|---|---|---|---|
4.0 | TBD (2.4?) | TBD (3.1?) | TBD (1.2?) | 9.0.x | None | 8 and later |
3.1 | 2.3 | 3.0 | 1.1 | 8.0.x | 8.0.15 | 7 and later |
3.0 | 2.2 | 2.2 | 1.1 | 7.0.x | 7.0.57 | 6 and later (WebSocket 1.1 requires 7 or later) |
2.5 | 2.1 | 2.1 | N/A | 6.0.x | 6.0.43 | 5 and later |
2.4 | 2.0 | N/A | N/A | 5.5.x (archived) | 5.5.36 (archived) | 1.4 and later |
2.3 | 1.2 | N/A | N/A | 4.1.x (archived) | 4.1.40 (archived) | 1.3 and later |
2.2 | 1.1 | N/A | N/A | 3.3.x (archived) | 3.3.2 (archived) | 1.1 and later |
在开始异步处理特性之后的Demo:
@WebServlet(urlPatterns = "/demo", asyncSupported = true)
public class AsyncDemoServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException, ServletException {
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
out.println("进入Servlet的时间:" + new Date() + ".");
out.flush();
//在子线程中执行业务调用,并由其负责输出响应,主线程退出
AsyncContext ctx = req.startAsync();
new Thread(new Executor(ctx)).start();
......
}
}
public class Executor implements Runnable {
private AsyncContext ctx = null;
public Executor(AsyncContext ctx){
this.ctx = ctx;
}
public void run(){
try {
//等待十秒钟,以模拟业务方法的执行
Thread.sleep(10000);
PrintWriter out = ctx.getResponse().getWriter();
out.println("业务处理完毕的时间:" + new Date() + ".");
out.flush();
ctx.complete();
} catch (Exception e) {
e.printStackTrace();
}
}
}
//可使用的对异步线程的监听
//异步线程开始时,调用 AsyncListener 的 onStartAsync(AsyncEvent event) 方法;
//异步线程出错时,调用 AsyncListener 的 onError(AsyncEvent event) 方法;
//异步线程执行超时,则调用 AsyncListener 的 onTimeout(AsyncEvent event) 方法;
//异步执行完毕时,调用 AsyncListener 的 onComplete(AsyncEvent event) 方法;
AsyncContext ctx = req.startAsync();
ctx.addListener(new AsyncListener() {
public void onComplete(AsyncEvent asyncEvent) throws IOException {
// 做一些清理工作或者其他
}
...
});
新增加的几个注解,可声明Servlet/过滤器/监听器:
新注释也需要开启标签metadata-complete 属性为true,都在不支持可插性支持
Servlet 3.0 引入了称之为“Web 模块部署描述符片段”的 web-fragment.xml 部署描述文件,该文件必须存放在 JAR 文件的 META-INF 目录下,该部署描述文件可以包含一切可以在 web.xml 中定义的内容。
以此,一些独立功能的可直接以jar包的形式直接在项目中集成,并且不需要对集成项目做任何配置【其配置项在jar包下的web-fragment.xml中即可。实现Setvlet功能模块上的可插性。】
文件对象javax.servlet.http.Part
,有用简易方法write()、delete() 等
,Demo如下:
Part photo = request.getPart("photo");
photo.write("/tmp/photo.jpg");
// 可以将两行代码简化为 request.getPart("photo").write("/tmp/photo.jpg") 一行。
可使用@MultipartConfig 注解 进行配置上传大小,保存路径等配置
实现代码加载Servlet、过滤器、监听器
Servlet3的动态加载servlet的机制,只能在webapp启动初始化时进行注册,并不能在运行时进行注册销毁。
ServletContext新增加的方法【3种不同参数╮(╯_╰)╭】:
/**
* 添加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 void addListener(T t);
public void addListener(Class extends EventListener> listenerClass);
以上 ServletContext 新增的方法要么是在 ServletContextListener 的 contexInitialized 方法中调用,要么是在 ServletContainerInitializer 的 onStartup() 方法中调用。
所以在初始情况下完成注册Servlet组件的两种方法:
1. 在ServletContextListener监听器中完成注册:
public void contextInitialized(ServletContextEvent sce) {
ServletContext sc = sce.getServletContext();
// Register Servlet
ServletRegistration sr = sc.addServlet("DynamicServlet",
"web.servlet.dynamicregistration_war.TestServlet");
sr.setInitParameter("servletInitName", "servletInitValue");
sr.addMapping("/*");
// Register Filter
FilterRegistration fr = sc.addFilter("DynamicFilter",
"web.servlet.dynamicregistration_war.TestFilter");
fr.setInitParameter("filterInitName", "filterInitValue");
fr.addMappingForServletNames(EnumSet.of(DispatcherType.REQUEST),
true, "DynamicServlet");
// Register ServletRequestListener
sc.addListener("web.servlet.dynamicregistration_war.TestServletRequestListener");
}
2. 在jar文件中的servlet组件注册:
需要在jar包含META-INF/services/javax.servlet.ServletContainerInitializer
文件,文件内容为已经实现ServletContainerInitializer
接口的类:
com.learn.servlet3.jardync.CustomServletContainerInitializer
实现的代码:
@HandlesTypes({ JarWelcomeServlet.class })
public class CustomServletContainerInitializer implements
ServletContainerInitializer {
private static final Log log = LogFactory
.getLog(CustomServletContainerInitializer.class);
private static final String JAR_HELLO_URL = "/jarhello";
public void onStartup(Set> c, ServletContext servletContext)
throws ServletException {
log.info("CustomServletContainerInitializer is loaded here...");
log.info("now ready to add servlet : " + JarWelcomeServlet.class.getName());
ServletRegistration.Dynamic servlet = servletContext.addServlet(
JarWelcomeServlet.class.getSimpleName(),
JarWelcomeServlet.class);
servlet.addMapping(JAR_HELLO_URL);
log.info("now ready to add filter : " + JarWelcomeFilter.class.getName());
FilterRegistration.Dynamic filter = servletContext.addFilter(
JarWelcomeFilter.class.getSimpleName(), JarWelcomeFilter.class);
EnumSet dispatcherTypes = EnumSet
.allOf(DispatcherType.class);
dispatcherTypes.add(DispatcherType.REQUEST);
dispatcherTypes.add(DispatcherType.FORWARD);
filter.addMappingForUrlPatterns(dispatcherTypes, true, JAR_HELLO_URL);
log.info("now ready to add listener : " + JarWelcomeListener.class.getName());
servletContext.addListener(JarWelcomeListener.class);
}
}
重要的地方:*其中@HandlesTypes
注解表示CustomServletContainerInitializer
可以处理的类,在onStartup 方法中,可以通过Set
获取得到。*
jar文件中不但可以包含需要自定义注册的servlet,也可以包含应用注解的servlet,具体怎么做,视具体环境而定。
把处理某类事物的servlet组件打包成jar文件,有利于部署和传输,功能不要了,直接去除掉jar即可,方便至极!
应用的框架源码Spring:
在Spring 3.1 中已实现这个接口,实现类为SpringServletContainerInitializer
,加载的接口为WebApplicationInitializer
。
此类会在启动时被加载,如果有实现
WebApplicationInitializer
接口的组件,会在这里调用接口的onStartup
方法,完成加载。
/**
* @author Chris Beams
* @author Juergen Hoeller
* @author Rossen Stoyanchev
* @since 3.1
* @see #onStartup(Set, ServletContext)
* @see WebApplicationInitializer
*/
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(Set> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List initializers = new LinkedList();
if (webAppInitializerClasses != null) {
for (Class> waiClass : webAppInitializerClasses) {
// Be defensive: Some servlet containers provide us with invalid classes,
// no matter what @HandlesTypes says...
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
initializers.add((WebApplicationInitializer) waiClass.newInstance());
}
catch (Throwable ex) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
}
}
}
}
if (initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
return;
}
AnnotationAwareOrderComparator.sort(initializers);
servletContext.log("Spring WebApplicationInitializers detected on classpath: " + initializers);
//这里将执行子类的onStartup实现,进行声明 应该是这个样子的
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
}
public interface WebApplicationInitializer {
/**
* Configure the given {@link ServletContext} with any servlets, filters, listeners
* context-params and attributes necessary for initializing this web application. See
* examples {@linkplain WebApplicationInitializer above}.
* @param servletContext the {@code ServletContext} to initialize
* @throws ServletException if any call against the given {@code ServletContext}
* throws a {@code ServletException}
*/
void onStartup(ServletContext servletContext) throws ServletException;
}
小杭
2017-06-05