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的方法非常相似,如下所示:
- 都提供了@WebXXX註解方式和XXXRegistrationBean註冊方法兩種方案
- 都需要使用註解@ServletComponentScan進行包掃描,且只在嵌入式服務器有效
- 在XXXRegistrationBean註冊方法中,都需要注意方法的命名不能夠重複
不同之處在於:
- 在XXXRegistrationBean註冊方法中,Filter中對應過濾器需要使用@Component註解標示,而Servlet和Listener則不需要,如果在Listener中使用了註解,最常見的副作用就是該監聽器可能會被執行兩次或多次。
6、示例的源碼地址:
在Gitee提供了完整的源碼,感興趣的童鞋,可以參考:https://gitee.com/hsh2015/servlet-learning