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包压缩

结果为:

posted on 2013-02-25 13:15  duanxz  阅读(1506)  评论(0编辑  收藏  举报