• Servlet3.0之四:动态注册和Servlet容器初始化


    相关文章

    Servlet3.0之四:动态注册和Servlet容器初始化

    SpringBoot中通过SpringBootServletInitializer如何实现组件加载

    SpringMVC之五:自定义DispatcherServlet配置及配置额外的 servlets 和 filters

    一、servlet3.0中动态注册Servlet、filter、Listener

    为了使动态注册成为可能,ServletContext接口中还添加了以下两类方法:

    1、用来动态地创建Web对象:

    <T extends Filter>createFilter(Java.lang.Class<T> clazz)
    
    <T extends java.util.EventListener> createListener(java.lang.Class<T> clazz)
    
    <T extends Servlet> createServlet(java.lang.Class<T> clazz)

    2、用来动态地将Web对象添加到ServletContext中:

    每类组件注册都提供三个方法,如下:

        //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, Filter filter);
        public FilterRegistration.Dynamic addFilter(String filterName,
            Class <? extends Filter> filterClass);
        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);

    二、动态注册时机及办法

    所谓的动态注册,也只能在初始化时进行注册。在运行时为了安全原因,无法完成注册。在初始化情况下的注册Servlet组件(servlet、filter、listener)有两种方法:

    1. 实现ServletContextListener接口,在contextInitialized方法中完成注册.
    2. 在jar文件中放入实现ServletContainerInitializer接口的初始化器
    3. ServletContainerInitializer接口的方法实现在容器启动阶段为容器动态注册Servlet、Filter和listeners。容器会在应用的启动阶段,调用所有实现ServletContainerInitializer接口类中的onStartup()方法。而Spring 3.2中,则进一步简化了这点,只需要实现WebApplicationInitializer接口就可以了,其中提供了一个相关的实现类--AbstractContextLoaderInitializer,它可以动态注册DispatcherServlet。
    4. 在SpringBoot中通过实现SpringBootServletInitializer接口来初始化容器,示例见《SpringBoot中通过SpringBootServletInitializer如何实现容器初始化

    简单说明如下

    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即可,方便至极!

    3、spring中

    ServletContainerInitializer接口的方法实现在容器启动阶段为容器动态注册Servlet、Filter和listeners。容器会在应用的启动阶段,调用所有实现ServletContainerInitializer接口类中的onStartup()方法。而Spring 3.2中,则进一步简化了这点,只需要实现WebApplicationInitializer接口就可以了,其中提供了一个相关的实现类--AbstractContextLoaderInitializer,它可以动态注册DispatcherServlet。这意味着,只要spring-webmvc.jar放置在web应用的web-inf/lib中,就可以调用Dispatcher servlet了。可以参考如下的例子(来自Spring文档):

    public class MyWebApplicationInitializer implements WebApplicationInitializer { 
     
        @Override 
        public void onStartup(ServletContext container) { 
            ServletRegistration.Dynamic registration = container.addServlet("dispatcher", new DispatcherServlet()); 
            registration.setLoadOnStartup(1); 
            registration.addMapping("/example/*"); 
        } 
    } 

    三、示例

    3.1、ServletContextListener监听器中动态注册servlet、filter、listener示例:

    3.1.1、动态注册Servlet

    FirstServlet.java

    package com.dxz.demo.servlet;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public class FirstServlet extends HttpServlet {
    
        private static final long serialVersionUID = 1L;
    
        private String name;
    
        @Override
        public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            response.setContentType("text/html");
            PrintWriter writer = response.getWriter();
            writer.println("<html><head><title>First servlet" + "</title></head><body>" + name);
            writer.println("</body></html>");
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
    }
    
    package com.dxz.demo.servlet;
    import javax.servlet.Servlet;  
    import javax.servlet.ServletContext;  
    import javax.servlet.ServletContextEvent;  
    import javax.servlet.ServletContextListener;  
    import javax.servlet.ServletException;  
    import javax.servlet.ServletRegistration;  
    import javax.servlet.annotation.WebListener;  
      
    @WebListener  
    public class DynRegListener implements ServletContextListener {  
      
        @Override  
        public void contextDestroyed(ServletContextEvent arg0) {  
        }  
      
        @Override  
        public void contextInitialized(ServletContextEvent sce) {  
            ServletContext servletContext = sce.getServletContext() ;  
            Servlet firstServlet = null ;  
            try {  
                firstServlet = servletContext.createServlet(FirstServlet.class) ;  
            } catch (ServletException e) {  
                e.printStackTrace();  
            }  
            if(firstServlet != null && firstServlet instanceof FirstServlet){  
                ((FirstServlet) firstServlet).setName("Dynamically registered servlet");  
            }  
            ServletRegistration.Dynamic dynamic = servletContext.addServlet("firstServlet", firstServlet) ;  
            dynamic.addMapping("/dynamicservlet") ;  
        }  
      
    }  

    当应用程序启动时,容器会调用监听器的contextInitialized方法,结果是创建了一个FirstServlet实例,并注册和映射到/dynamicservlet。可以利用下面这个路径访问FirstServlet

    http://127.0.0.1:8080/servlet3/dynamicservlet

    3.1.2、动态注册Filter

    package com.dxz.demo.servlet;
    
    import java.io.IOException;
    
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    
    public class FirstFilter implements Filter {
    
        @Override
        public void destroy() {
            System.out.println("destroy() in FirstFilter");
            
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                throws IOException, ServletException {
            System.out.println("doFilter() in FirstFilter");
            chain.doFilter(request, response);
            
        }
    
        @Override
        public void init(FilterConfig arg0) throws ServletException {
            System.out.println("init() in FirstFilter");
            
        }
    
    }
    package com.dxz.demo.servlet;
    import java.util.EnumSet;
    
    import javax.servlet.DispatcherType;
    import javax.servlet.Filter;
    import javax.servlet.FilterRegistration;
    import javax.servlet.Servlet;  
    import javax.servlet.ServletContext;  
    import javax.servlet.ServletContextEvent;  
    import javax.servlet.ServletContextListener;  
    import javax.servlet.ServletException;  
    import javax.servlet.ServletRegistration;  
    import javax.servlet.annotation.WebListener;  
      
    @WebListener  
    public class DynRegListener implements ServletContextListener {  
      
        @Override  
        public void contextDestroyed(ServletContextEvent arg0) {  
        }  
      
        @Override  
        public void contextInitialized(ServletContextEvent sce) {  
            ServletContext servletContext = sce.getServletContext() ;  
            Servlet firstServlet = null ;  
            try {  
                firstServlet = servletContext.createServlet(FirstServlet.class) ;  
            } catch (ServletException e) {  
                e.printStackTrace();  
            }  
            if(firstServlet != null && firstServlet instanceof FirstServlet){  
                ((FirstServlet) firstServlet).setName("Dynamically registered servlet");  
            }  
            ServletRegistration.Dynamic dynamic = servletContext.addServlet("firstServlet", firstServlet) ;  
            dynamic.addMapping("/dynamicservlet") ;  
            
            Filter firstFilter = null ;  
            try {  
                firstFilter = servletContext.createFilter(FirstFilter.class) ;  
            } catch (ServletException e) {  
                e.printStackTrace();  
            }  
            FilterRegistration.Dynamic dynamic2 = servletContext.addFilter("firstFilter", firstFilter);
            EnumSet<DispatcherType> dispatcherTypes = EnumSet 
                    .allOf(DispatcherType.class); 
                dispatcherTypes.add(DispatcherType.REQUEST); 
                dispatcherTypes.add(DispatcherType.FORWARD); 
    
            dynamic2.addMappingForUrlPatterns(dispatcherTypes, true, "/dynamicfilter");
        }  
      
    }  

    结果:

    访问:http://127.0.0.1:8080/servlet3/dynamicfilter

    3.1.3、动态注册Listener

    package com.dxz.demo.servlet;
    
    import java.util.EventListener;
    
    public class FirstListener implements EventListener {
    
        public void init() {
            System.out.println("init() in FirstListener");
        }
    }
    
    
    //注册
    servletContext.addListener(FirstListener.class);

    运行没有成功,报错了。(待跟进...)

    3.2、Servlet容器初始化

    如果你使用过像Struts或Struts2这类Java Web框架,就应该知道,在使用框架之前必须先配置应用程序。一般来说,需要通过修改部署描述符,告诉Servlet容器你正在使用一个框架。例如,要想在应用程序中使用Struts2,可以将以下标签添加到部署描述符中:

    <filter>  
        <filter-name>struts2</filter-name>  
        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>  
    </filter>  
    <filter-mapping>  
        <filter-name>struts2</filter-name>  
        <url-pattern>/*</url-pattern>  
    </filter-mapping>

    但在Servlet3中,就不再需要这些了。框架可以进行打包,自动完成Web对象的首次注册。

    Servlet容器初始化的核心是javax.servlet.ServletContainerInitializer接口。这是一个简单的接口,它只有一个方法:onStartup。在执行任何ServletContext监听器之前,由Servlet容器调用这个方法。

    实现ServletContainerInitializer的类必须用@HandleTypes注解进行标注,以便声明初始化程序可以处理这些类型的类。

    下面是个例子。要把下面的类和文件打成jar包。

    FirstServlet.java

    package servlet;  
      
    import java.io.IOException;  
    import java.io.PrintWriter;  
      
    import javax.servlet.ServletException;  
    import javax.servlet.http.HttpServlet;  
    import javax.servlet.http.HttpServletRequest;  
    import javax.servlet.http.HttpServletResponse;  
      
    public class FirstServlet extends HttpServlet {  
      
        private static final long serialVersionUID = 1L;  
          
        private String name  = "wuhaixu" ;  
          
        @Override  
        public void doGet(HttpServletRequest request, HttpServletResponse response)  
                throws ServletException, IOException {  
            response.setContentType("text/html");  
            PrintWriter writer = response.getWriter() ;  
            writer.println("<html><head><title>First servlet" +   
                    "</title></head><body>" + name);  
            writer.println("</body></html>") ;  
        }  
      
    }  

    javax.servlet.ServletContainerInitializer

    MyServletContainerInitializer.java

    package initializer;  
      
    import java.util.Set;  
      
    import javax.servlet.ServletContainerInitializer;  
    import javax.servlet.ServletContext;  
    import javax.servlet.ServletException;  
    import javax.servlet.ServletRegistration;  
    import javax.servlet.annotation.HandlesTypes;  
      
    import servlet.FirstServlet;  
    @HandlesTypes({FirstServlet.class})  
    public class MyServletContainerInitializer implements ServletContainerInitializer {  
      
        @Override  
        public void onStartup(Set<Class<?>> classes, ServletContext servletContext)  
                throws ServletException {  
            System.out.println("onStartup");  
            ServletRegistration registration = servletContext.addServlet("firstServlet", "servlet.FirstServlet");  
            registration.addMapping("/f");  
            System.out.println("leaving onStartup");  
        }  
      
    }  

    把这些文件和类放在下面的包中,在cmd进行jar包压缩

    结果为:

  • 相关阅读:
    JavaScript中的闭包
    正则表达式(括号)、[中括号]、{大括号}的区别
    写出将字符串中的数字转换为整型的方法,如:“as31d2v”->312,并写出相应的单元测试,正则去掉非数值、小数点及正负号外的字符串
    正则替换实现字符串链接每4位用“-”连接成新的字符串
    memcache搭建
    MySQL优化
    网络优化
    JDK配置及tomcat部署
    oracle中增加pga和sga
    sudo用法
  • 原文地址:https://www.cnblogs.com/duanxz/p/2931952.html
Copyright © 2020-2023  润新知