Spring源码阅读---Filter中使用@Autowired失效?

问题描述

先上代码

1.首先我在web.xml中配置了一个Filter名字叫做myFilter

2.我在这个类中使用@Autowired来试图注入一个Bean,觉果为null

抛出疑问

我明明已经用了@Component将MyFilter扔到IOC中了啊,为什么会注入失败?

问题解析

容器会加载用户自定义的Bean,通过注解、xml的方式,但并不是立刻初始化Bean对象,而是先获取它的BeanDefinition,把所有的BeanDefinition统一放到一个Map里面,这里面就包含名为myFilter的BeanDefinition

在BeanFactory初始化:ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory()之后,就可以调用getBean方法了,getBean不仅仅是获取,它其实应该叫做,在没有的情况下创建并获取,在有的情况下直接获取,它是BeanFactory接口中的核心方法,这其实是为了保证IOC中Bean的唯一性(单例的情况下),所以我现在通过getBean往IOC中注入myFilter了,

这个时候,我们发现虽然IOC中有了myFilter这个Bean,但是它的属性还是null,也就是说@Autowired并没有成功注入Bean,

这其实是因为getBean方法在创建Bean的时候会去找AutowiredAnnotationBeanPostProcessor,它会帮你完成@Autowired,但是因为我上面的Evaluate是在BeanFactory初始化之后立马执行的,这时BeanPostProcessor创建的代码还没执行,

容器中还没有BeanPostProcessor,自然没法依赖注入喽,

如果我们在invokeBeanFactoryPostProcessors(beanFactory)执行之后再执行getBean,就有了

当然,我手动调用getBean来创建myFilter只是为了测试。

好,接下来继续往下debug,哦,注意,如果你没有把MyFilter设置为Scope,那么请重新debug,原因不谈

Spring中是在接下来的finishBeanFactoryInitialization对我们开发人员的自定义Bean进行创建的

这一步将会调用getBean去创建Bean实例,把Bean中的属性取出来一个个取出来,反射创建之后再注入回原类,通过一个与@Autowired相对应的后置处理器来完成这个功能,

这一步完了之后,你就会发现Bean中属性已经被成功注入了,

所以这和标题有什么关系?

走到这里,我们已经发现,容器中会有一个myFilter,并且其中通过@Autowired注入属性也能成功,

但是,为什么我代码走到MyFilter的时候,还是属性皆为空?

难道?我用的根本就不是IOC中的那个myFilter?

没错,

MyFilter是被tomcat来调用的,这已经和Spring没啥关系了,也就是说,不管你Spring容器对我的MyFilter怎么折腾,我汤姆猫又不用你搞出来的那个Bean

问题解决

1、采用xml配置方式

  • Web.xml这样配置
<filter>
   <filter-name>MyFilterProxy</filter-name>
   <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
          <param-name>targetBeanName</param-name>
          <param-value>MyFilter</param-value>
 </init-param>
 <init-param>
          <param-name>targetFilterLifecycle</param-name>
          <param-value>true</param-value>
  </init-param>
</filter>
<filter-mapping>
   <filter-name>DelegatingFilterProxy</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>
  • 在spring配置文件中使用bean标签配置serviceImpl和MyFilter
<bean id="MyFilter" class="com.extension.filters.MyFilter">
   <property name="redisCache" ref="redisCache"></property>
</bean>

<!--要注入的bean-->
<bean id="redisCache" class="com.htjc.interceptor.redis.RedisCache">
</bean>

这种方式的重点是在web.xml中用到了DelegatingFilterProxy这个filter代理类。具体用法可以去看看DelegatingFilterProxy源码。

2、使用ApplicationContext对象获取,在filter的init方法中做这个事

 1 @Component
 2 @Slf4j
 3 public class MyFilter implements Filter {
 4 
 5     private XxxxSerice xxxxSerice;
 6 
 7     //使用filter中的init()方法来在filter的生命周期中我们手动注入需要使用的Service;
 8     @Override
 9     public void init(FilterConfig filterConfig) throws ServletException {
10         ServletContext context = filterConfig.getServletContext();
11         ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(context);
12         xxxxSerice= ctx.getBean(XxxxSericeImpl.class);//你要注入的服务
13     }
14 
15     @Override
16     public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
17         //xxxxx
18         filterChain.doFilter(servletRequest,servletResponse);
19     }
20 
21 }

 

参考:

https://zhuanlan.zhihu.com/p/331696398

https://blog.csdn.net/qq_43284469/article/details/119970391

http://t.zoukankan.com/panchanggui-p-14979829.html

 

posted @ 2022-07-06 11:37  Boblim  阅读(288)  评论(0编辑  收藏  举报