Servlet 3.0 新特性
Servlet3.0 提供了 @WebServlet
,@WebFilter
等注解,这样便有了抛弃 web.xml
的第一个途径,凭借注解声明 Servlet 和 Filter 来做到这一点。
除了这种方式,Servlet3.0 规范还提供了更强大的功能,可以在运行时动态注册 Servlet ,Filter,listener。以 Servlet 为例,过滤器与监听器与之类似。ServletContext 为动态配置 Servlet 增加了如下方法:
ServletRegistration.Dynamic addServlet(String servletName,Class<? extends Servlet> servletClass)
ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet)
ServletRegistration.Dynamic addServlet(String servletName, String className)
T createServlet(Class clazz)
ServletRegistration getServletRegistration(String servletName)
Map<String,? extends ServletRegistration> getServletRegistrations()
Map<String,? extends ServletRegistration> getServletRegistrations()
其中前三个方法的作用是相同的,只是参数类型不同而已;
通过 #createServlet(Class clazz)
方法创建的 Servlet,通常需要做一些自定义的配置,然后使用 #addServlet(...)
方法来将其动态注册为一个可以用于服务的 Servlet。
两个 #getServletRegistration()
方法主要用于动态为 Servlet 增加映射信息,这等价于在 web.xml
中使用 标签为存在的 Servlet 增加映射信息。
以上新增的方法一般在如下情况调用:
javax.servlet.ServletContextListener
的#contextInitialized(ServletContextEvent sce)
方法中调用。javax.servlet.ServletContainerInitializer
的#onStartup(Set<Class<?>> c, ServletContext ctx)
方法中调用。
ServletContainerInitializer 也是 Servlet 3.0 新增的一个接口,容器在启动时使用 JAR 服务 API(JAR Service API) 来发现 ServletContainerInitializer 的实现类,并且容器将 WEB-INF/lib
目录下 JAR 包中的类都交给该类的 #onStartup(...)
方法处理,我们通常需要在该实现类上使用 @HandlesTypes
注解来指定希望被处理的类,过滤掉不希望给 #onStartup(...)
处理的类。
将Servlet-HelloWorld配置的web.xml
去掉,编写如下类
package com.lzc.servlet;
import javax.servlet.*;
import java.util.Set;
/**
* Created by lzc
* 2020/2/12 21:39
*/
public class CustomServletContainerInitializer implements ServletContainerInitializer {
public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
System.out.println("CustomServletContainerInitializer create ServletHelloWorld ...");
ServletRegistration.Dynamic helloServlet = servletContext.addServlet("HelloWorld", ServletHelloWorld.class);
helloServlet.addMapping("/HelloWorld");
}
}
ServletContext 我们称之为 servlet 上下文,它维护了整个 web 容器中注册的 servlet,filter,listener,以 servlet 为例,可以使用 servletContext.addServlet
等方法来添加 servlet。
这么声明一个 ServletContainerInitializer 的实现类,web 容器并不会识别它,所以,需要借助 SPI 机制来指定该初始化类,这一步骤是通过在项目的 resources 路径下创建 META-INF/services/javax.servlet.ServletContainerInitializer
来做到的,它只包含一行内容:
com.lzc.servlet.CustomServletContainerInitializer
使用 ServletContainerInitializer 和 SPI 机制,我们的 web 应用便可以彻底摆脱 web.xml
了。
方法入参中 Set<Class<?>> set
和 @HandlesTypes
注解的使用
首先编写一个接口和该接口的实现类
public interface Hello {
void hello();
}
public class HelloWorld implements Hello {
@Override
public void hello() {
System.out.println("HelloWorld...");
}
}
改造 CustomServletContainerInitializer
@HandlesTypes({Hello.class})
public class CustomServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
for (Class<?> cls : set) {
try {
Hello hello = (Hello)cls.newInstance();
hello.hello();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
System.out.println("CustomServletContainerInitializer create ServletHelloWorld ...");
ServletRegistration.Dynamic demoServlet = servletContext.addServlet("HelloWorld", ServletHelloWorld.class);
demoServlet.addMapping("/HelloWorld");
}
}
运行上面代码即可看到效果
@HandlesTypes指定需要处理的类,Set<Class<?>> set携带了所有@HandlesTypes指定的类
本作品采用《CC 协议》,转载必须注明作者和本文链接