iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >Java超详细分析@Autowired原理
  • 904
分享到

Java超详细分析@Autowired原理

2024-04-02 19:04:59 904人浏览 泡泡鱼

Python 官方文档:入门教程 => 点击学习

摘要

目录@Autowired使用@Autowired源码分析1.查找所有@Autowired2. 注入2.1 字段注入(AutowiredFieldElement)2.2 方法注入(Au

@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个方法,其中一个过时不用,分别是postProceSSMergedBeanDefinitionpostProcessProperties后置处理,我们再看一下这2个方法的具体代码

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,并加入列表

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

  • 将找到的所有元素列表和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超详细分析@Autowired原理的文章就介绍到这了,更多相关Java @Autowired内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Java超详细分析@Autowired原理

本文链接: https://www.lsjlt.com/news/152604.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Java超详细分析@Autowired原理
    目录@Autowired使用@Autowired源码分析1.查找所有@Autowired2. 注入2.1 字段注入(AutowiredFieldElement)2.2 方法注入(Au...
    99+
    2024-04-02
  • Java@Autowired注解底层原理详细分析
    目录1.概念2.注入数据的注解3.@Autowired注解是如何实现的1.概念 @Autowired 是 Spring 提供的注解,默认的注入方式为 byType (按类型自动注入)...
    99+
    2022-11-13
    Java @Autowired注解 Java @Autowired Java @Autowired原理
  • Java CopyOnWriteArrayList源码超详细分析
    目录一、概述二、类图三、核心方法1.add()2.set()3.remove()4.get()5.size()四、总结一、概述 CopyOnWriteArrayList是基于写时复制...
    99+
    2022-11-13
    Java CopyOnWriteArrayList Java CopyOnWriteArrayList源码
  • C++ 超详细分析多态的原理与实现
    目录多态的定义及实现多态的构成条件虚函数重写C++11的override和final抽象类多态的原理虚函数表动态绑定与静态绑定单继承和多继承关系的虚函数表单继承中的虚函数表多继承中的...
    99+
    2024-04-02
  • Java超详细分析讲解哈希表
    目录哈希表概念哈希函数的构造平均数取中法折叠法保留余数法哈希冲突问题以及解决方法开放地址法再哈希函数法公共溢出区法链式地址法哈希表的填充因子代码实现哈希函数添加数据删除数据判断哈希表...
    99+
    2024-04-02
  • Redis集群原理详细分析
    目录一致性哈希Redis 集群一致性哈希 节点的增加和减少,大部分节点的 Hash一致 package consistenthash import ( "hash/crc32" ...
    99+
    2022-12-19
    Redis集群 Redis集群原理 Redis集群模式
  • Java超详细分析垃圾回收机制
    目录前言垃圾回收概述内存溢出和内存泄漏垃圾回收算法标记阶段STW(Stop-the-World)回收阶段标记-清除算法复制算法标记-压缩算法三种算法的比较总结前言 在前面我们对类加载...
    99+
    2024-04-02
  • Java超详细分析泛型与通配符
    目录1.泛型1.1泛型的用法1.1.1泛型的概念1.1.2泛型类1.1.3类型推导1.2裸类型1.3擦除机制1.3.1关于泛型数组1.3.2泛型的编译与擦除1.4泛型的上界1.4.1...
    99+
    2024-04-02
  • Java栈与队列超详细分析讲解
    目录一、栈(Stack)1、什么是栈?2、栈的常见方法3、自己实现一个栈(底层用一个数组实现)二、队列(Queue)1、什么是队列?2、队列的常见方法3、队列的实现(单链表实现)4、...
    99+
    2024-04-02
  • Java文件与IO流操作原理详细分析
    目录一、文件1、基本解释2、常用的文件操作3、获取文件相关信息4、目录操作和文件删除二、IO流原理及分类1、IO流原理2、流的分类3、IO流体系图一、文件 1、基本解释 (1)什么是...
    99+
    2024-04-02
  • AndroidLock锁实现原理详细分析
    目录Lock简介synchronized和lock的区别写个Demolock源码总结Lock简介 Lock接口位于J.U.C下locks包内,其定义了Lock应该具备的方法。 Loc...
    99+
    2023-02-17
    Android Lock锁 Android Lock锁原理
  • 二分算法(java超详细)
    文章目录 目录 文章目录 一、二分查找 1. 整数二分 1.1 二分查找算法模板1 1.2 二分查找算法模板2 1.3 二分查找算法模板3 1.4 二分查找算法模板4 1.5 二分查找算法模板5 练习题目+详解 ...
    99+
    2023-09-08
    算法 java 数据结构
  • C++超详细分析顺序表
    本次我们解剖顺序表将从以下三个结构: 1、静态顺序表和动态顺序表 2、顺序表实现增删查改等常见接口 3、顺序表相关OJ题练习 什么是顺序表 顺序表是用一段物理地址连续的存储单元依次存...
    99+
    2024-04-02
  • C++超详细分析红黑树
    目录红黑树红黑树的概念红黑树的性质红黑树结点的定义红黑树的插入操作情况一情况二情况三红黑树的验证用红黑树封装map、set红黑树的迭代器封装map封装set红黑树 红黑树的概念 红黑...
    99+
    2024-04-02
  • Javacurrent并发包超详细分析
    目录并发包ConcurrentHashMapCountDownLatch倒计数触发CyclicBarrier循环屏障Semaphore指示灯Exchanger交换者并发包 curre...
    99+
    2023-02-21
    Java current并发包 Java current
  • Vue组件实现原理详细分析
    目录1.渲染组件2.组件的状态与自更新3.组件实例和生命周期4.props与组件状态的被动更新5.setup函数的作用与实现6.组件事件和emit的实现7.插槽的工作原理及实现8.注...
    99+
    2023-01-18
    Vue组件 Vue组件原理
  • Java超详细分析继承与重写的特点
    概念:继承是面向对象语法三大特征之一,继承可以降低代码的沉余度,提高编程的效率。通过继承子类可以随意调用父类中的某些属性与方法,一个子类只能继承一个父类,一个父类可以被多个子类继承。...
    99+
    2024-04-02
  • Java Autowired注解深入分析
    今天是正月初八,先祝大家新年快乐。前几天遇见了一次Autowired注入失败的问题,所以找时间研究了一下相关的Spring源码,分享一下。如果哪位大佬发现问题,请帮忙反馈。分享之前,...
    99+
    2023-01-31
    Java Autowired注解 Java Autowired
  • TypeScript面向对象超详细分析
    目录1 面向对象原则2 TypeScript类2.1 类的定义2.2 创建实例对象3 类的继承4 static关键字5 抽象类和抽象方法6 类属性权限修饰符6.1 public(公有...
    99+
    2022-11-13
    TypeScript面向对象 TypeScript面向对象编程
  • SpringBoot超详细分析启动流程
    目录1、创建SpringApplication1.1、判断web类型1.2、加载上下文初始化器1.3、加载监听器1.4、获得引导类类型2、执行SpringApplication的ru...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作