Listener,Filter 和 Servlet 是 Java web 开发过程中常用的三个组件, 其中 Filter 组件的使用频率最高, 经常被用来做简单的权限处理, 请求头过滤和防止 XSS 攻击等. 如果我们使用的是传统的 Spring MVC 进行开发, 那么只需要在 Tomcat 的 Web.xml 文件中进行如下配置即可:
- <!-- 配置 Listener -->
- <listener>
- <listener-class>org.springframework.Web.util.WebAppRootListener</listener-class>
- </listener>
- <listener>
- <listener-class>org.springframework.Web.context.ContextLoaderListener</listener-class>
- </listener>
- <!-- 配置 Filter, 这边配置了一个 Filter, 但是匹配了多个 url-pattern-->
- <!-- 以 url-partern 方式配置的 filter 中, 如果有多个与当前请求匹配, 则按 web.xml 中 filter-mapping 出现的顺序来运行 -->
- <filter>
- <filter-name>filter1</filter-name>
- <filter-class>com.csx.MyFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>filter1</filter-name>
- <url-pattern>/url/a/*</url-pattern>
- </filter-mapping>
- <filter-mapping>
- <filter-name>filter1</filter-name>
- <url-pattern>/url/b/*</url-pattern>
- </filter-mapping>
- <!-- 配置 Servlet-->
- <servlet>
- <servlet-name>springmvc</servlet-name>
- <servlet-class>org.springframework.Web.servlet.DispatcherServlet</servlet-class>
- <init-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>classpath:springmvc.spring.xml</param-value>
- </init-param>
- <load-on-startup>1</load-on-startup>
- </servlet>
- <servlet-mapping>
- <servlet-name>springmvc</servlet-name>
- <!-- 这边不建议写成 /* -->
- <url-pattern>/</url-pattern>
- </servlet-mapping>
PS: 在容器启动的时候, 上面三个组件启动的顺序是 Listener --> Filter --> Servlet, 这边安利一个记忆的方法: 把启动顺序记忆成 "理 (Listener) 发(Filter)师(Servlet)" 即可.
在 Web.xml 中配置这三个组件比较简单, 但是使用 Spring-Boot 开发时使用的是嵌入式容器, 并没有 Web.xml 文件让我们进行配置. 那么在 Spring-Boot 中到底要怎么配置 Listener,Filter 和 Servlet 等组件呢?
本篇博客以 Filter 为列, 介绍下在 Spring-Boot 中怎么配置 Listener,Filter 和 Servlet 等组件.
方式一: 将 Filter 声明为 bean
这边我们先来自定义一个 Filter, 这个 Filter 的作用是统计一个接口的调用时间.
- public class TimeConsumingCalculationFilter implements Filter {
- @Override
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
- HttpServletRequest httpRequest=(HttpServletRequest)request;
- long startTime = System.nanoTime();
- logger.info(">>>>>>> Begin ["+httpRequest.getRequestURI()+"]...");
- try{
- chain.doFilter(request, response);
- }finally {
- long endTime = System.nanoTime();
- logger.info(">>>>>>> End ["+httpRequest.getRequestURI()+"]["+(endTime-startTime)/1000/1000.0+"ms].");
- }
- }
- }
在 Spring-Boot 中配置上面的 Filter, 我们只需要在 @Configuration 文件中做如下配置即可:
- @Configuration
- public class WebConfig {
- private static Logger logger = LoggerFactory.getLogger(WebConfig.class);
- @Bean
- public Filter filter1(){
- return new TimeConsumingCalculationFilter();
- }
- }
上面的 Filter 默认会拦截所有请求. 假如我们想要配置多个拦截器的话, 只要再加一个 Bean 方法就可以了.
- @Configuration
- public class WebConfig {
- private static Logger logger = LoggerFactory.getLogger(WebConfig.class);
- @Bean
- public Filter filter1(){
- return new TimeConsumingCalculationFilter();
- }
- @Bean
- public Filter filter2() {
- return new TimeConsumingCalculationFilter2();
- }
- }
上面的配置代码配置了两个 Filter, 两个 Filter 默认都会拦截所有请求, 拦截的顺序是: filter1-->filter2. 这边的逻辑是先配置的 Filter 先拦截, 后配置的 Filter 后拦截. 当然, 如果我们想明确指定拦截顺序的话可以借助 @Order 注解. 但是需要注意的是这个注解一定要加在定义的类上面.
- @Order(Ordered.LOWEST_PRECEDENCE - 2)
- public class TimeConsumingCalculationFilter implements Filter {
- @Override
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
- //your logic
- }
- }
- @Order(Ordered.LOWEST_PRECEDENCE - 1)
- public class TimeConsumingCalculationFilter implements Filter {
- @Override
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
- //your logic
- }
- }
PS: @Order 中的 value 值越大, 执行的优先级越小. 值越小, 优先级越大. 当我们自定义执行顺序的时候, 建议使用 @Order(Ordered.LOWEST_PRECEDENCE - 1)这种形式配置, Ordered.LOWEST_PRECEDENCE 减去的值越大, 优先级越高, 这样看起来比较直观.
另外, Spring 还提供了一个 OrderedFilter 接口, 这个接口是 Filter 和 Ordered 的组合接口, 原理和上面的是一样的. 大家可以看情况使用.
上面的这种配置方式优点是配置起来非常简单, 但是缺点也比较明显, 就是配置不够灵活, 默认会拦截所有请求.
方式二:@WebFilter 方式
@WebFilter 注解是 Servlet 中提供的注解, Spring 也支持这个注解.@WebFilter 能进行细粒度的进行配置, 比上面的方式更加灵活.
- @Configuration
- public class WebConfig {
- // 可以自定义 url-pattern
- @WebFilter(urlPatterns="/*")
- @Order(Ordered.LOWEST_PRECEDENCE - 2)
- // 这边如果不加 `@Configuration`, 需要通过 `@ServletComponentScan` 扫描 `Listener`,`Filter` 和 `Servlet` 这三个组件
- @Configuration
- public class TimeConsumingCalculationFilter implements Filter {
- @Override
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
- //your logic
- }
- }
- @WebFilter(urlPatterns="/*")
- @Order(Ordered.LOWEST_PRECEDENCE - 2)
- // 这边如果不加 `@Configuration`, 需要通过 `@ServletComponentScan` 扫描 `Listener`,`Filter` 和 `Servlet` 这三个组件
- @Configuration
- public class TimeConsumingCalculationFilter2 implements Filter {
- @Override
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
- //your logic
- }
- }
- }
通过 @WebFilter 注解的方式配置 Filter 简单, 而且能够自定义 url-pattern 和拦截顺序.
方式三: 使用 FilterRegistrationBean 配置
- @Configuration
- public class WebConfig {
- private static Logger logger = LoggerFactory.getLogger(WebConfig.class);
- @Bean
- public FilterRegistrationBean<Filter> filter1() {
- FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();
- registrationBean.setName("filter1");
- registrationBean.setOrder(Ordered.LOWEST_PRECEDENCE - 2);
- registrationBean.setFilter(new TimeConsumingCalculationFilter());
- registrationBean.addUrlPatterns("/foo/*");
- return registrationBean;
- }
- @Bean
- public FilterRegistrationBean<Filter> filter2() {
- FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();
- registrationBean.setName("filter2");
- registrationBean.setOrder(Ordered.LOWEST_PRECEDENCE - 3);
- registrationBean.setFilter(new TimeConsumingCalculationFilter1());
- registrationBean.addUrlPatterns("/*");
- registrationBean.addInitParameter("key1","value1");
- registrationBean.addInitParameter("key2","value2");
- // 通过 Servlet name 匹配 Filter, 不建议使用
- registrationBean.addServletNames("name1");
- return registrationBean;
- }
- }
注意点:
FilterRegistrationBean 与 Filter 之间是一对一关系.
如果存在多个 FilterRegistrationBean 需要调用其 setName(String name) 为其声明唯一名称, 否则只有第一个注册成功的有效.
如果需要保证调用顺序可通过调用其 setOrder(int order) 方法进行设置.
方式四: 使用 DelegatingFilterProxyRegistrationBean 方式
- @Configuration
- public class WebConfig {
- @Bean("myFilter")
- // 配置了 DelegatingFilterProxyRegistrationBean 后, 这种方式配置的 Filter 不会生效了, 只会拦截 / foo/* 的请求
- public Filter myFilter(){
- return new TimeConsumingCalculationFilter();
- }
- @Bean
- public DelegatingFilterProxyRegistrationBean delegatingFilterProxyRegistrationBean(){
- DelegatingFilterProxyRegistrationBean filterProxy = new DelegatingFilterProxyRegistrationBean("myFilter");
- filterProxy.addUrlPatterns("/foo/*");
- filterProxy.addInitParameter("targetFilterLifecycle","true");
- filterProxy.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.CSS,*.ico");
- filterProxy.setDispatcherTypes(DispatcherType.REQUEST);
- return filterProxy;
- }
- }
FilterRegistrationBean 和 DelegatingFilterProxyRegistrationBean 区别:
FilterRegistrationBean 通过 onStartup 方法直接注册 filter.
DelegatingFilterProxyRegistrationBean 是将 DelegatingFilterProxy 注册到 Servlet3.0 + 的容器中, 同时实现了 ApplicationContextAware 接口, 实例 ApplicationContext 通过通过传入自定义 filter 的名称查找对应的 bean, 并生成相应 bean 的代理对象.
触类旁通
添加自定义 Servlet 也可采用方法一 @WebServlet 或者 ServletRegistrationBean
添加自定义 Listener 也可以采用方法一 @WebListener 或者 ServletListenerRegistrationBean , 注意监听事件是泛型
其他相关类
- ServletComponentRegisteringPostProcessor
- ServletComponentHandler
- WebListenerHandler
- WebFilterHandler
- WebServletHandler
参考
- https://blog.csdn.net/loveForever_xiang/article/details/101270633
- https://www.liangzl.com/get-article-detail-121998.html
- https://mp.weixin.qq.com/s/t8WdKEkYJuRApeEEOvXR_A
- https://blog.csdn.net/andy_zhang2007/article/details/90399870
来源: https://www.cnblogs.com/54chensongxia/p/12177784.html