zl程序教程

您现在的位置是:首页 >  后端

当前栏目

Spring:代理Filter:DelegatingFilterProxy原理和作用

Spring代理原理 作用 filter
2023-09-11 14:19:34 时间

转自:https://blog.csdn.net/fly910905/article/details/95062258

 

DelegatingFilterProxy就是一个对于servlet filter的代理,用这个类的好处主要是通过Spring容器来管理servlet filter的生命周期,

  • 还有就是如果filter中需要一些Spring容器的实例,可以通过spring直接注入,
  • 另外读取一些配置文件这些便利的操作都可以通过Spring来配置实现。

 

Spring web在设计的时候考虑到某些功能的实现是

Spring web在设计的时候考虑到某些功能的实现是通过Filter来拦截进行实现的,如果直接的简单的实现几个Filter好像也不是不可以(平时我们就是这么用的),但是Spring框架最核心的是IOC容器,和Spring框架最好的实现就是将要实现的Filter功能注册到IOC容器的一个Bean,这样就可以和Spring IOC容器进行完美的融合,所以Spring Web设计了DelegatingFilterProxy。

本质上来说DelegatingFilterProxy就是一个Filter,其间接实现了Filter接口,但是在doFilter中其实调用的从Spring 容器中获取到的代理Filter的实现类delegate。

DelegatingFilterProxy原理:

1、DelegatingFilterProxy根据targetBeanName从Spring 容器中获取被注入到Spring 容器的Filter实现类,在DelegatingFilterProxy配置时一般需要配置属性targetBeanName

 1 @Override
 2 protected void initFilterBean() throws ServletException {
 3     synchronized (this.delegateMonitor) {
 4         if (this.delegate == null) {
 5             // If no target bean name specified, use filter name.
 6                         //当Filter配置时如果没有设置targentBeanName属性,则直接根据Filter名称来查找
 7             if (this.targetBeanName == null) {
 8                 this.targetBeanName = getFilterName();
 9             }
10             // Fetch Spring root application context and initialize the delegate early,
11             // if possible. If the root application context will be started after this
12             // filter proxy, we'll have to resort to lazy initialization.
13             WebApplicationContext wac = findWebApplicationContext();
14             if (wac != null) {
15                                 //从Spring容器中获取注入的Filter的实现类
16                 this.delegate = initDelegate(wac);
17             }
18         }
19     }
20 }
21  
22 protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
23         //从Spring 容器中获取注入的Filter的实现类
24         Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
25         if (isTargetFilterLifecycle()) {
26             delegate.init(getFilterConfig());
27         }
28         return delegate;
29     }

2、在DelegatingFilterProxy的实现方法doFilter中,其实最终调用的是委派的类delegate

 1 @Override
 2 protected void initFilterBean() throws ServletException {
 3     synchronized (this.delegateMonitor) {
 4         if (this.delegate == null) {
 5             // If no target bean name specified, use filter name.
 6                         //当Filter配置时如果没有设置targentBeanName属性,则直接根据Filter名称来查找
 7             if (this.targetBeanName == null) {
 8                 this.targetBeanName = getFilterName();
 9             }
10             // Fetch Spring root application context and initialize the delegate early,
11             // if possible. If the root application context will be started after this
12             // filter proxy, we'll have to resort to lazy initialization.
13             WebApplicationContext wac = findWebApplicationContext();
14             if (wac != null) {
15                                 //从Spring容器中获取注入的Filter的实现类
16                 this.delegate = initDelegate(wac);
17             }
18         }
19     }
20 }
21  
22 protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
23         //从Spring 容器中获取注入的Filter的实现类
24         Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
25         if (isTargetFilterLifecycle()) {
26             delegate.init(getFilterConfig());
27         }
28         return delegate;
29     }

总结:Spring web通过提高DelegatingProxyFilter类给开发者提供了便利

DelegatingFilterProxy运用

需求

在springboot中 使用了filter, 并且注入了业务工具类(APIUtil ),但注入是null

在springboot中 使用了filter, 并且注入了业务工具类(APIUtil ),但注入是null

其实Spring中,web应用启动的顺序是:listener->filter->servlet,先初始化listener,然后再来就filter的初始化,再接着才到我们的dispathServlet的初始化

解决办法1:

1 public class TokenAuthFilter implements Filter {
2  
3     private final static Logger log = LoggerFactory.getLogger(TokenAuthFilter.class);
4  
5     @Autowired
6     private APIUtil apiUtil;
7 }

新增一个config类,用来手工创建filter的bean, 例如:

 1 @Configuration
 2 public class WebConfig {
 3  
 4   @Bean
 5     public Filter tokenAuthFilter() {
 6  
 7         return new TokenAuthFilter();
 8     }
 9     /**
10      * 注册filter,统一处理api开头的请求
11      * @return FilterRegistrationBean
12      */
13     @Bean
14     public FilterRegistrationBean tokenAuthFilterRegistration() {
15         FilterRegistrationBean registration = new FilterRegistrationBean();
16         // DelegatingFilterProxy把servlet 容器中的filter同spring容器中的bean关联起来
17         registration.setFilter(new DelegatingFilterProxy("tokenAuthFilter"));
18         registration.addUrlPatterns("/api/*");
19         registration.setName("tokenAuthFilter");
20         registration.setOrder(1);
21         return registration;
22     }
23  
24 }

解决办法2:

过滤器是servlet规范中定义的,并不归spring容器管理,也无法直接注入spring中的bean(会报错)

初始化时通过spring上下文获取,进行bean的初始化:

1 @Override
2 public void init(FilterConfig filterConfig) throws ServletException {
3     ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(filterConfig.getServletContext());
4     RedisTemplate demoBean = (RedisTemplate)context.getBean("redisTemplate");
5     System.out.println(demoBean);
6  }