欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

通过ServletContainerInitializer注册Servlet对象

程序员文章站 2022-03-05 14:16:36
...

通过ServletContainerInitializer注册Servlet对象

Servlet3通过SPI的机制允许我们自定义一个ServletContainerInitializer的实现类,Servlet容器会在启动的时候自动调用实现类的onStartup方法,我们可以在该方法中进行一些Servlet对象的注册。ServletContainerInitializer接口的定义如下:

public interface ServletContainerInitializer {

    public void onStartup(Set<Class<?>> c, ServletContext ctx)
        throws ServletException; 
}

在下面的代码中自定义了一个ServletContainerInitializer的实现类,叫MyServletContainerInitialier。在其onStartup方法中我们通过入参ServletContext分别注册了一个Servlet、一个监听器和一个Filter。

public class MyServletContainerInitializer implements ServletContainerInitializer {

    @Override
    public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
        //注册Servlet
        javax.servlet.ServletRegistration.Dynamic servletRegistration = ctx.addServlet("hello", HelloServlet.class);
        servletRegistration.addMapping("/servlet3/hello");
        servletRegistration.setLoadOnStartup(1);
        servletRegistration.setAsyncSupported(true);
        
        //注册监听器
        ctx.addListener(StartupListener.class);
        
        //注册Filter
        javax.servlet.FilterRegistration.Dynamic filterRegistration = ctx.addFilter("hello", HelloFilter.class);
        filterRegistration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/servlet3/*");
    }

}

它是基于SPI发现的,这需要我们在classpath下面的META-INF/services下新建一个名为javax.servlet.ServletContainerInitializer的文件,然后在里面加上我们自定义的ServletContainerIntializer的全路径名称。如果有多个实现类,每一个实现类写一行。

HandlesTypes

在上面的示例中我们在onStartup的实现中完全没有使用第一个参数classes,它是干什么用的呢?Servlet3允许我们在定义自定义的ServletContainerInitializer的时候通过@HandlesTypes注解指定在自定义的ServletContainerInitializer初始化的时候,也就是调用onStartup时需要自动检测的类型,然后把它们作为onStartup的第一个参数。这在我们搭建框架的时候非常有用,我们可以通过它对外定义一个接口,让有需要通过它来动态注册Servlet对象的人都可以只需要实现我们开放的接口来注册Servlet对象,他们不需要实现完整的ServletContainerInitializer接口,并按照SPI规范在javax.servlet.ServletContainerInitailizer文件中定义。

下面示例中我们通过@HandlesTypes(WebInitializer.class)指定了在容器初始化的时候需要自动检测WebInitializer类型的Class,然后在onStartup方法体中利用反射一一实例化WebInitializer接口实现类,并调用它们的onStartup方法进行Servlet对象的注册。

@HandlesTypes(WebInitializer.class)
public class MyServletContainerInitializer2 implements ServletContainerInitializer {

	@Override
	public void onStartup(Set<Class<?>> classes, ServletContext ctx) 
			throws ServletException {
		// classes就是自动检测到的类路径下的该初始化类上的@HandlesTypes指定的类,
		// 在本示例中就是WebInitializer接口的实现类

		if (classes != null && !classes.isEmpty()) {
			for (Class<?> initializerClass : classes) {
				if (!initializerClass.isInterface() 
						&& !Modifier.isAbstract(initializerClass.getModifiers())) {
					WebInitializer initializer;
					try {
						initializer = (WebInitializer) initializerClass.newInstance();
						initializer.onStartup(ctx);
					} catch (InstantiationException e) {
						e.printStackTrace();
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}

}



public interface WebInitializer {

    void onStartup(ServletContext ctx) throws ServletException; 
    
}

(注:本文是基于Servlet3.1所写,由Elim写于2018年6月23日)