写点什么

java 培训 @Autowired 的实现原理

作者:@零度
  • 2022 年 6 月 07 日
  • 本文字数:6494 字

    阅读完需:约 21 分钟

@Autowired 使用

构造函数注入

public Class Outer {

private Inner inner;

@Autowired

public Outer(Inner inner) {

this.inner = inner;

}

}

属性注入

public Class Outer {

@Autowired

private Inner inner;

}

方法注入

public Class Outer {

private Inner inner;

public Inner getInner() {

return inner;

}

@Autowired

public void setInner(Inner inner) {

this.inner = inner;

}

}

目前绝大部分的代码都使用第 2、第 3 种。第 1 种在 bean 实例化时完成,而第 2、第 3 种的实现原理都是一样的,在属性填充时完成。本篇将介绍第二第三种的是实现原理

在开始之前,如果我们自己设计 @Autowired,我们应该怎么实现?我想做法还是比较简单的

  • 通过反射查找 bean 的 class 下所有注解了 @Autowired 的字段和方法

  • 获取到字段,通过 getBean(字段)获取到对应 bean,然后再通过反射调用 field 的 set 将 bean 注入

@Autowired 源码分析

AutowiredAnnotationBeanPostProcessor 类

该类是 @Autowired 的具体实现类,先预览一下类方法



​发现实际有机会介入 bean 的创建操作只有可能是后置处理器,用于后置处理的有 3 个方法,其中一个过时不用,分别是 postProcessMergedBeanDefinition、postProcessProperties 后置处理,我们再看一下这 2 个方法的具体代码_java培训

public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter

implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {

...

@Override

public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {

// 1. 寻找 bean 中所有被 @Autowired 注释的属性,并将属性封装成 InjectedElement 类型

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

metadata.checkConfigMembers(beanDefinition);

}

...

@Override

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

// 1. 寻找通过 @Autowired 注解的属性或者方法

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

try {

// 2. 注入

metadata.inject(bean, beanName, pvs);

}

catch (BeanCreationException ex) {

throw ex;

}

catch (Throwable ex) {

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

}

return pvs;

}

...

}

跟我们的猜想是一样的,首先先找出所有注解了 @Autowired 的属性或者方法,然后进行注入,当然 postProcessMergedBeanDefinition 后置处理器的调用肯定是在 postProcessProperties 之前的,这里我们回顾一下 spring bean 的创建过程。

2 个处理器我已用黄色标出



1. 查找所有 @Autowired

// 寻找 bean 中所有被 @Autowired 注释的属性,并将属性封装成 InjectedElement 类型

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

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

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

// 获取缓存的 key 值,一般以 beanName 做 key

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

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

// 从缓存中获取 metadata

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

// 检测 metadata 是否需要更新

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

synchronized (this.injectionMetadataCache) {

metadata = this.injectionMetadataCache.get(cacheKey);

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

if (metadata != null) {

metadata.clear(pvs);

}

// 通过 clazz 类,查找所有 @Autowired 的属性或者方法,并封装成 InjectionMetadata 类型

metadata = buildAutowiringMetadata(clazz);

// 将 metadata 加入缓存

this.injectionMetadataCache.put(cacheKey, metadata);

}

}

}

return metadata;

}

可以看到 spring 依然在用缓存的方式提高性能,继续跟踪核心代码 buildAutowiringMetadata(clazz)

private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {

// 查看 clazz 是否有 Autowired 注解

if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {

return InjectionMetadata.EMPTY;

}

// 这里需要注意 AutowiredFieldElement,AutowiredMethodElement 均继承了 InjectionMetadata.InjectedElement

// 因此这个列表是可以保存注解的属性和被注解的方法的

List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();

Class<?> targetClass = clazz;

// 1. 通过 do while 循环,递归的往直接继承的父类寻找 @Autowired

do {

final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();


// 2. 通过反射,获取所有属性,doWithLocalFields 则是循环的对每个属性应用以下匿名方法

ReflectionUtils.doWithLocalFields(targetClass, field -> {

// 判断当前 field 属性是否含有 @Autowired 的注解

MergedAnnotation<?> ann = findAutowiredAnnotation(field);

if (ann != null) {

// 返回该属性在类中的修饰符,如果等于 static 常量,则抛出异常,@Autowired 不允许注解在静态属性上

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

if (logger.isInfoEnabled()) {

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

}

return;

}

// @Autowired 有 required 属性,获取 required 的值,默认为 true

boolean required = determineRequiredStatus(ann);

// 3. 将 field 封装成 InjectedElement,并添加到集合中,这里用的是 AutowiredFieldElement

currElements.add(new AutowiredFieldElement(field, required));

}

});

// 4. @Autowired 可以注解在方法上

ReflectionUtils.doWithLocalMethods(targetClass, method -> {

Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);

if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {

return;

}

MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);

if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {

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

if (logger.isInfoEnabled()) {

logger.info("Autowired annotation is not supported on static methods: " + method);

}

return;

}

if (method.getParameterCount() == 0) {

if (logger.isInfoEnabled()) {

logger.info("Autowired annotation should only be used on methods with parameters: " +

method);

}

}

boolean required = determineRequiredStatus(ann);

PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);

// 5. 将方法封装成 InjectedElement,并添加到集合中,这里用的是 AutowiredMethodElement

currElements.add(new AutowiredMethodElement(method, required, pd));

}

});

elements.addAll(0, currElements);

// 返回直接继承的父类

targetClass = targetClass.getSuperclass();

}

// 如果父类不为空则需要把父类的 @Autowired 属性或方法也找出

while (targetClass != null && targetClass != Object.class);

// 6. new InjectionMetadata(clazz, elements),将找到的所有的待注入属性或方法生成 metadata 返回

return InjectionMetadata.forElements(elements, clazz);

}

  • 外层 do … while … 的循环被用于递归的查找父类的 @Autowired 属性或方法

  • 通过反射的方式获取到所有属性并循环验证每一个属性是否被 @Autowired 注解

  • 将查找到包含 @Autowired 注解的 filed 封装成 AutowiredFieldElement,加入到列表中

  • 循环查找在方法上的注解

  • 将找到的方法封装成 AutowiredMethodElement,并加入列表

这里需要特别强调一点,InjectedElement 被 AutowiredFieldElement、AutowiredMethodElement 所继承,他们都有各自的 inject 函数,实现各自的注入。因此改 ArrayList elements 是拥有 2 种类型的属性_java视频



  • 将找到的所有元素列表和 clazz 作为参数生成 metadata 数据返回

2. 注入

// 注入

metadata.inject(bean, beanName, pvs);

public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {

// 获取所有需要被注入的元素

Collection<InjectedElement> checkedElements = this.checkedElements;

Collection<InjectedElement> elementsToIterate =

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

// 迭代的元素不为空

if (!elementsToIterate.isEmpty()) {

for (InjectedElement element : elementsToIterate) {

if (logger.isTraceEnabled()) {

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

}

// 循环注入,这里有可能是 AutowiredFieldElement 也可能 AutowiredMethodElement,因此调用的 inject 是 2 个不同的方法

element.inject(target, beanName, pvs);

}

}

}

利用 for 循环,遍历刚刚我们查到到的 elements 列表,进行注入。

在上面有特别提醒,这里的 element 有可能是 AutowiredFieldElement 类型、或 AutowiredMethodElement 类型。各自代表 @Autowired 注解在属性上、以及注解在方法上的 2 种不同元素。因此他们调用的 element.inject(target, beanName, pvs);也是不一样的

2.1 字段注入(AutowiredFieldElement)

private class AutowiredFieldElement extends InjectionMetadata.InjectedElement {

@Override

protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {

Field field = (Field) this.member;

Object value;

if (this.cached) {

value = resolvedCachedArgument(beanName, this.cachedFieldValue);

}

else {

// 专门用于注入的包装类,包装构造函数参数,方法参数或字段

DependencyDescriptor desc = new DependencyDescriptor(field, this.required);

// 设置 class

desc.setContainingClass(bean.getClass());

// 需要被自动注入的 beanNames,这里只有可能 = 1,方法注入时才有可能为多个

Set<String> autowiredBeanNames = new LinkedHashSet<>(1);

Assert.state(beanFactory != null, "No BeanFactory available");

TypeConverter typeConverter = beanFactory.getTypeConverter();// 获取类型转换器

try {

// 通过 beanFactory 获取属性对应的值,比如需要调用 getBean("b")获取依赖的属性单例,并且通过自动转型转为需要的类型

value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);

}

catch (BeansException ex) {

throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);

}

synchronized (this) {

if (!this.cached) {

if (value != null || this.required) {

this.cachedFieldValue = desc;

// 注册依赖,

registerDependentBeans(beanName, autowiredBeanNames);

// 因为是属性注入,因此这里只有可能等于 1

if (autowiredBeanNames.size() == 1) {

String autowiredBeanName = autowiredBeanNames.iterator().next();

if (beanFactory.containsBean(autowiredBeanName) &&

beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {

// 缓存当前 value

this.cachedFieldValue = new ShortcutDependencyDescriptor(

desc, autowiredBeanName, field.getType());

}

}

}

else {

this.cachedFieldValue = null;

}

this.cached = true;

}

}

}

if (value != null) {

// 通过反射,将 value 值设置到 bean 中

ReflectionUtils.makeAccessible(field);

field.set(bean, value);

}

}

}

上方大部分的工作都在做待注入 bean 的获取以及类型的转换,如果深究下去可以再把 spring Ioc 讲一遍,但是核心还是 getBean(字段)获取到对应 bean…我们这里就关心核心的语句,就是这 2 句

if (value != null) {

// 通过反射,将 value 值设置到 bean 中

ReflectionUtils.makeAccessible(field);

field.set(bean, value);

}

spring 通过反射的方式,调用 field 的 set 进行属性的注入

2.2 方法注入(AutowiredMethodElement)

private class AutowiredMethodElement extends InjectionMetadata.InjectedElement {


@Override

protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {

if (checkPropertySkipping(pvs)) {

return;

}

// @Autowired 标注在方法上

Method method = (Method) this.member;

Object[] arguments;

if (this.cached) {

// Shortcut for avoiding synchronization...

// 有缓存

arguments = resolveCachedArguments(beanName);

}

else {

// 没缓存,直接获取方法上所有的参数

int argumentCount = method.getParameterCount();

arguments = new Object[argumentCount];

DependencyDescriptor[] descriptors = new DependencyDescriptor[argumentCount];

Set<String> autowiredBeans = new LinkedHashSet<>(argumentCount);

Assert.state(beanFactory != null, "No BeanFactory available");

TypeConverter typeConverter = beanFactory.getTypeConverter();

// 循环所有参数

for (int i = 0; i < arguments.length; i++) {

MethodParameter methodParam = new MethodParameter(method, i);

DependencyDescriptor currDesc = new DependencyDescriptor(methodParam, this.required);

currDesc.setContainingClass(bean.getClass());

descriptors[i] = currDesc;

try {

// 通过 beanFactory,获取代注入的 bean,并进行类型转换

Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter);

if (arg == null && !this.required) {

arguments = null;

break;

}

arguments[i] = arg;

}

catch (BeansException ex) {

throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(methodParam), ex);

}

}

synchronized (this) {

if (!this.cached) {

if (arguments != null) {

DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descriptors, arguments.length);

// 注册依赖

registerDependentBeans(beanName, autowiredBeans);

// 如果自动注入的个数 = 参数个数,则缓存

if (autowiredBeans.size() == argumentCount) {

Iterator<String> it = autowiredBeans.iterator();

Class<?>[] paramTypes = method.getParameterTypes();

for (int i = 0; i < paramTypes.length; i++) {

String autowiredBeanName = it.next();

if (beanFactory.containsBean(autowiredBeanName) &&

beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) {

// 缓存

cachedMethodArguments[i] = new ShortcutDependencyDescriptor(

descriptors[i], autowiredBeanName, paramTypes[i]);

}

}

}

// 缓存方法

this.cachedMethodArguments = cachedMethodArguments;

}

else {

this.cachedMethodArguments = null;

}

this.cached = true;

}

}

}

if (arguments != null) {

try {

// 反射调用注入方法,将获取到的所有 bean 作为参数

ReflectionUtils.makeAccessible(method);

method.invoke(bean, arguments);

}

catch (InvocationTargetException ex) {

throw ex.getTargetException();

}

}

}

}

这里与属性注入最大的区别在于,@Autowired 注解在方法上,方法可以拥有多个参数,因此这里需要通过循环将一个个获取,而获取 bean 的方式于上面一样,本质都是通过 getBean 获取。

而核心语句还是 2 句

// 反射调用注入方法,将获取到的所有 bean 作为参数

ReflectionUtils.makeAccessible(method);

method.invoke(bean, arguments);

与属性注入不同的是,当 @Autowired 注解在方法上,例如我们注解在 setter 方法上,则只需要直接调用该 setter 方法将参数数组传入即可以,即使用 invoke 触发方法,具体属性赋值的过程在 setter 方法中由用户自行编写

文章来源于 Java 知音

用户头像

@零度

关注

关注尚硅谷,轻松学IT 2021.11.23 加入

IT培训 www.atguigu.com

评论

发布
暂无评论
java培训 @Autowired 的实现原理_JAVA开发_@零度_InfoQ写作社区