一、简介
众所周知,Spring的依赖注入(DI)对Spring IOC 有着举足轻重的作用,是Spring灵魂所在。本篇文章就从日常开发中最常用的注解@Autowired开始,着手分析Spring是如何通过它们将Bean所需的外部资源注入其中.
1.1、@Autowired 注入规则
@Autowired可以应用在 非静态字段、非静态方法、构造器上面注入bean。
1.2、 @Autowired 注入过程
- 元信息解析
- 依赖查找
- 依赖注入(字段、方法)
1.3、代码准备
@Configuration
public class AnnotationDependencyInjectionResolutionDemo {
// DependencyDescriptor ->
// 必须(required=true)
// 实时注入(eager=true)
// 通过类型(User.class)
// 字段名称("user")
// 是否首要(primary = true)
@Autowired(required = false) // 依赖查找(处理)
private User user;
public static void main(String[] args) {
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 注册 Configuration Class(配置类) -> Spring Bean
applicationContext.register(AnnotationDependencyInjectionResolutionDemo.class);
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(applicationContext);
String xmlResourcePath = "classpath:/META-INF/dependency-lookup-context.xml";
// 加载 XML 资源,解析并且生成 BeanDefinition
beanDefinitionReader.loadBeanDefinitions(xmlResourcePath);
// 启动 Spring 应用上下文
applicationContext.refresh();
// 依赖查找 QualifierAnnotationDependencyInjectionDemo Bean
AnnotationDependencyInjectionResolutionDemo demo = applicationContext.getBean(AnnotationDependencyInjectionResolutionDemo.class);
// 期待输出 superUser Bean
System.out.println("demo.user = " + demo.user);
applicationContext.close();
}
}
xml中的
<bean id="user" class="com.dukun.study.ioc.domain.User">
<property name="id" value="1"/>
<property name="name" value="坤仔"/>
<property name="city" value="SHANGHAI"/>
<property name="workCities" value="BEIJING,SHANGHAI"/>
</bean>
二、源码分析
2.1 @Autowired的定义
首先,我们看@Autowired的定义里可以发现,它是通过AutowiredAnnotationBeanPostProcessor去实现解析的
/*
* @author Juergen Hoeller
* @author Mark Fisher
* @author Sam Brannen
* @since 2.5
* @see AutowiredAnnotationBeanPostProcessor
* @see Qualifier
* @see Value
*/
@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;
}
2.2 AutowiredAnnotationBeanPostProcessor
在看一下AutowiredAnnotationBeanPostProcessor
它的继承图表:
由此继承图标我们能得出如下结论:
- 实现了BeanPostProcessor,所以介入到Bean的初始化前后
- 实现了InstantiationAwareBeanPostProcessor接口,所以可以介入到Bean的实例化前后
- 实现了MergedBeanDefinitionPostProcessor,说明它可以合并bean的定义信息(用来合并parent="user" 父类中属性)
- 实现了BeanFactoryAware(BeanFactory的回调),
- 实现了PriorityOrdered(排序)
2.3、第一阶段:元信息解析
解析@Resource
注解的不是这个类,而是CommonAnnotationBeanPostProcessor
,但本文只会以AutowiredAnnotationBeanPostProcessor
为例做深入分析~~~(解析@Autowired
)
2.3.1、AutowiredAnnotationBeanPostProcessor 中属性
// 该处理器支持解析的注解们~~~(这里长度设置为4)
// 默认支持的是3个(当然你可以自己添加自定义的依赖注入的注解 这点非常强大)
private final Set<Class<? extends Annotation>> autowiredAnnotationTypes = new LinkedHashSet<>();
// @Autowired(required = false)这个注解的属性值名称
private String requiredParameterName = "required";
// 这个值一般请不要改变(若改成false,效果required = false的作用是相反的了)
private boolean requiredParameterValue = true;
private int order = Ordered.LOWEST_PRECEDENCE - 2;
@Nullable
private ConfigurableListableBeanFactory beanFactory;
// 对@Lookup方法的支持 本文不讨论
private final Set<String> lookupMethodsChecked = Collections.newSetFromMap(new ConcurrentHashMap<>(256));
// 构造函数注入,本文也不讨论
private final Map<Class<?>, Constructor<?>[]> candidateConstructorsCache = new ConcurrentHashMap<>(256);
// 方法注入、字段filed注入 本文的重中之重
// 此处InjectionMetadata这个类非常重要,到了此处@Autowired注解含义已经没有了,完全被准备成这个元数据了
// 所以方便我们自定义注解的支持~~~优秀
// InjectionMetadata持有targetClass、Collection<InjectedElement> injectedElements等两个重要属性
// 其中InjectedElement这个抽象类最重要的两个实现为:AutowiredFieldElement和AutowiredMethodElement
private final Map<String, InjectionMetadata> injectionMetadataCache = new ConcurrentHashMap<>(256);
2.3.2、构造方法(也是唯一的一个构造方法)
AutowiredAnnotationBeanPostProcessor执行构造方法时集合autowiredAnnotationTypes中依次放入了三个注解类@Autowired、@Value和JSR-330标准的@Inject
// 这是它唯一构造函数 默认支持下面三种租借(当然@Inject需要额外导包)
// 请注意:此处@Value注解也是被依赖注入解析的~~~~~~~~
// 当然如果你需要支持到你的自定义注解,你还可以调用下面的set方法添加。
&#