springboot 注册Servlet

334 阅读3分钟

今天在做web开发的时候,需要在springboot中新增一个自定义的Servlet。之前对这一块不是很了解,趁着这次的机会可以好好了解一下springboot的一些略微底层的一些原理。 众所周知,springboot集成的spring mvc用来接受用户的请求,而在springmvc中最重要的莫过于DispatcherServlet这个类,这个类的本质还是一个Servlet,但是起负责的主要功能非常重要,它主要负责的是请求的转发,即将用户的请求分发到不同的Controller的方法中。对次比较感兴趣的可以读一读源码,非常有意思。

基于WebApplicationInitializer方案

WebApplicationInitializer是一个接口,接口中只有一个方法onStartUp

public interface WebApplicationInitializer {

	/**
	 * Configure the given {@link ServletContext} with any servlets, filters, listeners
	 * context-params and attributes necessary for initializing this web application. See
	 * examples {@linkplain WebApplicationInitializer above}.
	 * @param servletContext the {@code ServletContext} to initialize
	 * @throws ServletException if any call against the given {@code ServletContext}
	 * throws a {@code ServletException}
	 */
	void onStartup(ServletContext servletContext) throws ServletException;
}

理论上在onStartup中实现创建Servlet的逻辑,在Springboot启动的时候应该就会将该Servlet注入到容器中,具体实现原理参考 Spring中WebApplicationInitializer的理解,写的非常详细。具体的实现方案参考:How to Register a Servlet in Java,基本参考该文档实现了,但是在测试过程发现WebAppInitializer无法被执行。回头在仔细阅读第一篇文章,猜测原因springboot与一般的spring mvc项目还是不一样的,在spring mvc项目中,在META-INF/service中应该是有 SpringServletContainerInitializer的描述,才能根据java的spi机制加载WebAppInitializer。很明显在springboot中没有相关的配置,隐藏自定义的WebAppInitializer也就无法被执行,至此次方案失败。

springboot注册Servlet

虽然方案一失败了,但是考虑到既然普通的spring mvc项目都能使用注解的方式来添加servlet,那么springboot中肯定有其他类似的机制来完成Servlet的注入。查找相关资料,终于找到了相关线索,在springboot中注册servlet有如下三种方式。

Spring Boot 注册

Spring Boot 提供了 ServletRegistrationBean, FilterRegistrationBean, ServletListenerRegistrationBean 三个类分别用来注册 Servlet, Filter, Listener,下面是 Servlet ,filter以及servletlistener的示例代码。

@Bean
public ServletRegistrationBean registerServlet() {
    ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new RegisterServlet(), "/registerServlet");
    return servletRegistrationBean;
}

@Bean
public FilterRegistrationBean filterRegistrationBean(){
    ...
}

@Bean
public ServletListenerRegistrationBean servletListenerRegistrationBean(){
    ...
}

组件扫描注册

Servlet 3.0 之前,Servlet、Filter、Listener 这些组件都需要在 web.xml 中进行配置,3.0 之后开始不再需要 web.xml 这个配置文件了,所有的组件都可以通过代码配置或者注解来达到目的。 Servlet 3.0 开始提供了这 3 个注解来代替。

@WebServlet => 代替 servlet 配置
@WebFilter => 代替 filter 配置
@WebListener => 代替 listener 配置

可以直接使用这三种注解来替代xml的配置。

动态注册

如果你想在 Spring Boot 中完成 Servlet、Filter、Listener 的初始化操作,你需要在 Spring 中实现下面这个接口,并注册为一个 bean。

org.springframework.boot.web.servlet.ServletContextInitializer

代码参考如下:

@Component
public class ServletConfig implements ServletContextInitializer {
    @Override
    public void onStartup(ServletContext servletContext) {
        ServletRegistration initServlet = servletContext
                .addServlet("initServlet", InitServlet.class);
        initServlet.addMapping("/initServlet");
    }
}

小结

个人比较推荐第三种实现方式,因为在onStartup方法中可以同时添加servlet,filter,listener三种类型bean。

参考

Spring Boot 注册 Servlet 的三种方法,真是太有用了