通过ServletContainerInitializer注册Servlet对象
通过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日)
上一篇: 你们都是不懂艺术的人,哼!