今天在做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。