教你如何五分钟学会在SpringBoot项目中集成Servlet、Filter和Listener

1、前言

  在Servlet3.0之前,定义Servlet、Filter和Listener的方法一般都是在web.xml中进行进行配置的,如下所示:

Servlet在web.xml中的配置:

 <servlet>
 	<servlet-name>HelloServletservlet-name>
 	<servlet-class>servlet.HelloServletservlet-class>
 servlet>
 <servlet-mapping>
 	<servlet-name>HelloServletservlet-name>
 	<url-pattern>/servlet/HelloServleturl-pattern>
 servlet-mapping>

Filter在web.xml中的配置:

 <filter>  
    <filter-name>myServletFilterfilter-name>  
    <filter-class>com.hsh.MyServletFilter2filter-class>  
filter>  
<filter-mapping>  
    <filter-name>myServletFilterfilter-name>  
    <url-pattern>/myHttpServleturl-pattern>  
filter-mapping>  

listener在web.xml中的配置:

<listener>
    <listener-class>com.xxx.xxx.xxxListenerlistener-class>
listener> 

  从Servlet3.0之后,Servlet规范提供了Registration接口(包括了FilterRegistration和ServletRegistration接口),让动态(编程式注册,并非运行时注册)注册Servlet、Filter和Listener的成为了可能。在SpringBoot项目中集成Servlet、Filter和Listener就是使用了Servlet规范的这个特性,从而实现了不依赖web.xml而进行配置。

2、集成Servlet

  SpringBoot为集成Servlet提供了两种方法,一种是通过注解@ServletComponentScan+@WebServlet实现,一种是通过@Bean注解+ServletRegistrationBean类实现。

2.1、注解@ServletComponentScan+@WebServlet实现

  这种方法实现,首先需要在Application.class类上添加@ServletComponentScan注解,需要注意的是:该注解只有在嵌入式的服务器中才会生效,在war的部署方式中,扫描路径是Application.class类所在的包及其同级下子包目录。其次,针对需要定义的Servlet类添加注解@WebServlet,该类需要在能够扫描到的包路径下。

Application.class类中的@ServletComponentScan注解如下所示:

@SpringBootApplication
//可以扫描到first包下的servlet类,且需要使用@WebServlet或@WebFilter
@ServletComponentScan(value= {"com.hsh.learning.servlet","com.hsh.learning.filter","com.hsh.learning.listener"})
@ComponentScan("com.hsh.learning")
public class Application extends SpringBootServletInitializer{
	
	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
	@Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
       return application.sources(Application.class);
    }
}

Servlet类中的@WebServlet注解如下所示:

@WebServlet(name = "firstServlet", urlPatterns = "/firstServlet")  //标记为servlet,以便启动器扫描。
public class FirstServlet extends HttpServlet {
	
	private static final long serialVersionUID = 1L;

	@Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().append("firstServlet");
    }
}

  通过如上配置,SpringBoot集成Servlet的步骤就完成了。其中,@WebServlet注解中的name属性对应web.xml配置中的servlet-name元素,urlPatterns 属性对应url-pattern元素,@WebServlet注解的类就是servlet-class元素表示的类。

2.2、@Bean注解+ServletRegistrationBean类

  这种方法实现,其实就是利用了Spring的@Bean注解把Servlet封装成了ServletRegistrationBean对象,并注入到Spring的容器中。通过@Bean注解的方法注入ServletRegistrationBean对象的方式有两种,一种是直接在Application.class中添加对应的方法,一种是使用单独的配置文件进行配置。其中,需要封装成ServletRegistrationBean对象的Servlet类和前面提到FirstServlet类完全一致,只是不再需要@WebServlet注解而已。配置如下所示:

在Application.class中添加:

@Bean
public ServletRegistrationBean getServletRegistrationBean() {  //一定要返回ServletRegistrationBean
  	//放入自己的Servlet对象实例
 	ServletRegistrationBean bean = new ServletRegistrationBean(new SecondServlet());   
       bean.addUrlMappings("/secondServlet");  //访问路径值
       return bean;
}

在单独的文件中配置:

@Configuration
public class ServletConfig {
	@Bean
	public ServletRegistrationBean getServletRegistrationBean() {  //一定要返回ServletRegistrationBean
	  	//放入自己的Servlet对象实例
	 	ServletRegistrationBean bean = new ServletRegistrationBean(new ThirdServlet());   
        bean.addUrlMappings("/thirdServlet");  //访问路径值
        return bean;
	}
}

  需要注意的是,两种方法其实是等效的,如果配置内容比较多,建议使用单独的文件进行配置,有利于文件的维护。Servlet类和前面提到的FirstServlet类一样,不再重复贴代码了。还有一点儿需要注意的是:如果需要通过该方式注册多个Servlet类,需要保证@Bean注解的方法名,都不一样,否则会出现问题或添加的方法无效。

3、集成Filter

  在SpringBoot中集成Filter的方式和集成Servlet的方式基本上是一样的,只是用的注解和需要动态封装的类不一样,在集成Filter过程中,需要把@WebServlet注解换成@WebFilter注解,需要把ServletRegistrationBean换成FilterRegistrationBean类。具体代码如下:

3.1、注解@ServletComponentScan+@WebFilter实现

  其中@ServletComponentScan注解和集成Servlet的用法一样,如果同时集成了Servlet和Filter,只需要在@ServletComponentScan注解中把两个类型的包目录都包含进去即可。

@WebFilter注解的具体用法如下:

@Order(1)
@WebFilter(filterName="firstFilter", urlPatterns="/*") 
public class FirstFilter implements Filter {
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
	}
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		System.out.println("FirstFilter-doFilter()方法");
		chain.doFilter(request, response);
	}
	@Override
	public void destroy() {
	}
}
3.2、@Bean注解+FilterRegistrationBean类

  这种方法的实现,和集成Servlet也是类似的,同时也提供了直接在Application.class中配置和单独文件配置的方式,方式基本一样,注册多个Filter时,也需要注意方法名不可以重复的问题。

单独配置时的方式:

@Configuration
public class FilterConfig {
	@Bean
    public FilterRegistrationBean<Filter> myFilterBean2(ThirdFilter filter) {
        FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
        filterRegistrationBean.setFilter(filter);//设置为自定义的过滤器FirstFilter
        filterRegistrationBean.addUrlPatterns("/*");//拦截所有请求
        return filterRegistrationBean;
    }
}

  在Application.class中配置的方式,也类似,不在重复贴代码了。

4、集成Listener

  在SpringBoot中集成Listener的方式和集成Servlet、Filter的方式基本上都是一样的,只是用的注解和需要动态封装的类不一样,在集成Listener过程中,需要用到的注解是@WebListener和ServletListenerRegistrationBean类。具体代码如下:

4.1、注解@ServletComponentScan+@WebListener实现

  @WebListener注解的用法如下所示:

@WebListener
public class FirstRequestListener implements ServletRequestListener {

	@Override
	public void requestDestroyed(ServletRequestEvent sre) {
		System.out.println("FirstRequestListener-requestDestroyed()方法");
	}
	@Override
	public void requestInitialized(ServletRequestEvent sre) {
		System.out.println("FirstRequestListener-requestInitialized()方法");
	}
}
4.2、注解@Bean+ServletListenerRegistrationBean实现

  在这种实现中,也需要注意方法名重名的问题。ServletListenerRegistrationBean对象的对应的方法如下所示:

@Configuration
public class ListenerConfig {
	@Bean
	public ServletListenerRegistrationBean<ThirdRequestListener> getListener2(){
		ServletListenerRegistrationBean<ThirdRequestListener> bean = 
				new ServletListenerRegistrationBean<>(new ThirdRequestListener());
		return bean;
	}
}
5、总结

  通过上面的描述,我们可以发现,SpringBoot集成Servlet、Filter、Listener的方法非常相似,如下所示:

  1. 都提供了@WebXXX注解方式和XXXRegistrationBean注册方法两种方案
  2. 都需要使用注解@ServletComponentScan进行包扫描,且只在嵌入式服务器有效
  3. 在XXXRegistrationBean注册方法中,都需要注意方法的命名不能够重复

不同之处在于:

  1. 在XXXRegistrationBean注册方法中,Filter中对应过滤器需要使用@Component注解标示,而Servlet和Listener则不需要,如果在Listener中使用了注解,最常见的副作用就是该监听器可能会被执行两次或多次。
6、示例的源码地址:

  在Gitee提供了完整的源码,感兴趣的童鞋,可以参考:https://gitee.com/hsh2015/servlet-learning

你可能感兴趣的:(Servlet,servlet,filter,listener)