Servlet3.0特性详解-笔记

Servlet3.0特性详解-笔记

Apache官方对各版本的解释

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

Servlet3.0 特性


主要的一些特性:

  • 异步处理支持 【使Servlet线程资源得到释放,线程本身返回容器,启动异步线程完成业务处理】
  • 新增的注解支持 【使部署文件web.xml内的声明可以标签注释即可】
  • 可插性支持 【支持在jar中进行对项目的声明配置Servlet/过滤器/监听器等】
  • HttpServletRequest 对文件上传的支持 【其实没啥用,已经有解决方案的了╮(╯_╰)╭】
  • ServletContext 的性能增强 【实现代码加载Servlet、过滤器、监听器】

异步处理支持

在开始异步处理特性之后的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/过滤器/监听器:

  • @WebServlet
  • @WebInitParam【初始化参数】
  • @WebFilter
  • @WebListener
  • @MultipartConfig【对上传的若干属性】

新注释也需要开启标签metadata-complete 属性为true,都在不支持可插性支持

可插性支持web-fragment.xml

Servlet 3.0 引入了称之为“Web 模块部署描述符片段”的 web-fragment.xml 部署描述文件,该文件必须存放在 JAR 文件的 META-INF 目录下,该部署描述文件可以包含一切可以在 web.xml 中定义的内容。

以此,一些独立功能的可直接以jar包的形式直接在项目中集成,并且不需要对集成项目做任何配置【其配置项在jar包下的web-fragment.xml中即可。实现Setvlet功能模块上的可插性。】

HttpServletRequest 对文件上传的支持

文件对象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 注解 进行配置上传大小,保存路径等配置

ServletContext 的性能增强

实现代码加载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 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 filterClass); 
  /** 
   * 添加Listener 
   */ 
  public void addListener(String className);     
  public  void addListener(T t);     
  public void addListener(Class listenerClass);

以上 ServletContext 新增的方法要么是在 ServletContextListener 的 contexInitialized 方法中调用,要么是在 ServletContainerInitializer 的 onStartup() 方法中调用。

所以在初始情况下完成注册Servlet组件的两种方法:

  • 实现ServletContextListener接口,在contextInitialized方法中完成注册.
  • 在jar文件中放入实现ServletContainerInitializer接口的初始化器

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> c获取得到。*

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;
}

参考资料:

  • 木杉的博客
  • IBM:Servlet 3.0 新特性详解
  • 聂永的博客

小杭
2017-06-05


你可能感兴趣的:(笔记,java,javaee,web服务框架,Spring)