@Autowired注解底层是如何实现的

2023/11/29 21:22:54

1.概念:

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

2.注入数据的注解:

@Value
    含义:通过set方式注入基本类型与String,set方法可以省略
    语法:@Value("数据")/@Value("${key}")
    位置:修饰属性,set方法
    注意:如果动态获取必须指定加载资源文件                                                                     <context:property-placeholder location="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也使用了反射技术并且依然是分成字段和方法去处理的。


http://www.jnnr.cn/a/77743.html

相关文章

网络安全比赛A模块任务书

前言 这是作者这几个月来的第一次更新文章&#xff0c;问就是太忙了&#xff0c;最近要去参加国赛&#xff0c;在此重新回来写文章&#xff0c;也不知道能写多久&#xff0c;就当练习了。 一.A模块基础设施设置/安全加固 A-1.登录加固 1.密码策略 a.最小密码长度不少于8个字…

网课搜题系统搭建

网课搜题系统搭建 本平台优点&#xff1a; 多题库查题、独立后台、响应速度快、全网平台可查、功能最全&#xff01; 1.想要给自己的公众号获得查题接口&#xff0c;只需要两步&#xff01; 2.题库&#xff1a; 查题校园题库&#xff1a;查题校园题库后台&#xff08;点击跳…

没有学过C语言可以学Java吗?

Java和C语言之间并没有紧密的关系&#xff0c;虽然Java的实现借鉴了一些C设计&#xff0c;但对学习者来说先学谁后学谁都无所谓。没有学过C语言不可以学Java&#xff0c;不存在一定这个说法&#xff0c;学没学习C语言都可以学Java编程。 学过C肯定能更快的学会Java基础部分&…

Oracle 11g+windows 环境下Ecology7系统安装

环境&#xff1a; Windows 8.1 64位 Oracle 11g 11.2.0.1.0 64位 E-cology 7.1.0.331 安装步聚&#xff1a; 以管理员模式运行Oracle setup.exe文件根据提示安装Oracle&#xff0c;创建数据库&#xff0c;注意修改连接数&#xff08;默认为150&#xff09;创建用户&#xff…

包装行业数智化采购管理系统提升企业采购效率,构筑智慧采购新生态

包装行业作为我国制造体系的重要组成部分&#xff0c;2020年市场规模已步入万亿量级&#xff0c;机会与挑战并存。然而&#xff0c;随着行业壮大&#xff0c;我国传统包装企业在采购环节中的发展痛点也逐渐凸显&#xff0c;如何借助新一代信息技术实现高效的采购流程管理&#…

大数据面试重点之Kafka(五)

大数据面试重点之Kafka&#xff08;五&#xff09; Kafka是如何进行数据备份的&#xff1f; 问过的一些公司&#xff1a;祖龙娱乐参考答案&#xff1a; Kafka的备份的单元是partition&#xff0c;也就是每个partition都都会有leader partiton和follow partiton。其中leader par…

WebRTC 媒体数据传输控制之平滑发送实现

WebRTC 的平滑发送 pacer 模块用于更有节奏地发送音视频媒体数据及音视频媒体传输控制数据&#xff0c;更具体地说&#xff0c;由于音频数据包大多比较小&#xff0c;一般不会超过 MTU&#xff0c;因而 pacer 模块主要用于控制视频数据包的发送。WebRTC 官方有一份文档对 pacer…

Java语言程序设计(进阶版)编程练习题(第二十二章)

目录 22.1 最大连续递增的有序子串 22.2 最大增序子序列 20.3 模式匹配 22.5 同样个数的子序列 22.6 GCD的执行时间 剩下的改日再写。 22.1 最大连续递增的有序子串 package algorithm;import java.util.*;public class OrderlySubtring {public static void main(String[] ar…

scipy.stats常见概率分布-正态分布与泊松分布

常见分布 总结统计工作中几个常用用法在python统计函数库scipy.stats的使用范例 常见术语 pdf&#xff0c;概率密度函数&#xff08;Probability Density Function&#xff09;&#xff0c;连续型随机变量的概率。cdf&#xff0c;累积分布函数&#xff08;Cumulative Distri…

数据挖掘—数据预处理

文章目录数据预处理1 数据清洗缺失值处理异常值处理2 数据集成实体识别冗余属性识别数据变换简单函数变换规范化连续属性离散化属性构造3 数据规约属性归约数值归约Python主要数据预处理函数数据预处理 数据预处理的过程 数据预处理的目的&#xff1a; 1&#xff09;提高数据…

如何免费压缩图片-批量免费压缩图片大小的软件

如何免费压缩图片&#xff0c;今天给大家分享一款免费压缩图片的软件&#xff0c;支持任意格式的照片压缩&#xff0c;不仅可以压缩照片还可以放大图片&#xff0c;支持批量照片自动水印。批量关键词采集全网图片&#xff0c;导入链接批量下载图片&#xff0c;整站图片下载导出…

Linux学习7—软件管理

文章目录一.RPM包管理1.1RPM简介1.2 YUM工具二.源码包管理2.1 源代包包简介2.2 获得源码包2.3 实战案例一.RPM包管理 1.1RPM简介 RPM包&#xff08;Package Manager)由Red Hat公司提出&#xff0c;被众多Linux发行版所采用&#xff0c;也称为二进制包 无需编译&#xff0c;可以…

华为od机考真题-消消乐游戏,算法第08讲:高频真题解析 I

while 1:try:nums = input()i = 0while i < len(nums):if nums[i] * 2 in nums:nums

CISP-PTE学习总结之基础练习题(二)

文章目录基础题目一&#xff1a;SQL注入0x01 题目要求0x02 解题过程基础题目二&#xff1a;文件上传突破0x01 题目要求0x02 解题过程基础题目三&#xff1a;文件包含0x01 题目要求0x02 解题过程0x03 解题总结基础题目四&#xff1a;代码执行漏洞0x01 题目要求0x02 解题过程0x03…

电化学传感器使用-电子学角度分析

目录 CO1数据手册 CO1应用电路-恒电位 电路拆解分析 恒电位 ***电路动态分析&#xff08;忽略RL&#xff09;*** 回归到传感器本身 温度补偿 CO1数据手册 CO1应用电路-恒电位 电路拆解分析 恒电位 如上图&#xff0c;假设RL左端悬空&#xff0c; 电压关系&#xff1a…

Windows保护模式(八)TLB控制寄存器

TLB TLB&#xff08;Translation Lookaside Buffer&#xff09;是 CPU 内部用于缓存线性地址与物理地址映射关系的表。 TLB结构 ATTR&#xff1a;属性 在10-10-12分页模式下&#xff1a;ATTR PDE属性 & PTE属性在2-9-9-12分页模式下&#xff1a;ATTR PDPTE属性 & P…

FME在变更地类流向统计中的应用

“国土变更调查”是全面查清我国土地利用状况&#xff0c;掌握详实准确的土地基础数据&#xff0c;摸清家底&#xff0c;提高土地资源的信息化管理和社会化运用&#xff0c;保障我国国民经济平稳健康发展&#xff0c;以及促进国土资源的合理开发和可持续利用的一项年度工作。 …

5G无线技术基础自学系列 | 5G组网方式

素材来源&#xff1a;《一本读懂5G技术》 一边学习一边整理内容&#xff0c;并与大家分享&#xff0c;侵权即删&#xff0c;谢谢支持&#xff01; 附上汇总贴&#xff1a;5G无线技术基础自学系列 | 汇总_COCOgsta的博客-CSDN博客 5G是移动通信网部署的大势所趋&#xff0c;但…

Nginx反向代理和负载均衡

文章目录Nginx负载均衡案例源码部署nginx设置weight权重ip_hash配置动静分离nginxtomcat部署tomcatNginx反向代理和负载均衡原文Nginx负载均衡案例 主机Ip安装系统Nginx192.168.40.99NginxRHEL8/CentOS8Rs1192.168.100.100HttpdRHEL8/CentOS8Rs2192.168.100.101Httpd TomcatRH…

数据可视化之金秋换新装:2020年服装零售业利润达3523.9亿元

这个夏天终究是结束了&#xff0c;遗憾就留给下一个夏天去填充吧。夏天再见&#xff0c;秋天你好&#xff01; 秋天是美丽的季节&#xff0c;美丽的秋天蕴涵着成熟的魅力&#xff0c;漫山遍野的红叶又为秋天的成熟渲染了一笔艳丽&#xff0c;让人们在严冬之前能够欣赏到最后的…
最新文章