动态加载及Servlet容器加载
动态加载
动态加载是 Servlet 3.0 中的新特性,它可以实现在不重启 Web 应用的情况下加载新的 Web 对象(Servlet、Filter、Listener)。
为了实现动态加载的第一种方式:创建 Web 对象和注册 Web 对象到 ServletContext 中分步进行
ServletContext 接口增加了如下方法,用于动态创建 Web 对象:
<T extends Servlet> T createServlet(java.lang.Class<T> c) throws ServletException // <T extends Filter> T createFilter(java.lang.Class<T> c) throws ServletException // <T extends java.util.EventListener> T createListener(java.lang.Class<T> c) throws ServletException //
例如,如果MyServlet是一个直接或者间接继承 javax.servlet.Servlet 的类,那么就可以通过 createServlet 的方法初始化它:
Servlet myServlet = servletContext.createServlet(MyServlet.class); // 这里使用了反射技术
在创建了 Web 对象之后,可以通过 ServletContext 中的如下方法把它注册到 ServletContext 中
ServletRegistration.Dynamic addServlet(java.lang.String servletName, Servlet servlet) //
FilterRegistration.Dynamic addFilter(java.lang.String filterName, Filter filter
) //
<T extends java.util.EventListener> void addListener(T t) //
实现动态加载的第二种方式:创建 Web 对象和注册 Web 对象到 ServletContext 中一步完成
使用 ServletContext 中的如下方法
ServletRegistration.Dynamic addServlet(java.lang.String servletName, java.lang.Class<? extends Servlet> servletClass) ServletRegistration.Dynamic addServlet(java.lang.String servletName, java.lang.String className
) FilterRegistration.Dynamic addFilter(java.lang.String filterName, java.lang.Class<? extends Filter> filterClass
) FilterRegistration.Dynamic addFilter(java.lang.String filterName, java.lang.String className
) void addListener(java.lang.Class<? extends java.util.EventListener> listenerClass
) void addListener(java.lang.String className
)
要创建或者增加Listener,传递给第一个 addListener 方法的类需要实现以下的一个或者多个接口
ServletContextAttributeListener
ServletRequestListener
ServletRequestAttributeListener
HttpSessionListener
HttpSessionAttributeListener
如果 ServletContext 是用于 ServletContextInitializer 中的 onStartup 方法的参数,那么 Listener 也需要实现 ServletContextListener
动态加载实例
package app14a.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 { // 一个 Servlet,该 Servlet 没有使用 @WebServlet 注解,也没有使用部署描述符来声明。 private static final long serialVersionUID = 1L; // 而是通过使用 Listener 来动态创建、注册、绑定这个 Servlet 并让其生效的。 private String name; public void setName(String name) { this.name = name; } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter writer = response.getWriter(); writer.println("<html>"); writer.println("<head>"); writer.println("<title>First Servlet</title>"); writer.println("</head>"); writer.println("<body>"); writer.println(name); writer.println("</body>"); writer.println("</html>"); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
package app14a.listener; import javax.servlet.Servlet; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.ServletRegistration; import javax.servlet.annotation.WebListener; import app14a.servlet.FirstServlet; @WebListener // 注解 public class DynRegListener implements ServletContextListener { // 监听器 @Override public void contextInitialized(ServletContextEvent sce) { // ServletContext 创建时,容器调用该方法 ServletContext servletContext = sce.getServletContext(); // 获取 ServletContext 对象实例 Servlet firstServlet = null; try { firstServlet = servletContext.createServlet(FirstServlet.class); // 动态创建 Web 对象 } catch (Exception e) { e.printStackTrace(); } if (firstServlet != null && firstServlet instanceof FirstServlet) { ((FirstServlet) firstServlet).setName("Dynamically registered servlet"); } ServletRegistration.Dynamic dynamic = servletContext.addServlet("firstServlet", firstServlet); // 注册到 ServletContext 中 dynamic.addMapping("/dynamic"); // 绑定路径 } @Override public void contextDestroyed(ServletContextEvent sce) { } }
测试结果,Servlet 成功注册到了 ServletContext 中,且绑定到 /dynamic 路径
Servlet容器加载
Servlet 容器加载器也是 Servlet 3.0 中的新特性,对于框架的开发者来说特别有用。
Servlet 容器初始化主要是通过 javax.servlet.ServletContainerInitializer 这个接口。该接口只有一个方法 onStartup。
Servlet 容器中,这个方法在任何 ServletContext 的 Listener 初始化之前都可能会被调用到。
void onStartup(java.util.Set<java.lang.Class<?>> c, ServletContext ctx) throws ServletException //
ServletContainerInitializer 的实现类必须使用 HandleTypes 的注解,以便让加载器能够识别。
package app14a.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.UsefulServlet; @HandlesTypes({UsefulServlet.class}) // ServletContainerInitializer 的实现类必须使用该注解 public class MyServletContainerInitializer implements ServletContainerInitializer { @Override public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException { // 该方法的主要任务就是注册 Web 对象 System.out.println("onStartup"); ServletRegistration registration = servletContext.addServlet("usefulServlet", "servlet.UsefulServlet"); // 创建并注册 registration.addMapping("/useful"); // 绑定访问路径 System.out.println("leaving onStartup"); } }
插件(即 initializer.jar)中的内容
测试结果,