JavaAutowired注解深入分析

发布时间:2023-02-01

  今天是正月初八,先祝大家新年快乐。前几天遇见了一次Autowired注入失败的问题,所以找时间研究了一下相关的Spring源码,分享一下。如果哪位大佬发现问题,请帮忙反馈。
分享之前,先给一个小建议。Spring源码庞大,其中的扩展点众多,贸然全篇吸收,很容易劝退。我的思路是想看哪部分知识,就只看和这部分相关的,其他细节,先放放,只捋主线,这样做,效率比较高。
下面进入正文。
我写了一个非常简单的web工程,便于调试源码。测试代码如下:
Controller代码

  

  

?

  

1

  

2

  

3

  

4

  

5

  

6

  

7

  

8

  

9

  

@RestController

  

publicclassHomeController {

  

@Autowired

  

privateHomeService homeService;

  

@RequestMapping(/index)

  

publicString index() {

  

returnhomeService.testService();

  

}

  

}

  

  

  Service代码

  

  

?

  

1

  

2

  

3

  

4

  

5

  

6

  

7

  

8

  

9

  

10

  

11

  

12

  

//接口

  

publicinterfaceHomeService {

  

String testService();

  

}

  

//实现类

  

@Service

  

publicclassHomeServiceImpl implementsHomeService{

  

@Override

  

publicString testService(){

  

returnIm a test service;

  

}

  

}

  

  

  我们的目的是:把HomeService通过Autowired注解注入HomeController中,从而在index方法中实现对HomeService方法的内部调用。
我们想探究的是:
为什么我们给字段加一个Autowired注解,Spring就知道需要给bean注入这个字段对应的服务呢?
想知道为什么Spring知道给HomeController中注入另外一个bean,我们肯定就得看HomeController,这个bean是怎么创建出来的。
直接看AbstractAutowireCapableBeanFactory方法的doCreateBean方法,里面是Spring创建bean的逻辑。有2个地方和Autowired有关系。
我去掉了无关逻辑,只留下了和Autowired有关系的逻辑
1、applyMergedBeanDefinitionPostProcessors()
2、populateBean(beanName, mbd, instanceWrapper)

  

  

?

  

1

  

2

  

3

  

4

  

5

  

6

  

7

  

8

  

9

  

10

  

11

  

12

  

13

  

14

  

15

  

16

  

17

  

18

  

19

  

20

  

21

  

22

  

23

  

24

  

25

  

26

  

27

  

28

  

29

  

30

  

protectedObject doCreateBean(String beanName, RootBeanDefinition mbd, @NullableObject[] args)

  

throwsBeanCreationException {

  

//遍历所有的beanPostProcessor,寻找bean中被Autowired注解修饰的成员变量

  

synchronized(mbd.postProcessingLock) {

  

if(!mbd.postProcessed) {

  

try{

  

applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);

  

}

  

catch(Throwable ex) {

  

thrownewBeanCreationException(mbd.getResourceDescription(), beanName,

  

Post-processing of merged bean definition failed, ex);

  

}

  

mbd.postProcessed = true;

  

}

  

}

  

Object exposedObject = bean;

  

try{

  

//给bean填充其他bean。对应到我们的demo,就是给HomeController注入HomeService。

  

populateBean(beanName, mbd, instanceWrapper);

  

}

  

catch(Throwable ex) {

  

if(ex instanceofBeanCreationException beanName.equals(((BeanCreationException) ex).getBeanName())) {

  

throw(BeanCreationException) ex;

  

}

  

else{

  

thrownewBeanCreationException(

  

mbd.getResourceDescription(), beanName, Initialization of bean failed, ex);

  

}

  

}

  

}

  

  

  applyMergedBeanDefinitionPostProcessors方法,这个方法的作用是寻找HomeController的字段里,有没有哪个字段添加了Autowired注解。

  

  

?

  

1

  

2

  

3

  

4

  

5

  

protectedvoidapplyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {

  

for(MergedBeanDefinitionPostProcessor processor : getBeanPostProcessorCache().mergedDefinition) {

  

processor.postProcessMergedBeanDefinition(mbd, beanType, beanName);

  

}

  

}

  

  

  这段逻辑实际上是在遍历容器中的BeanPostProcessor,然后执行BeanPostProcessor中的逻辑,我们需要关注的是AutowiredAnnotationBeanPostProcessor,这个类是实现Autowired逻辑的核心类,大家重点关注。

  

  

?

  

1

  

2

  

3

  

4

  

5

  

@Override

  

publicvoidpostProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {

  

InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);

  

metadata.checkConfigMembers(beanDefinition);

  

}

  

  

  可以看到逻辑很少。第一行代码就是找HomeController中被Autowired注解修饰的字段。
我们进入findAutowiringMetadata中看一下。其中有一个内部方法调用是buildAutowiringMetadata(clazz),我们再进入这个方法

  

  

?

  

1

  

2

  

3

  

4

  

5

  

6

  

7

  

8

  

9

  

10

  

11

  

12

  

13

  

14

  

15

  

16

  

17

  

18

  

privateInjectionMetadata buildAutowiringMetadata(Class<?> clazz) {

  

ReflectionUtils.doWithLocalFields(targetClass, field -> {

  

//寻找字段上有被Autowired注解修饰的字段

  

MergedAnnotation<?> ann = findAutowiredAnnotation(field);

  

if(ann != null) {

  

//static修饰的静态字段不能进行注入

  

if(Modifier.isStatic(field.getModifiers())) {

  

if(logger.isInfoEnabled()) {

  

logger.info(Autowired annotation is not supported on static fields: + field);

  

}

  

return;

  

}

  

booleanrequired = determineRequiredStatus(ann);

  

currElements.add(newAutowiredFieldElement(field, required));

  

}

  

});

  

returnInjectionMetadata.forElements(elements, clazz);

  

}

  

  

  以上逻辑执行完,我们就拿到了待注入的homeService的元信息对象,即:InjectionMetadata。
上面逻辑中有一行代码,是判断字段是否被static修饰,如果是,不可以被注入。
小节一下。
绕了这么一圈,其实很简单。就是Spring在创建HomeController这个bean的时候,会检测其成员字段有没有被Autowired修饰。
对应到我们的demo代码,我们知道了HomeController中有一个被Autowired注解修饰的字段HomeService。
接下来,之后就是给HomeController注入这个HomeService。

  populateBean(beanName, mbd, instanceWrapper);我们进入这个方法看一下。

  重要逻辑是:pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
对应的实现类还是AutowiredAnnotationBeanPostProcessor。注入的核心逻辑是

  

  

?

  

1

  

2

  

3

  

4

  

5

  

6

  

7

  

8

  

9

  

10

  

11

  

12

  

13

  

14

  

@Override

  

publicPropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {

  

InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);

  

try{

  

metadata.inject(bean, beanName, pvs);

  

}

  

catch(BeanCreationException ex) {

  

throwex;

  

}

  

catch(Throwable ex) {

  

thrownewBeanCreationException(beanName, Injection of autowired dependencies failed, ex);

  

}

  

returnpvs;

  

}

  

  

  只有两行逻辑。
第一行:
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);这行代码,我们上面说到过,第一次执行时,是为了寻找被Autowired修饰字段的元信息。然后放入了缓存中。这一次再执行,我们就是要从缓存中拿到待注入的数据元信息InjectionMetadata。
第二行:
开始注入。
先看第一行代码的具体逻辑,获取待注入的数据元信息。

  

  

?

  

1

  

2

  

3

  

4

  

5

  

6

  

7

  

8

  

9

  

10

  

11

  

12

  

13

  

14

  

15

  

16

  

17

  

18

  

19

  

20

  

privateInjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @NullablePropertyValues pvs) {

  

//构建缓存key

  

String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());

  

//第一次执行,缓存中没有数据,先构建,然后放入缓存。第二次执行有数据,直接从缓存中获取

  

InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);

  

if(InjectionMetadata.needsRefresh(metadata, clazz)) {

  

synchronized(this.injectionMetadataCache) {

  

metadata = this.injectionMetadataCache.get(cacheKey);

  

if(InjectionMetadata.needsRefresh(metadata, clazz)) {

  

if(metadata != null) {

  

metadata.clear(pvs);

  

}

  

//第一次执行,缓存中不存在,先放入缓存。

  

metadata = buildAutowiringMetadata(clazz);

  

this.injectionMetadataCache.put(cacheKey, metadata);

  

}

  

}

  

}

  

returnmetadata;

  

}

  

  

  可以看到,上面有一个双检锁的设计,应该是防止并发情况下,多次创建InjectionMetadata对象。
再看第二行的具体逻辑,metadata.inject(bean, beanName, pvs);核心逻辑在AutowiredAnnotationBeanPostProcessor的626行,inject方法中。重点就是下面的代码。找到这个待注入的homeService对。

注册即送1000元现金券