Java @Autowired注解底层原理详细分析

 更新时间:2022年11月05日 10:11:17   作者:OlaiolaiO  
@Autowired注解可以用在类属性,构造函数,setter方法和函数参数上,该注解可以准确地控制bean在何处如何自动装配的过程。在默认情况下,该注解是类型驱动的注入

1.概念

@Autowired 是 Spring 提供的注解,默认的注入方式为 byType (按类型自动注入);@Autowired 注释,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作;通过 @Autowired的使用来消除 set ,get方法。

2.注入数据的注解

@Value

含义:通过set方式注入基本类型与String,set方法可以省略

语法:@Value("数据")/@Value("${key}")

位置:修饰属性,set方法

注意:如果动态获取必须指定加载资源文件 <context:property-placeholderlocation="classpath:msg.properties"> </context:property-placeholder>

@Autowired

替换:autowire属性,自动装配(按照类型装配,通过set方法,且方法可以省略)

位置:修饰属性,set方法

语法:@Autowired(required="true")

注意:

1.如果容器中没有一个可以与之匹配且required属性为true则会报异常

NoSuchBeanDefinitionException

2.如果容器中有多个可以类型可以与之匹配,则自动切换为按照名称装配

3.如果名称也没有匹配,则报异常NoUniqueBeanDefinitionException

3.@Autowired注解是如何实现的

@Autowired注解在spring源代码里的定义:

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 {
    boolean required() default true;
}

通过代码看到Autowired注解可以应用在构造方法,普通方法,参数,字段,以及注解这五种类型的地方,它的保留策略是在运行时。

在Spring源代码当中,Autowired注解位于包org.springframework.beans.factory.annotation之中,实现逻辑位于类:AutowiredAnnotationBeanPostProcessor之中。

核心处理代码如下:

private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
  LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<>();
  Class<?> targetClass = clazz;//需要处理的目标类
  do {
   final LinkedList<InjectionMetadata.InjectedElement> currElements = new LinkedList<>();
            /*通过反射获取该类所有的字段,并遍历每一个字段,并通过方法findAutowiredAnnotation遍历每一个字段的所用注解,并如果用autowired修饰了,则返回auotowired相关属性*/  
   ReflectionUtils.doWithLocalFields(targetClass, field -> {
    AnnotationAttributes ann = findAutowiredAnnotation(field);
    if (ann != null) {//校验autowired注解是否用在了static方法上
     if (Modifier.isStatic(field.getModifiers())) {
      if (logger.isWarnEnabled()) {
       logger.warn("Autowired annotation is not supported on static fields: " + field);
      }
      return;
     }//判断是否指定了required
     boolean required = determineRequiredStatus(ann);
     currElements.add(new AutowiredFieldElement(field, required));
    }
   });
            //和上面一样的逻辑,但是是通过反射处理类的method
   ReflectionUtils.doWithLocalMethods(targetClass, method -> {
    Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
    if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
     return;
    }
    AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
    if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
     if (Modifier.isStatic(method.getModifiers())) {
      if (logger.isWarnEnabled()) {
       logger.warn("Autowired annotation is not supported on static methods: " + method);
      }
      return;
     }
     if (method.getParameterCount() == 0) {
      if (logger.isWarnEnabled()) {
       logger.warn("Autowired annotation should only be used on methods with parameters: " +
         method);
      }
     }
     boolean required = determineRequiredStatus(ann);
     PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
                   currElements.add(new AutowiredMethodElement(method, required, pd));
    }
   });
    //用@Autowired修饰的注解可能不止一个,因此都加在currElements这个容器里面,一起处理  
   elements.addAll(0, currElements);
   targetClass = targetClass.getSuperclass();
  }
  while (targetClass != null && targetClass != Object.class);
  return new InjectionMetadata(clazz, elements);
 }

最后这个方法返回的就是包含所有带有autowire注解修饰的一个InjectionMetadata集合。这个类由两部分组成:

public InjectionMetadata(Class<?> targetClass, Collection<InjectedElement> elements) {
  this.targetClass = targetClass;
  this.injectedElements = elements;
 }

一是处理的目标类,二就是上述方法获取到的所以elements集合。

有了目标类,与所有需要注入的元素集合之后,就可以实现autowired的依赖注入逻辑了,实现的方法如下:

@Override
public PropertyValues postProcessPropertyValues(
  PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
 
 InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
 try {
  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;
}

它调用的方法是InjectionMetadata中定义的inject方法,如下

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);
    }
    element.inject(target, beanName, pvs);
   }
  }
 }

其逻辑就是遍历,然后调用inject方法,inject方法其实现逻辑如下:

protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs)
  throws Throwable {
 if (this.isField) {
  Field field = (Field) this.member;
  ReflectionUtils.makeAccessible(field);
  field.set(target, getResourceToInject(target, requestingBeanName));
 }
 else {
  if (checkPropertySkipping(pvs)) {
   return;
  }
  try {
   Method method = (Method) this.member;
   ReflectionUtils.makeAccessible(method);
   method.invoke(target, getResourceToInject(target, requestingBeanName));
  }
  catch (InvocationTargetException ex) {
   throw ex.getTargetException();
  }
 }
}

inject也使用了反射技术并且依然是分成字段和方法去处理的。

到此这篇关于Java @Autowired注解底层原理详细分析的文章就介绍到这了,更多相关Java @Autowired注解内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java教程之java注解annotation使用方法

    java教程之java注解annotation使用方法

    这篇文章主要介绍了java注解annotation使用方法,注解可以定义到方法上,类上,一个注解相当与一个类,就相当于实例了一个对象,加上了注解,就相当于加了一个标志
    2014-01-01
  • 如何查找YUM安装的JAVA_HOME环境变量详解

    如何查找YUM安装的JAVA_HOME环境变量详解

    这篇文章主要给大家介绍了关于如何查找YUM安装的JAVA_HOME环境变量的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2017-10-10
  • Java 网络编程之 TCP 实现简单的聊天系统

    Java 网络编程之 TCP 实现简单的聊天系统

    这篇文章主要介绍了Java 网络编程之 TCP 实现简单的聊天系统,帮助大家更好的理解和学习Java 网络编程,感兴趣的朋友可以了解下
    2020-11-11
  • Spring整合Mybatis 扫描注解创建Bean报错的解决方案

    Spring整合Mybatis 扫描注解创建Bean报错的解决方案

    这篇文章主要介绍了Spring 整合Mybatis 扫描注解创建Bean报错的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • Java的StringBuilder在高性能场景下的正确用法

    Java的StringBuilder在高性能场景下的正确用法

    StringBuilder 对字符串的操作是直接改变字符串对象本身,而不是生成新的对象,所以新能开销小.与StringBuffer相比StringBuilder的性能略高,StringBuilder则没有保证线程的安全,从而性能略高于StringBuffer,需要的朋友可以参考下
    2023-05-05
  • Java+mysql本地图片上传数据库及下载示例

    Java+mysql本地图片上传数据库及下载示例

    本篇文章主要介绍了Java+mysql本地图片上传数据库及下载示例,具有一定的参加价值,有兴趣的可以了解一下。
    2017-01-01
  • java web中的servlet3 upload上传文件实践

    java web中的servlet3 upload上传文件实践

    这篇文章主要介绍了servlet3 upload上传文件实践,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2017-11-11
  • 详解基于SpringBoot使用AOP技术实现操作日志管理

    详解基于SpringBoot使用AOP技术实现操作日志管理

    这篇文章主要介绍了详解基于SpringBoot使用AOP技术实现操作日志管理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-11-11
  • Java Stax解析XML示例

    Java Stax解析XML示例

    这篇文章主要介绍了Java Stax解析XML示例,帮助大家更好的理解和使用Java,感兴趣的朋友可以了解下
    2020-09-09
  • Java String 拼接字符串原理详解

    Java String 拼接字符串原理详解

    在本篇文章里小编给大家分享的是关于Java String 拼接字符串原理详解的相关资源内容,有需要的朋友们可以跟着学习参考下。
    2020-02-02

最新评论