spring(十二)2-----Bean的自动注入及循环依赖问题
创始人
2025-06-01 16:54:57
0

这里只分析自动注入模型为1、2情况

3情况:Spring源码学习(十)--推断构造方法_spring推断构造方法_从头再来_f的博客-CSDN博客

一、java的内省机制

浅谈Java内省机制_java_脚本之家

内省机制中是通过set、get方法来推断出类中包含哪些属性,如果你set、get方法不按第一个字母小写的规范写,此时内省机制就不会检测出你有这个属性。如果你根据内省的规则凭空编写符合规则的get、set(但实际上没有这个属性值),此时内省机制也会推断出有这个属性。

而spring的属性注入用的就是java的内省机制或者反射机制。

        只要类中有getXXX方法,或者setXXX方法,或者同时有getXXX及setXXX方法,其中getXXX方 法没有方法参数,有返回值; setXXX方法没有返回值,有一个方法参数;那么内省机制就认为 XXX为一个属性;

二、spring中的各种注入方式

对于是spring的注入前置知识、@Autowired、@Resource等的知识可以看其他文章,这里就不多说了

我们对Bean的注入,一般有下面几种方式:

1)、通过@Autowired、@Resource作用在属性上

2)、通过@Autowired、@Resource作用在方法上

3)、通过提供set方法+改变注入模型为自动注入

4)、通过BeanDefinition方式完成属性注入

我们先说前三种方式:

我们用下面的测试类来检验:

1、测试注解作用在属性上:

@Component
public class AnnotationAutowiredFiledBeanTest {
}

2、测试注解作用在方法上:

@Component
public class AnnotationAutowiredMethodBeanTest {
}

3、测试通过提供set方法+改变注入模型为自动注入

@Component
public class AutowiredInjectByTypeMethodBeanTest {
}

修改为注入模型类:

/*** 用来设置SpringBeanInfoTest类的属性注入为自动注入模式** */
@Component
public class UpdateBeanInfoBeanFactoryPostProcessor implements BeanFactoryPostProcessor{@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {AbstractBeanDefinition a = (AbstractBeanDefinition) beanFactory.getBeanDefinition("a");a.setAutowireMode(2);//a.getPropertyValues().add("beanDefinitionPropertyValuesBeanTest",new BeanDefinitionPropertyValuesBeanTest());}
}

4、各种方式的注入类:

@Component("a")
public class SpringBeanInfoTest {//@Autowired作用在属性上进行注入@AutowiredAnnotationAutowiredFiledBeanTest autowiredFiledBeanTest;//@Autowired作用在方法上进行注入@Autowiredpublic void setAnnotationAutowiredMethodBeanTest(AnnotationAutowiredMethodBeanTest annotationAutowiredMethodBeanTest){System.out.println("AnnotationAutowiredMethodBeanTest=[{}]"+annotationAutowiredMethodBeanTest);}//使用自动注入(使用byType的模式)public void setAutowiredInjectByTypeMethodBeanTest(AutowiredInjectByTypeMethodBeanTest autowiredInjectByTypeMethodBeanTest){System.out.println("AutowiredInjectByTypeMethodBeanTest=[{}]"+autowiredInjectByTypeMethodBeanTest);}//用来打印@Autowired作用在属性上进行注入是否成功public void printf(){System.out.println("autowiredFiledBeanTest=[{}]"+autowiredFiledBeanTest);}}

5、启动测试类:

配置类:

@Configuration
@ComponentScan("com.spring.demo.introspect")
public class App {}

启动类:

public class ApplicationTest {@Testpublic void testSpringInject() throws NoSuchFieldException, IntrospectionException {AnnotationConfigApplicationContext context= new AnnotationConfigApplicationContext();context.register(App.class);context.refresh();context.getBean(SpringBeanInfoTest.class).printf();}
}

此时我们在UpdateBeanInfoBeanFactoryPostProcessor类中设置了SpringBeanInfoTest类的属性注入为自动注入(2,默认为0)。我们运行下看看注入情况:

此时发现三种情况都能进行注入

此时我们修改注入模型为默认的手动注入。

/*** 用来设置SpringBeanInfoTest类的属性注入为自动注入模式** */
@Component
public class UpdateBeanInfoBeanFactoryPostProcessor implements BeanFactoryPostProcessor{@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {AbstractBeanDefinition a = (AbstractBeanDefinition) beanFactory.getBeanDefinition("a");//a.setAutowireMode(2);//a.getPropertyValues().add("beanDefinitionPropertyValuesBeanTest",new BeanDefinitionPropertyValuesBeanTest());}
}

 结果打印

 此时发现第三种方式无法注入了。

那么此时我们再来测试下第四种方式(通过BeanDefinition方式)

我们知道java内省机制最后都是讲解析出来的属性等转换为一个BeanInfo对象,然后所有属性等信息存在一个PropertyDescriptor数组中,而spring中在BeanDefinition中定义了一个MutablePropertyValues对象(propertyValues)中的一个List用来定义描述这个类的属性等,那么我们要往SpringBeanInfoTest中注入一个属性,此时就可以往这个List中存进我们要注入的对象即可。

(其实四种方式都是往propertyValues的List中添加属性内容,最后会对这个list进行统一的处理。只是前三种通过不同方式获取到属性内容,然后放进list,而第四种则是直接add进行)

Spring 之 MutablePropertyValues 和 ConstructorArgumentValues 的简单理解_魔道不误砍柴功的博客-CSDN博客

我们先编写一个测试类(这里不用@Component):

public class BeanDefinitionPropertyValuesBeanTest {
}

此时在UpdateBeanInfoBeanFactoryPostProcessor 类中进行BeanDefinition方式的注入:

/*** 用来设置SpringBeanInfoTest类的属性注入为自动注入模式** */
@Component
public class UpdateBeanInfoBeanFactoryPostProcessor implements BeanFactoryPostProcessor{@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {AbstractBeanDefinition a = (AbstractBeanDefinition) beanFactory.getBeanDefinition("a");a.getPropertyValues().add("beanDefinitionPropertyValuesBeanTest",new BeanDefinitionPropertyValuesBeanTest());}
}

这里由于是手动的修改BeanDefinition对象,所以其注入模型并不会影响到这个是否生效。

然后在SpringBeanInfoTest中添加注入方法:

@Component("a")
public class SpringBeanInfoTest {//@Autowired作用在属性上进行注入@AutowiredAnnotationAutowiredFiledBeanTest autowiredFiledBeanTest;//@Autowired作用在方法上进行注入@Autowiredpublic void setAnnotationAutowiredMethodBeanTest(AnnotationAutowiredMethodBeanTest annotationAutowiredMethodBeanTest){System.out.println("AnnotationAutowiredMethodBeanTest=[{}]"+annotationAutowiredMethodBeanTest);}//使用自动注入(使用byType的模式)public void setAutowiredInjectByTypeMethodBeanTest(AutowiredInjectByTypeMethodBeanTest autowiredInjectByTypeMethodBeanTest){System.out.println("AutowiredInjectByTypeMethodBeanTest=[{}]"+autowiredInjectByTypeMethodBeanTest);}//添加使用BeanDefinition方式进行属性注入public void setBeanDefinitionPropertyValuesBeanTest(BeanDefinitionPropertyValuesBeanTest beanDefinitionPropertyValuesBeanTest){System.out.println("BeanDefinitionPropertyValuesBeanTest=[{}]"+beanDefinitionPropertyValuesBeanTest);}//用来打印@Autowired作用在属性上进行注入是否成功public void printf(){System.out.println("autowiredFiledBeanTest=[{}]"+autowiredFiledBeanTest);}}

我们可以来看看是否生效:

可以看是可以进行注入的。

下面我们可以先简单的对这四种情况做个总结,后续再进行源码分析验证猜想:

1)、在一个属性上面加一个@Autowired注解

使用反射机制进行注入,可以看@Autowired源码,伪代码大概如下:

Class clazz = null;
Field autowiredFiledBeanTest = clazz.getDeclaredField("autowiredFiledBeanTest");
autowiredFiledBeanTest.setAccessible(true);
autowiredFiledBeanTest.set(this,getBean(AnnotationAutowiredFiledBeanTest.class));

2)、在一个方法上面加一个@Autowired注解

 2.1如果注入模型是1、2 (自动注入),那么spring底层采用的是java的自省机制发现setter方法然后调用执行
* 也就是说方法上面的@Autowierd注解无用,直接走内省机制进行注入而不是通过解析@Autowierd进行注入2.2如果注入模型为0(默认值,手动注入) 那么则是和在属性上面加注解差不多,底层查找所有加了@Autowired注解的方法,然后反射调用method.invoke()

3)、提供一个setter方法,继而把该bean的注入模型改成1、2 自动注入

* 3.1  注入模型是自动注入 则是java的内省机制
伪代码如下:
BeanInfo beanInfo = Introspector.getBeanInfo(SpringBeanInfoTest.class);
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {Method writeMethod = propertyDescriptor.getWriteMethod();writeMethod.invoke(this,getBean(parma))
}
* 3.2 注入模型为0(默认值)  则这个方法spring忽略

4)、使用BeanDefinition方式进行注入

不和注入模型有相关联,即所有情况都能生效

------------------------------------------源码验证

入口:refresh---》

finishBeanFactoryInitialization----》beanFactory.preInstantiateSingletons();---》
getBean---》doGetBean---》createBean-----》doCreateBean----》populateBean

我们进入populateBean方法看看:

//先从容器中获取bean,有则直接返回进行注入//无则调用createBean创建需要进行注入的bean,放进单例池,最后再进行注入protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {if (bw == null) {if (mbd.hasPropertyValues()) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");}else {// Skip property population phase for null instance.return;}}// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the// state of the bean before properties are set. This can be used, for example,// to support styles of field injection.if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof InstantiationAwareBeanPostProcessor) {InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {return;}}}}//1、获取到MutablePropertyValues对象,里面的List propertyValueList封装着一些属性的定义//这里现在只能获取到手动使用BeanDefinition动态添加的属性PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);//获取注入模型int resolvedAutowireMode = mbd.getResolvedAutowireMode();//2、注入模型为1或者2(自动注入),通过内省机制获取所有符合的属性(包括获取到使用了@Autowired注解的set),并getbean放进propertyValueList中if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {MutablePropertyValues newPvs = new MutablePropertyValues(pvs);// Add property values based on autowire by name if applicable.//获取到符合规则的属性(setter)//然后获取到该属性的bean,并加入到MutablePropertyValues中的List中if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {autowireByName(beanName, mbd, bw, newPvs);}// Add property values based on autowire by type if applicable.//获取到符合规则的属性(setter)// 然后获取到该属性的bean,并加入到MutablePropertyValues中的List中if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {autowireByType(beanName, mbd, bw, newPvs);}//所有符合上面的属性都会加到这里pvs = newPvs;}boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);//3、如果注入模型为0,手动注入,此时这里的propertyValueList只会存在我们手动使用BeanDefinition add进去的。//那么到这里为止所有set方法都没被识别到(既不会在applyPropertyValues中执行了)//下面的循环则是去解析@Autowired作用的属性、方法(反射机制)//注意:如果该属性已经存在propertyValueList,这里则不会对其进行解析(即自动注入模型下@Autowired作用在方法的被忽略执行)PropertyDescriptor[] filteredPds = null;if (hasInstAwareBpps) {if (pvs == null) {pvs = mbd.getPropertyValues();}//获取到所有BeanPostProcessor//如果是InstantiationAwareBeanPostProcessor,即处理@Autowired注解、@Resouce注解、@PostConstruct注解的BeanPostProcessor类型,则完成注入等操作for (BeanPostProcessor bp : getBeanPostProcessors()) {//完成@Autowired作用在属性、方法上面的处理(使用反射)if (bp instanceof InstantiationAwareBeanPostProcessor) {InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;//完成@Autowired作用在属性、方法上面的处理PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);if (pvsToUse == null) {if (filteredPds == null) {filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);}pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);if (pvsToUse == null) {return;}}pvs = pvsToUse;}}}if (needsDepCheck) {if (filteredPds == null) {filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);}checkDependencies(beanName, mbd, filteredPds, pvs);}//4、处理propertyValueList中的所有还未执行的属性//遍历属性名、对象,内省机制调用invoke方法执行set方法等if (pvs != null) {applyPropertyValues(beanName, mbd, bw, pvs);}}

我们总结下:

1、先获取手动添加的propertyValueList的(通过BeanDefinition进行add的)

2、如果注入模型为自动注入,则通过内省机制获取所有符合的规则的属性(包含使用注解、未使用注解的),getBean获取到bean对象,并加入到propertyValueList中

3、通过反射获取使用@Autowired注解的属性,并解析执行。这里有几点需要注意

1)、解析包含@Autowired作用的属性和方法两种

2)、如果该属性存在propertyValueList则不进行解析(即当注入模型为自动注入时,@Autowired作用的方法上的方式在步骤2中使用内省机制进行获取,此时跳过第三步)

4、对propertyValueList的所有属性调用invoke方法执行

不同方式在不同注入模型下获取属性的方式不同(内省机制、反射机制)、

============   注意:这里对@Autowired的情况同样适用于@Resource  ===============

===========================  spring的循环依赖问题  ==========================

https://www.cnblogs.com/codegitz/p/16353227.html

面试中如何回答循环依赖问题:

aaaa

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...