前言

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

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

正文

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

 package org.springframework.beans.factory.annotation;

 import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired { /**
* Declares whether the annotated dependency is required.
* <p>Defaults to {@code true}.
*/
boolean required() default true; }

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

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

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

 package com.mybokeyuan.springAutowiredDemo;

 public interface IndexInterface {
}

再就是两个子类

 package com.mybokeyuan.springAutowiredDemo;

 import org.springframework.stereotype.Component;

 @Component
public class IndexService implements IndexInterface{ }
 package com.mybokeyuan.springAutowiredDemo;

 import org.springframework.stereotype.Component;

 @Component
public class OtherIndexService implements IndexInterface{ }

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

 package com.mybokeyuan.springAutowiredDemo;

 import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; @Component
public class MyService { @Autowired
private IndexService indexService; }

扫描配置类

 package com.mybokeyuan.springAutowiredDemo;

 import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration; @Configuration
@ComponentScan("com.mybokeyuan.springAutowiredDemo")
public class AutowiredConfig {
}

main方法类

 package com.mybokeyuan.springAutowiredDemo.client;

 import com.mybokeyuan.springAutowiredDemo.AutowiredConfig;
import com.mybokeyuan.springAutowiredDemo.MyService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class DemoClientTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AutowiredConfig.class);
System.out.println(ac.getBean(MyService.class)); }
}

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

二、依赖注入方法

MyService类如下所示

 package com.mybokeyuan.springAutowiredDemo;

 import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component; import java.util.List; @Component
public class MyService { private IndexService indexService; @Autowired
public void getIndexInterf (IndexService index) {
indexService = index;
} @Autowired
private ApplicationContext applicationContext; }

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

三、构造方法注入参数

MyService类如下所示:

 package com.mybokeyuan.springAutowiredDemo;

 import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import java.util.List; @Component
public class MyService { private IndexService indexService; private List<IndexInterface> indexList; @Autowired
public MyService (List<IndexInterface> indexList, IndexService indexService) {
this.indexList = indexList;
this.indexService = indexService;
}
}

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

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

四、注入Map、List等集合

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

 package com.mybokeyuan.springAutowiredDemo;

 import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import java.util.List;
import java.util.Map;
import java.util.Set; @Component
public class MyService { @Autowired
private IndexService indexService; @Autowired
private List<IndexInterface> indexList; @Autowired
private Map<String, IndexInterface> indexMap; @Autowired
private Set<IndexInterface> indexSet;
}

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

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

五、源码分析

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

 package com.mybokeyuan.springAutowiredDemo;

 import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component; import java.util.List; @Component
public class MyService { private IndexService indexService; private List<IndexInterface> indexList; @Autowired
public void getIndexInterf (IndexService index) {
indexService = index;
} @Autowired
private ApplicationContext applicationContext; @Autowired
public MyService (List<IndexInterface> indexList) {
this.indexList = indexList;
}
}

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

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

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

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

     @Nullable
private AnnotationAttributes findAutowiredAnnotation(AccessibleObject ao) {
if (ao.getAnnotations().length > 0) { // autowiring annotations have to be local
for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {// 此集合中只有两个值,一个是Autowired注解,一个是Value注解
AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ao, type);
if (attributes != null) {
return attributes;
}
}
}
return null;
}

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

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

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

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 throws BeansException {
3
4 return beanFactory.getBean(beanName);
5 }

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

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

 1 @Override
2 public PropertyValues 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 throw ex;
9 }
10 catch (Throwable ex) {
11 throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
12 }
13 return pvs;
14 }

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

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

 1 public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
2 Collection<InjectedElement> checkedElements = this.checkedElements;
3 Collection<InjectedElement> 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 return metadata;
19 }

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

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

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

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

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

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

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

 @Nullable
private Object resolveMultipleBeans(DependencyDescriptor descriptor, @Nullable String beanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) { final Class<?> type = descriptor.getDependencyType();
// 0、暂时不清楚是做什么用的...
if (descriptor instanceof StreamDependencyDescriptor) {
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
if (autowiredBeanNames != null) {
autowiredBeanNames.addAll(matchingBeans.keySet());
}
Stream<Object> stream = matchingBeans.keySet().stream()
.map(name -> descriptor.resolveCandidate(name, type, this))
.filter(bean -> !(bean instanceof NullBean));
if (((StreamDependencyDescriptor) descriptor).isOrdered()) {
stream = stream.sorted(adaptOrderComparator(matchingBeans));
}
return stream;
}
else if (type.isArray()) { // 1、判断要注入的是不是一个数组,可见除了集合注入外,也可以以数组的形式注入bean
Class<?> componentType = type.getComponentType();
ResolvableType resolvableType = descriptor.getResolvableType();
Class<?> resolvedArrayType = resolvableType.resolve(type);
if (resolvedArrayType != type) {
componentType = resolvableType.getComponentType().resolve();
}
if (componentType == null) {
return null;
}
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, componentType,
new MultiElementDescriptor(descriptor));
if (matchingBeans.isEmpty()) {
return null;
}
if (autowiredBeanNames != null) {
autowiredBeanNames.addAll(matchingBeans.keySet());
}
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
Object result = converter.convertIfNecessary(matchingBeans.values(), resolvedArrayType);
if (result instanceof Object[]) {
Comparator<Object> comparator = adaptDependencyComparator(matchingBeans);
if (comparator != null) {
Arrays.sort((Object[]) result, comparator);
}
}
return result;
}
else if (Collection.class.isAssignableFrom(type) && type.isInterface()) { // 2、判断注入的是不是集合的接口类(List、Set等)
Class<?> elementType = descriptor.getResolvableType().asCollection().resolveGeneric();
if (elementType == null) {
return null;
}
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, elementType,
new MultiElementDescriptor(descriptor));
if (matchingBeans.isEmpty()) {
return null;
}
if (autowiredBeanNames != null) {
autowiredBeanNames.addAll(matchingBeans.keySet());
}
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
Object result = converter.convertIfNecessary(matchingBeans.values(), type);
if (result instanceof List) {
Comparator<Object> comparator = adaptDependencyComparator(matchingBeans);
if (comparator != null) {
((List<?>) result).sort(comparator);
}
}
return result;
}
else if (Map.class == type) { ///3、判断要注入的是不是map类
ResolvableType mapType = descriptor.getResolvableType().asMap();
Class<?> keyType = mapType.resolveGeneric(0);
if (String.class != keyType) {
return null;
}
Class<?> valueType = mapType.resolveGeneric(1);
if (valueType == null) {
return null;
}
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, valueType,
new MultiElementDescriptor(descriptor));
if (matchingBeans.isEmpty()) {
return null;
}
if (autowiredBeanNames != null) {
autowiredBeanNames.addAll(matchingBeans.keySet());
}
return matchingBeans;
}
else { // 4、其他情况返回空,进入doResolveDependency方法做默认处理
return null;
}
}

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

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

Spring注解之@Autowired的更多相关文章

  1. Spring注解之@Autowired、@Qualifier、@Resource、@Value

    前言 @Autowired.@Qualifier.@Resource.@Value四个注解都是用于注入数据的,他们的作用就和在xml配置文件中的bean标签中写一个标签的作用是一样的!本篇中特别要讲解 ...

  2. spring 注解@Resource @Autowired区别

    1.@Autowired寻找类的时候默认是ByType,也就是通过类的类型来寻找类.不过,也可以通过借助@Qualifier("name")来指定寻找的类名 @Autowired ...

  3. spring注解源码分析--how does autowired works?

    1. 背景 注解可以减少代码的开发量,spring提供了丰富的注解功能.我们可能会被问到,spring的注解到底是什么触发的呢?今天以spring最常使用的一个注解autowired来跟踪代码,进行d ...

  4. Spring注解 @Resource和@Autowired

    @Resource和@Autowired两者都是做bean的注入使用.其实@Resource并不是Spring的注解,他的包是javax.annotation.Resource 需要导入.但是Spri ...

  5. Spring注解标签详解@Autowired @Qualifier等 @Slf4j

    @Slf4j @Slf4j注解实现日志输出 自己写日志的时候,肯定需要: private final Logger logger = LoggerFactory.getLogger(LoggerTes ...

  6. Spring注解@Component、@Repository、@Service、@Controller @Resource、@Autowired、@Qualifier、@scope

    以下内容摘自部分网友的,并加上了自己的理解 @Service用于标注业务层组件(我们通常定义的service层就用这个) @Controller用于标注控制层组件(如struts中的action.Sp ...

  7. spring注解-@Autowired。@Resource。@Service

    Spring的@Autowired注解.@Resource注解和@Service注解 什么是注解 传统的Spring做法是使用.xml文件来对bean进行注入或者是配置aop.事物,这么做有两个缺点: ...

  8. Spring AOP注解通过@Autowired,@Resource,@Qualifier,@PostConstruct,@PreDestroy注入属性的配置文件详解

    本文介绍了使用Spring注解注入属性的方法.使用注解以前,注入属性通过类以及配置文件来实现.现在,注入属性可以通过引入@Autowired注解,或者@Resource,@Qualifier,@Pos ...

  9. Spring AOP注解通过@Autowired,@Resource,@Qualifier,@PostConstruct,@PreDestroy注入属性的

    本文介绍了使用spring注解注入属性的方法. 使用注解以前,注入属性通过类以及配置文件来实现.现在,注入属性可以通过引入@Autowired注解,或者@Resource,@Qualifier,@Po ...

随机推荐

  1. 在可插拔settings的基础上加入类似中间件的设计

    在可插拔settings的基础上加入类似中间件的设计 settings可插拔设计可以看之前的文章 https://www.cnblogs.com/zx125/p/11735505.html 设计思路 ...

  2. Python内置类属性,元类研究

    Python内置类属性 我觉得一切都是对象,对象和元类对象,类对象其实都是一样的,我在最后进行了证明,但是只能证明一半,最后由于元类的父类是type,他可以阻挡对object属性的访问,告终 __di ...

  3. 虚拟机中linux操作系统raid5(5块磁盘,3块做raid,2块做备份)配置流程及损坏磁盘的移除

    1.打开所要用的虚拟机,点击编辑虚拟机设置,点击硬盘,添加 2.一直点击下一步不做修改,直到最后完成 3.按照以上步骤添加5块磁盘 4.点击开启虚拟机,输入用户名root密码登录进去 5.进入虚拟机后 ...

  4. wdCP v3.3.8apache阿里云ssl证书实现智慧软文http转换https的详细操作教程

    先展示一下效果:智慧软文发布系统(https://www.zhihuiruanwen.com) 之前用的是传统的http,发现360浏览器,火狐浏览器,谷歌浏览器均提示不安全的链接,最主要的是第一次打 ...

  5. for循环使用element的折叠面板遇到的问题-3

    需求:for循环渲染上去的表单怎么使用element的表单校验 之前做这个的时候,死活绑不上去,不知道哪里出了问题,后来解决办法是prop要注意用拼接,使它和索引的变量一致 <el-form-i ...

  6. C#中的委托和事件(二)

    引言 如果你看过了 C#中的委托和事件 一文,我想你对委托和事件已经有了一个基本的认识.但那些远不是委托和事件的全部内容,还有很多的地方没有涉及.本文将讨论委托和事件一些更为细节的问题,包括一些大家常 ...

  7. springcloud+kafka集群

    上节说了kafka在linux环境下怎么搭建集群.这节写一下怎么在springcloud中以stream流方式去做kafka集群对接. 1.yml配置 #spring Cloud kafka -- s ...

  8. 在Eclipse中混合Java和Scala编程

    1. 新建项目目录 scala-java-mix 2. 创建 src 目录及子目录: mkdir -p src/main/java mkdir -p src/main/scala 3. 在目录 sca ...

  9. go实践之apiserver搭建

    文章目录 go实践之apiserver搭建 1.配置文件读取 2.数据连接 3.日志初始化 4.server初始化 5.接口编写 go实践之apiserver搭建 本文主要记录下博主用gin搭建app ...

  10. kettle教程---kettle作业调度,附件(excel)配置表名,一个调度完成所有的表操作

    在平时工作当中,会遇到这种情况:复制一个库,几百甚至上千张表,并且无法设置dblink,此时通过kettle可以快速完成该任务. 按照正常的调度,有几百张表,咱们就要写几百个转换去处理,很不科学,下面 ...