BeanFactoryPostProcessor分析概述 BeanFactoryPostProcessor 他有什么用?
概述
- BeanFactoryPostProcessor 他有什么用?程序员主要对他做什么扩展。
- 他的执行时机?bean工厂的处理器,主要是提供给程序员扩展的;在spring容器运行期间可以让程序员对BeanFactory组件进行设置;
主要的api分析:
ignoreDependencyType
在bean的注入过程当中忽略某个类型的的依赖beanFactory.ignoreDependencyType(B.class);不管那个哪个bean当中依赖了B这类,那么spring容器在启动的过程当中都不会自动装配B,但是这仅仅局限与这个依赖项为自动装配;什么意思呢?举个栗子:
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Slf4j(topic = "e")
@Component
public class A {
B b;
public void setB(B b) {
this.b = b;
}
public void printInfo(){
log.debug("bean-b:{}",b);
}
}
import org.springframework.stereotype.Component;
@Component
public class B {
}
类A当中有一个依赖项B,正常情况下如果A的注入模式是自动注入那么B是能够被依赖上的,但是A的正常情况下的注入模式是AbstractBeanDefinition.AUTOWIRE_NO也就是不自动注入,所以正常情况下b应该为null。
@Slf4j(topic = "e")
public class TestBeanFactoryPostProcessor {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class);
applicationContext.getBean(A.class).printInfo();
}
}
运行这测试类,调用A的printInfo方法后台打印出来的b=null。
再次强调这里之所以为空,是因为A类的自动注入模式为默认的(不自动注入),所以b为null。接下来给b加上@Autowired 那么再次运行b肯定不等于null。需要说明的是即使你给A上面的b属性加上@Autowired 此时A的注入模型依旧是AUTOWIRE_NO不是自动注入,之所以能够注入上,是因为你都在b上面指定了@Autowired这相当于是手动注入了。
@Slf4j(topic = "e")
@Component
public class A {
//加上这注解之后 b 是能够被spring给扫描到并且给注入上
@Autowired
B b;
// 通过set()方法将b注入到A中
public void setB(B b) {
this.b = b;
}
public void printInfo(){
log.debug("bean-b:{}", b);
}
}
这两种情况都比较简单,现在我们来测试IgnoreDependencyType这个方法,先看看spring源码当中的javadoc:
/**
* 忽略自动装配的依赖类型
* Ignore the given dependency type for autowiring:
* for example, String. Default is none.
* @param type the dependency type to ignore
*/
void ignoreDependencyType(Class<?> type);
新建一个类TestIgnoreDependencyType实现BeanFactoryPostProcessor:
@Component
@Slf4j(topic = "e")
public class TestIgnoreDependencyType implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
ScannedGenericBeanDefinition a = (ScannedGenericBeanDefinition) beanFactory.getBeanDefinition("a");
//设置所有自动注入的属性如果类型为B则忽略
beanFactory.ignoreDependencyType(B.class);
}
}
然后运行测试类,讲道理按照spring提供的javadoc这时候A中的b应该为null
但是结果还是不为null,说明b被注入进来了,表面上beanFactory.ignoreDependencyType(B.class);没有生效。但是实际是我们的代码有问题,因为A当中的b依然是手动注入(加了@Autowired属于手动注入);关于@Autowired和自动注入的问题可以参考一篇博客:blog.csdn.net/java_lyvee/…
而ignoreDependencyType这个方法只针对自动注入,故而这里会失效我们需要把A的注入模式改为自动注入,继而把A类中的b属性上面的@Autowired注解去掉即可因为即使你把A的注入模型改为了自动注入模型,但是加了@Autowired注解的属性依旧是手动注入,其他的没有加这个注解的是自动注入。
@Slf4j(topic = "e")
@Component
public class A {
//没有加注解了 这时候b是自动注入的
B b;
//如果是自动注入必须加上这setter
public void setB(B b) {
this.b = b;
}
public void printInfo(){
log.debug("bean-b:{}",b);
}
}
------------------------------------------------------------------------------------------
@Component
@Slf4j(topic = "e")
public class TestIgnoreDependencyType implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
//获取A的BeanDefinition
ScannedGenericBeanDefinition a =
(ScannedGenericBeanDefinition) beanFactory.getBeanDefinition("a");
//设置A的注入模型为AUTOWIRE_BY_TYPE
a.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
//设置所有自动注入的属性如果类型为B则忽略
beanFactory.ignoreDependencyType(B.class);
}
}
ignoreDependencyInterface
/**
* 忽略自动装配的依赖接口
* given Ignore the dependency interface for autowiring.
* <p>This will typically be used by application contexts to register
* dependencies that are resolved in other ways, like BeanFactory through
* BeanFactoryAware or ApplicationContext through ApplicationContextAware.
* <p>By default, only the BeanFactoryAware interface is ignored.
* For further types to ignore, invoke this method for each type.
* @param ifc the dependency interface to ignore
* @see org.springframework.beans.factory.BeanFactoryAware
* @see org.springframework.context.ApplicationContextAware
*/
void ignoreDependencyInterface(Class<?> ifc);
这个方法其实很晦涩难懂,很多人会以为这个方法和ignoreDependencyType一样——在自动注入的时候忽略给定的接口;但是你可以测试一下他的效果并不是如你想的那样。
public interface C {
}
@Component
public class D implements C{
}
@Component
public class E implements C{
}
@Slf4j(topic = "e")
@Component
public class A {
// C是一个接口 有两个实现 D E 这里通过byname注入的应该是D
C d;
public void setD(C d) {
this.d = d;
}
public void printInfo(){
log.debug("bean-b:{}",d);
}
}
如果直接这样子肯定会报错,因为前面我们已经通过TestIgnoreDependencyType这类把A设置为了根据类型自动注入,而这里会找到两个C类型的 D 和 E 的bean故而会出错;
为了不影响ignoreDependencyInterface这方法的测试我们需要把TestIgnoreDependencyType上面的@Component去掉,这样spring就不会发现TestIgnoreDependencyType这bean;然后再新建一个类TestIgnoreDependencyInterface代码如下:
package com.spring.extension.beanFactoryPostProcessor;
import com.spring.extension.beanFactoryPostProcessor.bean.C;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.context.annotation.ScannedGenericBeanDefinition;
import org.springframework.stereotype.Component;
@Component
public class TestIgnoreDependencyInterface implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
//获取A的BeanDefinition
ScannedGenericBeanDefinition a =
(ScannedGenericBeanDefinition) beanFactory.getBeanDefinition("a");
//设置A的注入模型为AUTOWIRE_BY_NAME byname就能正常注入d
a.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME);
beanFactory.ignoreDependencyInterface(C.class);
}
}
按照我们之前的理解这里我忽略了所有C类型的依赖注入,那么在A中注入的d肯定为null,但是事实不如你所想,这里能够正确注入D:
这莫非是spring的一个bug?当然不会,其实spring的javadoc里面有解释,只是解释的比较模糊,要结合spring的源码去看才能理解这个方法的意义。
首先你有一个Y的bean:这个y能够正常被spring扫描到;
@Component
public class Y {
}
假设你的X接口当中有一个setY方法:
public interface X {
public void setY(Y y);
}
然后再有一个类W实现了X接口,那么肯定要重写里面的setY的方法,正好在这个时候W里面需要依赖Y(有一个Y的属性),也就是需要注入Y:
@Slf4j(topic = "e")
@Component
public class W implements X{
Y y;
@Override
public void setY(Y y) {
this.y = y;
}
public void printInfo(){
log.debug("bean y:{}", y);
}
}
如果你的W类是自动注入的,比如根据类型自动注入,那么肯定没有问题,可以注入成功;我们可以在TestIgnoreDependencyInterface当中把代码修改为获取W的beanDefintion然后修改W的注入模型为自动注入byType;
@Component
public class TestIgnoreDependencyInterface implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
//获取W的BeanDefinition
ScannedGenericBeanDefinition w =
(ScannedGenericBeanDefinition) beanFactory.getBeanDefinition("w");
//设置A的注入模型为AUTOWIRE_BY_TYPE
a.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
}
修改测试类,主要是获取w然后调用w的printInfo方法,这样肯定没有任何问题Y能够被正常注入;
@Slf4j(topic = "e")
public class TestBeanFactoryPostProcessor {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class);
applicationContext.getBean(W.class).printInfo();
}
}
运行结果能正常打印Y,说明Y能够被注入进来:
@Component
public class TestIgnoreDependencyInterface implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
//获取A的BeanDefinition
ScannedGenericBeanDefinition a =
(ScannedGenericBeanDefinition) beanFactory.getBeanDefinition("w");
//设置A的注入模型为AUTOWIRE_BY_NAME
a.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
//设置忽略X接口--什么意思呢?
beanFactory.ignoreDependencyInterface(X.class);
}
}
这时候发觉y等于null了也就是加上beanFactory.ignoreDependencyInterface(X.class);忽略接口为X类型的注入y就等于null这个是什么意思呢?
说起来挺绕,spring的意思是如果你的类A实现了接口B,那么在类A完成自动自动注入的时候会去调用setter方法,但是如果你的setter方法在接口B中定义了则忽略;比如上文中的W实现了X的setY,而X刚好自己也要setY,和X接口的当中的setY重复了或者说相同了,那么这个setY就失效了,不会对Y进行自动注入了;当然不是所有这种情况都会失效你必须指定beanFactory.ignoreDependencyInterface(X.class);那么这样做有什么意义呢?spring为什么要这么设计呢?其实你去观察spring的源码,再容器刚刚启动的时候spring多处调用了beanFactory.ignoreDependencyInterface(XXXXXX.class);
你会发觉这些类都是XXXAware的类,如果你听过子路讲的spring循环依赖或者说你熟悉spring的应用你就应该知道这些aware的调用时机以及他们主要提供了一个set方法,我们举个栗子ApplicationContextAware这个类;首先看这个接口的定义:
非常简单就是一个set方法,那么这类有什么作用呢?从他的set方法可以看到他主要就是给一个bean中设置进一个容器对象(ApplicationContext),也就是说我们有一个类Z,如果Z需要获取容器对象只需要实现这接口,继而重写这接口的setApplicationContexxt方法就能得到容器对象(至于为什么需要得到容器对象,我在博客里面有提到过这里不讨论了);我们可以来试一下:
@Slf4j(topic = "e")
@Component
public class Z implements ApplicationContextAware {
ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext=applicationContext;
log.debug("applicationContext-name:{}",applicationContext.getClass().getSimpleName());
log.debug("现在容器对象已经有了,你可以在你的Z当中放肆使用了");
}
}
接下来我们只需要初始化spring容器就能看到信息了,因为setApplicationContext方法是由容器自己调用的,所以你只需要去初始化spring容器就能看到打印的信息了;
好了,现在我们来思考一个问题,假设你的Z是自动注入的,那么注入的applicationContext究竟是谁呢?不用我说大家都知道ApplicationContext是个接口,他的实现类可太多了,这接口差不多可以是一个顶级类了;再退一万步讲就算spring能给定一个特定的实现给你,但是也应该是spring自己在特定的情况下给的;什么意思呢?如果按照自动注入的逻辑这applicationContext是因为我们在实例化Z的时候,由于Z依赖了applicationContext故而spring会去产生一个applicationContext(如果已经产生则直接从容器获取给)对象然后注入给Z,但是我们前面说过了,这applicationContext不能按照这么来给;
原因:
- 是因为spring需要给一个他想要的给你(特定的applicationContext)不能和自动注入的逻辑写在一起;而是因为你实现了ApplicationContextAware这接口,如果你听过子路讲的循环依赖就知道这些aware接口是在spring的特定生命周期被调用的(好吧就算你没有听过,反正你记住这些xxxxAware接口的setxxx方法都是在实例化一个bean的时候由容器调用的,用意就是set一个特定的bean给你);所以这个set方法就不能再自动注入的时候调用,就是因为spring觉得有些特定的类需要在特定的时机,以特定的方式给你,而不是由用户直接自动注入得到的(因为有可能那个时候这个bean还没有实例化好,或者还不能够给你) ;故而spring在最开始就大量调用了类似beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);这样的代码;这样所有实现了 ApplicationContextAware接口的bean其中的setApplicationContext方法都不能算作自动注入的方法,而是由容器在特定时机调用。不知道你现在懂了ignoreDependencyInterface方法没有;
以前在讲公开课的时候,比如讲循环依赖的时候说到bean的生命周期,其中重要的一步就是调用各种aware方法,那个时候由于各种原因一直借口关于aware的意义放到以后在讲,可是一直没有机会,这里补上了算是补上了;这些aware接口有两重意义:
- 获取spring容器提供的的一些特定对象,或者让你的bean具备一些特殊的功能。
- 保证你获取的这些特定对象是正确的,而不是通过依赖注入得到的(因为有些bean,直接通过依赖注入会有问题)。
registerResolvableDependency
/**
* 给指定类型的依赖注入项一个特定的值
* Register a special dependency type with corresponding autowired value.
* <p>This is intended for factory/context references that are supposed
* to be autowirable but are not defined as beans in the factory:
* e.g. a dependency of type ApplicationContext resolved to the
* ApplicationContext instance that the bean is living in.
* <p>Note: There are no such default types registered in a plain BeanFactory,
* not even for the BeanFactory interface itself.
* @param dependencyType the dependency type to register. This will typically
* be a base interface such as BeanFactory, with extensions of it resolved
* as well if declared as an autowiring dependency (e.g. ListableBeanFactory),
* as long as the given value actually implements the extended interface.
* @param autowiredValue the corresponding autowired value. This may also be an
* implementation of the {@link org.springframework.beans.factory.ObjectFactory}
* interface, which allows for lazy resolution of the actual target value.
*/
void registerResolvableDependency(Class<?> dependencyType, @Nullable Object autowiredValue);
这方法比较简单,给指定类型的依赖注入项一个特定的值,什么意思呢?
比如:A里面注入了C,而C有两个实现D和E,那么你可以通过这方法给依赖类型为C的注入项指定一个固定的对象,D或者E,这样就能避免spring因为多个实例而出现的错误(这里并没有说自动注入,也就是@Autowired也可以);
照样举个栗子吧:
@Slf4j(topic = "e")
@Component
public class A {
// C是一个接口 有两个实现 D E 这里通过byname注入的应该是D
@Autowired
C c;
public void setC(C c) {
this.c = c;
}
public void printInfo(){
log.debug("bean-b:{}",c);
}
}
接下来我们新建一个TestRegisterResolvableDependency的类在当中调用beanFactory.registerResolvableDependency(C.class,new E()); 表示只要遇到注入C,就用new E();这个对象去注入。
同样你可以改成D依然没有问题:
那么关于这个api在spring源码哪里用到了,如果你熟悉spring源码就会知道在spring容器最开始的地方用到了:
转载自:https://juejin.cn/post/7383029376383057957