Servlet3.0特性详解-笔记

38 篇文章 0 订阅
10 篇文章 0 订阅

Servlet3.0特性详解-笔记

Apache官方对各版本的解释

Servlet SpecJSP SpecEL SpecWebSocket SpecApache Tomcat versionActual release revision SupportJava Versions
4.0TBD (2.4?)TBD (3.1?)TBD (1.2?)9.0.xNone8 and later
3.12.33.01.18.0.x8.0.157 and later
3.02.22.21.17.0.x7.0.576 and later (WebSocket 1.1 requires 7 or later)
2.52.12.1N/A6.0.x6.0.435 and later
2.42.0N/AN/A5.5.x (archived)5.5.36 (archived)1.4 and later
2.31.2N/AN/A4.1.x (archived)4.1.40 (archived)1.3 and later
2.21.1N/AN/A3.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<? 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);

以上 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<Class<?>> 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<DispatcherType> 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<Class<?>> 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<Class<?>> webAppInitializerClasses, ServletContext servletContext)
            throws ServletException {

        List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();

        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


  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小_杭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值