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
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步