注解 java autowired_Spring注解之@Autowired

本文详细探讨了Spring中最常用的@Autowired注解,从源码角度解析其功能和原理。通过实例展示了@Autowired在成员变量、方法、构造器注入等场景的使用,并通过源码分析揭示了注解的工作机制,包括构造器参数、成员变量、集合注入的实现。文章最后提到了findAutowireCandidates方法在@Autowired注入过程中的关键作用。
摘要由CSDN通过智能技术生成

前言

说起Spring的@Autowired注解,想必大家已经熟悉的不能再熟悉了。本文就针对此最常用的注解,梳理一下它的功能和原理,争取从源码的角度将此注解讲通,如有写的不准确的地方,欢迎各位园友拍砖。

注:此篇博文基于Spring5.1.10.RELEASE,SpringBoot2.1.9.RELEASE

正文

首先看一下@Autowired注解的源码

1 packageorg.springframework.beans.factory.annotation;2

3 importjava.lang.annotation.Documented;4 importjava.lang.annotation.ElementType;5 importjava.lang.annotation.Retention;6 importjava.lang.annotation.RetentionPolicy;7 importjava.lang.annotation.Target;8

9 @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})10 @Retention(RetentionPolicy.RUNTIME)11 @Documented12 public @interfaceAutowired {13

14 /**

15 * Declares whether the annotated dependency is required.16 *

Defaults to {@codetrue}.17 */

18 boolean required() default true;19

20 }

可以看到,此注解运行时生效,且适用范围很广,从构造器,到方法,到参数、属性、注解,都可以加上@Autowired注解。它还有一个属性required,此属性用于控制如果找不到要依赖注入的对象时是否报错,默认true即默认找不到要注入的对象时会报错。下面我们慢慢梳理,将@Autowired的使用场景一一罗列出来。

一、普通用法,依赖注入成员变量

下面将要用的测试类贴出来,首先是接口

1 packagecom.mybokeyuan.springAutowiredDemo;2

3 public interfaceIndexInterface {4 }

再就是两个子类

1 packagecom.mybokeyuan.springAutowiredDemo;2

3 importorg.springframework.stereotype.Component;4

5 @Component6 public class IndexService implementsIndexInterface{7

8 }

1 packagecom.mybokeyuan.springAutowiredDemo;2

3 importorg.springframework.stereotype.Component;4

5 @Component6 public class OtherIndexService implementsIndexInterface{7

8 }

核心类(后续的各种场景演示均只基于此类进行改动)

1 packagecom.mybokeyuan.springAutowiredDemo;2

3 importorg.springframework.beans.factory.annotation.Autowired;4 importorg.springframework.stereotype.Component;5

6 @Component7 public classMyService {8

9 @Autowired10 privateIndexService indexService;11

12 }

扫描配置类

1 packagecom.mybokeyuan.springAutowiredDemo;2

3 importorg.springframework.context.annotation.ComponentScan;4 importorg.springframework.context.annotation.Configuration;5

6 @Configuration7 @ComponentScan("com.mybokeyuan.springAutowiredDemo")8 public classAutowiredConfig {9 }

main方法类

1 packagecom.mybokeyuan.springAutowiredDemo.client;2

3 importcom.mybokeyuan.springAutowiredDemo.AutowiredConfig;4 importcom.mybokeyuan.springAutowiredDemo.MyService;5 importorg.springframework.context.annotation.AnnotationConfigApplicationContext;6

7 public classDemoClientTest {8 public static voidmain(String[] args) {9 AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AutowiredConfig.class);10 System.out.println(ac.getBean(MyService.class));11

12 }13 }

运行main方法时,如果在第10行打了断点,会看到MyService中的indexService属性已经依赖注入了值,这是我们最常使用@Autowired注解的场景。

二、依赖注入方法

MyService类如下所示

1 packagecom.mybokeyuan.springAutowiredDemo;2

3 importorg.springframework.beans.factory.annotation.Autowired;4 importorg.springframework.context.ApplicationContext;5 importorg.springframework.stereotype.Component;6

7 importjava.util.List;8

9 @Component10 public classMyService {11

12 privateIndexService indexService;13

14 @Autowired15 public voidgetIndexInterf (IndexService index) {16 indexService =index;17 }18

19 @Autowired20 privateApplicationContext applicationContext;21

22 }

运行main方法后,会发现该类中的indexService完成了注入,这样的用法类似于set方法的功能。并且可以看到,applicationContext也注入了。所以往后就不要用ApplicationContextAware了,直接@Autowired轻快便捷的达到目的。

三、构造方法注入参数

MyService类如下所示:

1 packagecom.mybokeyuan.springAutowiredDemo;2

3 importorg.springframework.beans.factory.annotation.Autowired;4 importorg.springframework.stereotype.Component;5

6 importjava.util.List;7

8 @Component9 public classMyService {10

11

12 privateIndexService indexService;13

14 private ListindexList;15

16 @Autowired17 public MyService (ListindexList, IndexService indexService) {18 this.indexList =indexList;19 this.indexService =indexService;20 }21 }

运行后会发现成员变量indexList和indexService中已经注入了值。此处需要注意一点,如果有两个自定义构造方法,两个都没加@Autowired注解,则会报错,因为Spring不知道你要用哪个构造方法初始化;

如果只有一个构造方法加了@Autowired注解,那么就会用这个构造方法初始化;同理,如果有多个构造方法都加了@Autowired注解,那么还是会报错。

四、注入Map、List等集合

在【一、普通用法】中的MyService里添加几个成员变量,如下所示:

1 packagecom.mybokeyuan.springAutowiredDemo;2

3 importorg.springframework.beans.factory.annotation.Autowired;4 importorg.springframework.stereotype.Component;5

6 importjava.util.List;7 importjava.util.Map;8 importjava.util.Set;9

10 @Component11 public classMyService {12

13 @Autowired14 privateIndexService indexService;15

16 @Autowired17 private ListindexList;18

19 @Autowired20 private MapindexMap;21

22 @Autowired23 private SetindexSet;24 }

再次运行main方法,在断点中可以看到从Spring中获取到的MyService对象中,已经完成了对后面三个集合的依赖注入,如下图所示:

bc1c20aa4d6db22796810558082f21c5.png

可以看到,IndexInterface接口的两个实现类全部注入到了集合中,且Map里面的key默认就是两个实现类各自的beanName。

五、源码分析

看了上面的各种使用场景,想必大家会对这个最熟悉的注解有了一丝陌生的感觉,不过不要紧,下面咱们慢慢来脱下TA的神秘衣服,对ta重新熟悉起来。我这里是以下面的MyService类为例子进行的源码debug分析。

1 packagecom.mybokeyuan.springAutowiredDemo;2

3 importorg.springframework.beans.factory.annotation.Autowired;4 importorg.springframework.context.ApplicationContext;5 importorg.springframework.stereotype.Component;6

7 importjava.util.List;8

9 @Component10 public classMyService {11

12

13 privateIndexService indexService;14

15 private ListindexList;16

17 @Autowired18 public voidgetIndexInterf (IndexService index) {19 indexService =index;20 }21

22 @Autowired23 privateApplicationContext applicationContext;24

25 @Autowired26 public MyService (ListindexList) {27 this.indexList =indexList;28 }29 }

首先在AutowiredAnnotationBeanPostProcessor#postProcessProperties的方法中打上一个断点,然后右键断点加上条件过滤 beanName.equals("myService"),debug运行main方法,会在断点处停住,此时我们的调用栈是下图这样的:

12d96ba8b0f986253781ba52b4f84472.png

相信对Spring源码有过了解的园友对调用栈中的方法都不陌生,从下往上看,先经过了refresh方法,在finishBeanFactoryInitialization方法中getBean,然后走getObject的时候触发bean的初始化。

bean的初始化是一个很复杂地方,在AbstractAutowireCapableBeanFactory#doCreateBean方法中,先创建一个BeanWrapper,它的内部成员变量wrappedObject中存放的就是实例化的MyService对象,只是此时未完成初始化,所以属性都是null(如下图所示)。再往后进入populateBean方法进行属性注入,这才逐渐逼近我们的目的地。

46de309625f05e2ddb969a25ec917af4.png

不过在进入populateBean之前,需要先去看一下前置方法,即BeanWrapper创建的方法createBeanInstance。Spring在这个方法中先推断出合适的构造方法,然后实例化bean。在推断构造方法的时候(AbstractAutowireCapableBeanFactory#determineConstructorsFromBeanPostProcessors方法中调用了AutowiredAnnotationBeanPostProcessor#determineCandidateConstructors方法进行的推断),有一个筛选标准就是会根据是否有@Autowired注解来选择用哪个构造器,具体详见AutowiredAnnotationBeanPostProcessor#findAutowiredAnnotation方法,此方法如下所示,功能就是看看当前构造器上加没加注解。

1 @Nullable2 privateAnnotationAttributes findAutowiredAnnotation(AccessibleObject ao) {3 if (ao.getAnnotations().length > 0) { //autowiring annotations have to be local

4 for (Class extends Annotation> type : this.autowiredAnnotationTypes) {// 此集合中只有两个值,一个是Autowired注解,一个是Value注解5 AnnotationAttributes attributes =AnnotatedElementUtils.getMergedAnnotationAttributes(ao, type);6 if (attributes != null) {7 returnattributes;8 }9 }10 }11 return null;12 }

此处构造器的推断和属性注入,就是使用场景三【三、构造方法注入参数】中,@Autowired注解加在构造器上发挥的作用。

走完createBeanInstance方法得到的BeanWrapper中,可以看到属性wrappedObject中已经对indexList进行了依赖注入

38e571774b8114124453ad07dfd788cc.png

进行到这里,有必要重新梳理清楚类构造器注入时的调用关系,示例的调用栈图如下所示:

1a81a35f82948faaa8545f1ed6f38801.png

1、在myService对象进行实例化调用构造器时,执行AbstractAutowireCapableBeanFactory#autowireConstructor方法;

2、进入到另一个类ConstructorResolver的autowireConstructor方法中;

2.1、调用同类下的resolveAutowiredArgument方法,该方法会调用DefaultListableBeanFactory下的resolveDependency方法,此方法会调用到findAutowireCandidates方法,在该方法中有如下代码

1 for(String candidate : candidateNames) {

2 if (!isSelfReference(beanName, candidate) &&isAutowireCandidate(candidate, descriptor)) {

3 addCandidateEntry(result, candidate, descriptor, requiredType);

4 }

5 }

其中的candidateNames就是构造器中要注入的对象名字indexService和otherIndexService,此处通过循环调用addCandidateEntry来获取这两个name对应的bean;

2.2、在DefaultListableBeanFactory的addCandidateEntry方法中,调到了DependencyDescriptor#resolveCandidate方法,在此方法中调用的getBean方法获取它的成员变量bean。

DependencyDescriptor#resolveCandidate方法如下所示:

1 public Object resolveCandidate(String beanName, Class>requiredType, BeanFactory beanFactory)

2 throwsBeansException {

3

4 returnbeanFactory.getBean(beanName);

5 }

然后又进入了下一次的getBean逻辑。此处就是这样完成的构造器方法参数注入。

下面进入populateBean中。看源码经过一个for循环和一个NAME或者TYPE模式的判断之后,进入了BeanPostProcessor的循环调用中,通过遍历后置处理器,调用了AutowiredAnnotationBeanPostProcessor#postProcessProperties,也就是一开始我们打断点的地方,该方法源码如下:

1 @Override

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

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

4 try{

5 metadata.inject(bean, beanName, pvs);

6 }

7 catch(BeanCreationException ex) {

8 throwex;

9 }

10 catch(Throwable ex) {

11 throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);

12 }

13 returnpvs;

14 }

在第13行打断点,发现到13行的时候,bean中的属性已经被注入了,所以可以知道,是在第5行进行的属性注入。

继续跟踪第5行,进入InjectionMetadata#inject方法:

1 public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throwsThrowable {

2 Collection checkedElements = this.checkedElements;

3 Collection elementsToIterate =

4 (checkedElements != null ? checkedElements : this.injectedElements);

5 if (!elementsToIterate.isEmpty()) {

6 for(InjectedElement element : elementsToIterate) {

7 if(logger.isTraceEnabled()) {

8 logger.trace("Processing injected element of bean '" + beanName + "': " +element);

9 }

10 element.inject(target, beanName, pvs);

11 }

12 }

13 }

发现是从当前metadata对象中取的element对象,而通过断点可以看到,checkedElements中的Member对象就是当前bean中加了@Autowired注解的成员变量或者普通方法在反射包中对应的对象,随后通过element的inject方法进行的注入。那么问题来了,element对象是如何创建来的?inject方法又是如何将bean注入到成员变量中的?

对于element对象的创建,还要返回postProcessProperties方法中查看上面的findAutowiringMetadata方法:

1 private InjectionMetadata findAutowiringMetadata(String beanName, Class>clazz, @Nullable PropertyValues pvs) {

2 // Fall back to class name as cache key, for backwards compatibility with custom callers.

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

4 // Quick check on the concurrent map first, with minimal locking.

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

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

7 synchronized (this.injectionMetadataCache) {

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

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

10 if (metadata != null) {

11 metadata.clear(pvs);

12 }

13 metadata =buildAutowiringMetadata(clazz);

14 this.injectionMetadataCache.put(cacheKey, metadata);

15 }

16 }

17 }

18 returnmetadata;

19 }

通过断点到这个方法中,发现此时injectionMetadataCache中已经有了myService对应的InjectionMetadata,此方法只是从map中取出返回。没办法了,还要继续追寻下去,AutowiredAnnotationBeanPostProcessor中的injectionMetadataCache属性是什么时候触发的初始化?

在当前类中查询injectionMetadataCache的put方法,发现只有这一个地方,所以继续在这里断点一下,看看什么时候触发的injectionMetadataCache.put。

912f7a45af81178593b4d889073a1279.png

发现原来就是在doCreateBean的applyMergedBeanDefinitionPostProcessors方法中触发的put操作。

下面继续追寻inject方法中Spring如何完成的属性注入。

属性注入进入内部类 AutowiredFieldElement的方法inject中,方法注入进入的是AutowiredMethodElement中。在inject方法中可以看到我们的老朋友DefaultListableBeanFactory#resolveDependency方法,即通过这个方法获取到对应的bean对象,最后通过反射完成的赋值或方法调用。

这里我们第二次遇到DefaultListableBeanFactory#resolveDependency,发现它是@Autowired注解注入构造器参数、注入成员变量、注入方法参数必经的方法,所以必须继续搞明白它。

追踪resolveDependency方法,进入DefaultListableBeanFactory#resolveMultipleBeans方法,该方法属于@Autowired注解的核心方法,虽然很长,但需要贴出源代码:

1 @Nullable2 privateObject resolveMultipleBeans(DependencyDescriptor descriptor, @Nullable String beanName,3 @Nullable SetautowiredBeanNames, @Nullable TypeConverter typeConverter) {4

5 final Class> type =descriptor.getDependencyType();6 //0、暂时不清楚是做什么用的...

7 if (descriptor instanceofStreamDependencyDescriptor) {8 Map matchingBeans =findAutowireCandidates(beanName, type, descriptor);9 if (autowiredBeanNames != null) {10 autowiredBeanNames.addAll(matchingBeans.keySet());11 }12 Stream stream =matchingBeans.keySet().stream()13 .map(name -> descriptor.resolveCandidate(name, type, this))14 .filter(bean -> !(bean instanceofNullBean));15 if(((StreamDependencyDescriptor) descriptor).isOrdered()) {16 stream =stream.sorted(adaptOrderComparator(matchingBeans));17 }18 returnstream;19 }20 else if (type.isArray()) { //1、判断要注入的是不是一个数组,可见除了集合注入外,也可以以数组的形式注入bean

21 Class> componentType =type.getComponentType();22 ResolvableType resolvableType =descriptor.getResolvableType();23 Class> resolvedArrayType =resolvableType.resolve(type);24 if (resolvedArrayType !=type) {25 componentType =resolvableType.getComponentType().resolve();26 }27 if (componentType == null) {28 return null;29 }30 Map matchingBeans =findAutowireCandidates(beanName, componentType,31 newMultiElementDescriptor(descriptor));32 if(matchingBeans.isEmpty()) {33 return null;34 }35 if (autowiredBeanNames != null) {36 autowiredBeanNames.addAll(matchingBeans.keySet());37 }38 TypeConverter converter = (typeConverter != null ?typeConverter : getTypeConverter());39 Object result =converter.convertIfNecessary(matchingBeans.values(), resolvedArrayType);40 if (result instanceofObject[]) {41 Comparator comparator =adaptDependencyComparator(matchingBeans);42 if (comparator != null) {43 Arrays.sort((Object[]) result, comparator);44 }45 }46 returnresult;47 }48 else if (Collection.class.isAssignableFrom(type) && type.isInterface()) { //2、判断注入的是不是集合的接口类(List、Set等)

49 Class> elementType =descriptor.getResolvableType().asCollection().resolveGeneric();50 if (elementType == null) {51 return null;52 }53 Map matchingBeans =findAutowireCandidates(beanName, elementType,54 newMultiElementDescriptor(descriptor));55 if(matchingBeans.isEmpty()) {56 return null;57 }58 if (autowiredBeanNames != null) {59 autowiredBeanNames.addAll(matchingBeans.keySet());60 }61 TypeConverter converter = (typeConverter != null ?typeConverter : getTypeConverter());62 Object result =converter.convertIfNecessary(matchingBeans.values(), type);63 if (result instanceofList) {64 Comparator comparator =adaptDependencyComparator(matchingBeans);65 if (comparator != null) {66 ((List>) result).sort(comparator);67 }68 }69 returnresult;70 }71 else if (Map.class == type) { ///3、判断要注入的是不是map类

72 ResolvableType mapType =descriptor.getResolvableType().asMap();73 Class> keyType = mapType.resolveGeneric(0);74 if (String.class !=keyType) {75 return null;76 }77 Class> valueType = mapType.resolveGeneric(1);78 if (valueType == null) {79 return null;80 }81 Map matchingBeans =findAutowireCandidates(beanName, valueType,82 newMultiElementDescriptor(descriptor));83 if(matchingBeans.isEmpty()) {84 return null;85 }86 if (autowiredBeanNames != null) {87 autowiredBeanNames.addAll(matchingBeans.keySet());88 }89 returnmatchingBeans;90 }91 else { //4、其他情况返回空,进入doResolveDependency方法做默认处理

92 return null;93 }94 }

主要看1/2/3的注释,可以知道Spring支持多种注入方式就是在这里实现的。我们能发现,在获取bean的时候都是调用了findAntowireCandidates方法,而且第4中默认的情况,在外部的doResolveDenpency方法中也调用了findAutowireCandidates方法。

至于findAutowireCandidates方法是如何完成的属性获取?是通过type还是name获取到的bean?我们下期继续!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值