当前位置: 首页 > news >正文

@Autowired注解的实现原理

        @Autowired注解可以被标注在构造函数、属性、setter方法或配置方法上,用于实现依赖自动注入。

这里对@Autowired注解底层进行源码分析

参考:https://blog.csdn.net/One_L_Star/article/details/114829247

        @Autowired注解的作用是由AutowiredAnnotationBeanPostProcessor实现的,查看该类的源码会发现它实现了MergedBeanDefinitionPostProcessor接口,进而实现了接口中的postProcessMergedBeanDefinition方法,@Autowired注解正是通过这个方法实现注入类型的预解析,将需要依赖注入的属性信息封装到InjectionMetadata类中,InjectionMetadata类中包含了哪些需要注入的元素及元素要注入到哪个目标类中,在Spring容器启动的过程中初始化单例bean的时候通过populateBean方法实现对属性的注入。

   

public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {

public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
    if (beanType != null) {
        InjectionMetadata metadata = this.findAutowiringMetadata(beanName, beanType, (PropertyValues)null);
        metadata.checkConfigMembers(beanDefinition);
    }
}

public class InjectionMetadata {
	private static final Log logger = LogFactory.getLog(InjectionMetadata.class);
	private final Class<?> targetClass;
	private final Collection<InjectedElement> injectedElements;
	private volatile Set<InjectedElement> checkedElements;

        

 Spring对autowire注解的实现逻辑位于类:AutowiredAnnotationBeanPostProcessor#postProcessProperties之中,——>findAutowiringMetadata——>buildAutowiringMetadata,核心代码就在buildAutowiringMetadata方法里面

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

最后返回包含所有带有autowire注解修饰的一个InjectionMetadata集合,如下

  • targetClass:要处理的目标类
  • elements:上述方法获取到的所以elements集合
public InjectionMetadata(Class<?> targetClass, Collection<InjectionMetadata.InjectedElement> elements) {
    this.targetClass = targetClass;
    this.injectedElements = elements;
}

        

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

public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) {
    if (!this.validatedBeanNames.contains(beanName)) {
        if (!this.shouldSkip(this.beanFactory, beanName)) {
            List<String> invalidProperties = new ArrayList();
            PropertyDescriptor[] var6 = pds;
            int var7 = pds.length;
 
            for(int var8 = 0; var8 < var7; ++var8) {
                PropertyDescriptor pd = var6[var8];
                if (this.isRequiredProperty(pd) && !pvs.contains(pd.getName())) {
                    invalidProperties.add(pd.getName());
                }
            }
 
            if (!invalidProperties.isEmpty()) {
                throw new BeanInitializationException(this.buildExceptionMessage(invalidProperties, beanName));
            }
        }
 
        this.validatedBeanNames.add(beanName);
    }
 
    return pvs;
}

 调用InjectionMetadata中定义的inject方法:

public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    Collection<InjectionMetadata.InjectedElement> checkedElements = this.checkedElements;
    Collection<InjectionMetadata.InjectedElement> elementsToIterate = checkedElements != null ? checkedElements : this.injectedElements;
    if (!((Collection)elementsToIterate).isEmpty()) {
        Iterator var6 = ((Collection)elementsToIterate).iterator();
 
        while(var6.hasNext()) {
            InjectionMetadata.InjectedElement element = (InjectionMetadata.InjectedElement)var6.next();
            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, this.getResourceToInject(target, requestingBeanName));
    } else {
        if (this.checkPropertySkipping(pvs)) {
            return;
        }
 
        try {
            Method method = (Method)this.member;
            ReflectionUtils.makeAccessible(method);
            // 注入的bean的名字,这个方法的功能就是根据这个bean的名字去拿到它
            method.invoke(target, this.getResourceToInject(target, requestingBeanName));
        } catch (InvocationTargetException var5) {
            throw var5.getTargetException();
        }
    }
}
  • 使用了反射技术,分成字段和方法去处理的。
  • makeAccessible这样的可以称之为暴力破解的方法,通过反射技术对对象进行实例化和赋值
  • getResourceToInject方法的参数就是要注入的bean的名字,这个方法的功能就是根据这个bean的名字去拿到它

@AutoWired自动注入过程图:

 

相关文章:

  • 【C语言】指针(进阶)
  • Opencv 图像处理:数字图像的必会知识
  • 【Linux】Linux下的编辑器——vim
  • JVM是什么?
  • 基于ssm广东东莞大益球队管理系统-计算机毕业设计源码+LW文档
  • 对抗生成网络GAN系列——AnoGAN原理及缺陷检测实战
  • 动态卷积条件卷积
  • 元宇宙发展演变及安全风险研究
  • 数据结构线性表之顺序表的实现
  • 基于CNN的字符型验证码识别系统设计开发[完整源码实战]
  • 【轻敲stl的大门】函数模板和类模板
  • 网络安全红队常用的攻击方法及路径
  • IMX6ULL学习笔记(10)——通过TFTP烧录Linux内核
  • 【C++】STL —— String类不会怎么办? 看文档(万字详解)
  • Spring中@Autowired注解实现原理
  • create-react-app做的留言板
  • iOS高仿微信项目、阴影圆角渐变色效果、卡片动画、波浪动画、路由框架等源码...
  • Laravel 中的一个后期静态绑定
  • LeetCode18.四数之和 JavaScript
  • php面试题 汇集2
  • PyCharm搭建GO开发环境(GO语言学习第1课)
  • Python3爬取英雄联盟英雄皮肤大图
  • SegmentFault 2015 Top Rank
  • Windows Containers 大冒险: 容器网络
  • 猴子数据域名防封接口降低小说被封的风险
  • 开放才能进步!Angular和Wijmo一起走过的日子
  • 聊聊sentinel的DegradeSlot
  • 前端技术周刊 2018-12-10:前端自动化测试
  • 如何使用 OAuth 2.0 将 LinkedIn 集成入 iOS 应用
  • 物联网链路协议
  • 一个JAVA程序员成长之路分享
  • 因为阿里,他们成了“杭漂”
  • 用mpvue开发微信小程序
  • 微龛半导体获数千万Pre-A轮融资,投资方为国中创投 ...
  • 我们雇佣了一只大猴子...
  • !!【OpenCV学习】计算两幅图像的重叠区域
  • # 深度解析 Socket 与 WebSocket:原理、区别与应用
  • (16)Reactor的测试——响应式Spring的道法术器
  • (4.10~4.16)
  • (delphi11最新学习资料) Object Pascal 学习笔记---第8章第2节(共同的基类)
  • (JS基础)String 类型
  • (NO.00004)iOS实现打砖块游戏(九):游戏中小球与反弹棒的碰撞
  • (十八)用JAVA编写MP3解码器——迷你播放器
  • (十六)串口UART
  • (算法)求1到1亿间的质数或素数
  • (万字长文)Spring的核心知识尽揽其中
  • (小白学Java)Java简介和基本配置
  • (转)Groupon前传:从10个月的失败作品修改,1个月找到成功
  • (转载)(官方)UE4--图像编程----着色器开发
  • .NET 8 编写 LiteDB vs SQLite 数据库 CRUD 接口性能测试(准备篇)
  • .NET CF命令行调试器MDbg入门(二) 设备模拟器
  • .net core 6 集成和使用 mongodb
  • .net程序集学习心得
  • .php结尾的域名,【php】php正则截取url中域名后的内容
  • /bin/bash^M: bad interpreter: No such file ordirectory