在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而进行配置。
SpringBoot为集成Servlet提供了两种方法,一种是通过注解@ServletComponentScan+@WebServlet实现,一种是通过@Bean注解+ServletRegistrationBean类实现。
这种方法实现,首先需要在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元素表示的类。
这种方法实现,其实就是利用了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注解的方法名,都不一样,否则会出现问题或添加的方法无效。
在SpringBoot中集成Filter的方式和集成Servlet的方式基本上是一样的,只是用的注解和需要动态封装的类不一样,在集成Filter过程中,需要把@WebServlet注解换成@WebFilter注解,需要把ServletRegistrationBean换成FilterRegistrationBean类。具体代码如下:
其中@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() {
}
}
这种方法的实现,和集成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中配置的方式,也类似,不在重复贴代码了。
在SpringBoot中集成Listener的方式和集成Servlet、Filter的方式基本上都是一样的,只是用的注解和需要动态封装的类不一样,在集成Listener过程中,需要用到的注解是@WebListener和ServletListenerRegistrationBean类。具体代码如下:
@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()方法");
}
}
在这种实现中,也需要注意方法名重名的问题。ServletListenerRegistrationBean对象的对应的方法如下所示:
@Configuration
public class ListenerConfig {
@Bean
public ServletListenerRegistrationBean<ThirdRequestListener> getListener2(){
ServletListenerRegistrationBean<ThirdRequestListener> bean =
new ServletListenerRegistrationBean<>(new ThirdRequestListener());
return bean;
}
}
通过上面的描述,我们可以发现,SpringBoot集成Servlet、Filter、Listener的方法非常相似,如下所示:
不同之处在于:
在Gitee提供了完整的源码,感兴趣的童鞋,可以参考:https://gitee.com/hsh2015/servlet-learning