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 增加映射信息。

以上新增的方法一般在如下情况调用:

  1. javax.servlet.ServletContextListener#contextInitialized(ServletContextEvent sce) 方法中调用。
  2. 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 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!