教你如何五分鐘學會在SpringBoot項目中集成Servlet、Filter和Listener

1、前言

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

Servlet在web.xml中的配置:

 <servlet>
 	<servlet-name>HelloServlet</servlet-name>
 	<servlet-class>servlet.HelloServlet</servlet-class>
 </servlet>
 <servlet-mapping>
 	<servlet-name>HelloServlet</servlet-name>
 	<url-pattern>/servlet/HelloServlet</url-pattern>
 </servlet-mapping>

Filter在web.xml中的配置:

 <filter>  
    <filter-name>myServletFilter</filter-name>  
    <filter-class>com.hsh.MyServletFilter2</filter-class>  
</filter>  
<filter-mapping>  
    <filter-name>myServletFilter</filter-name>  
    <url-pattern>/myHttpServlet</url-pattern>  
</filter-mapping>  

listener在web.xml中的配置:

<listener>
    <listener-class>com.xxx.xxx.xxxListener</listener-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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章