Spring中@Autowired注解的原理详解

 更新时间:2023年11月03日 10:56:02   作者:飘飘~  
这篇文章主要介绍了Spring中@Autowired注解的原理详解,对于spring配置一个bean时,如果需要给该bean提供一些初始化参数,则需要通过依赖注入方式,所谓的依赖注入就是通过spring将bean所需要的一些参数传递到bean实例对象的过程,需要的朋友可以参考下

前言

在分享之前,我们先来回顾一下说明是依赖注入,依赖注入中手动注入和自动装配的几种方式,以便于提高大家对本篇文章的理解。 

 一、依赖注入的方式

对于spring配置一个bean时,如果需要给该bean提供一些初始化参数,则需要通过依赖注入方式

所谓的依赖注入就是通过spring将bean所需要的一些参数传递到bean实例对象的过程(将依赖关系注入到对象中) 

手动注入   

使用属性的setter方法注入 ,这是最常用的方式:

要求: 属性注入要求Bean提供一个默认的构造函数,并为需要注入的属性提供对应的Setter方法。Spring先调用Bean的默认构造函数实例化Bean对象,然后通过反射的方式调用Setter方法注入属性值。

  ② 构造器注入

使用方式:

第一,在类中,不用为属性设置setter方法(但是可以有),但是需要生成该类带参的构造方法。

第二,在配置文件中配置该类的bean,并配置构造器,在配置构造器中用到了<constructor-arg>节点,该节点有四个属性:

  •  index是索引,指定注入的属性位置,从0开始;
  •  type是指该属性所对应的类型;
  •  ref 是指引用的依赖对象;
  •  value 当注入的不是依赖对象,而是基本数据类型时,就用value;

自动装配 

XML方式进行自动装配 

大家可以看到用xml装配bean是一件很繁琐的事情,而且我们还要找到对应类型的bean才能装配。

创建应用对象之间协作关系的行为称为装配。也就是说当一个对象的属性是另一个对象时,实例化时,需要为这个对象属性进行实例化,这就是装配。

如果一个对象只通过接口来表明依赖关系,那么这种依赖就能够在对象本身毫不知情的情况下,用不同的具体实现进行切换。但是这样会存在一个问题,在传统的依赖注入配置中,我们必须要明确要给属性装配哪一个bean的引用,一旦bean很多,就不好维护了。基于这样的场景,spring使用注解来进行自动装配,解决这个问题。自动装配就是开发人员不必知道具体要装配哪个bean的引用,这个识别的工作会由spring来完成。

在sping框架中共有5种自动装配 :

  1. no:默认的方式是不进行自动装配的,通过手工设置ref属性来进行装配bean。
  2. byName:通过bean的名称进行自动装配,如果一个bean的 property 与另一bean 的name 相同,就进行自动装配。
  3. byType:通过参数的数据类型进行自动装配。
  4. constructor:利用构造函数进行装配,并且构造函数的参数通过byType进行装配。
  5. autodetect:自动探测,如果有构造方法,通过 construct的方式自动装配,否则使用 byType的方式自动装配。

上面所有方式的applicationContext.xml配置文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
 
    <!-- 使用注解前的配置 -->
    <context:annotation-config></context:annotation-config>
 
    <!-- 扫描 -->
    <context:component-scan base-package="com.ape.pojo"></context:component-scan>
 
    <!-- 手动注入 -->
    <!-- set注入 -->
    <bean id="Student" class="com.ape.pojo.Student">
        <property name="sid" value="1"></property>
        <property name="sname" value="张三"></property>
        <property name="smoney" value="100.00"></property>
        <property name="cat" ref="cat"></property>
    </bean>
 
    <!-- 构造器注入construct -->
    <bean id="Student" class="com.ape.pojo.Student">
        <constructor-arg name="sid" value="1"></constructor-arg>
        <constructor-arg name="sname" value="张三"></constructor-arg>
        <constructor-arg name="smoney" value="100.00"></constructor-arg>
        <constructor-arg name="cat" ref="cat"></constructor-arg>
    </bean>
 
    <bean id="Student" class="com.ape.pojo.Student">
        <constructor-arg type="int" value="1"></constructor-arg>
        <constructor-arg type="java.lang.String" value="张三"></constructor-arg>
        <constructor-arg type="double" value="100.00"></constructor-arg>
        <constructor-arg type="com.ape.pojo.Cat" ref="cat"></constructor-arg>
    </bean>
 
    <bean id="Student" class="com.ape.pojo.Student">
        <constructor-arg index="0" value="1"></constructor-arg>
        <constructor-arg index="1" value="张三"></constructor-arg>
        <constructor-arg index="2" value="100.00"></constructor-arg>
        <constructor-arg index="3" ref="cat"></constructor-arg>
    </bean>
 
    <!-- 自动装配 -->
    <!-- xml -->
    <bean id="Student" class="com.ape.pojo.Student" autowire="no">
        <property name="sid" value="1"></property>
        <property name="sname" value="张三"></property>
        <property name="smoney" value="100.00"></property>
        <property name="cat" ref="cat"></property>
    </bean>
 
    <bean id="Student" class="com.ape.pojo.Student" autowire="byName">
    </bean>
 
    <bean id="Student" class="com.ape.pojo.Student" autowire="byType">
    </bean>
 
    <bean id="Student" class="com.ape.pojo.Student" autowire="constructor">
    </bean>
 
    <bean id="Student" class="com.ape.pojo.Student" autowire="default">
    </bean>
 
 
 
</beans>

二、注解@Autowired的自动装配原理 

@Autowired自动装配过程 

 使用@Autowired注解来自动装配指定的bean。在使用@Autowired注解之前需要在Spring配置文件进行配置<context:annotation-config />。

在启动spring IoC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IOC容器自动查找需要的bean,并装配给该对象的属性。

在使用@Autowired时,首先在容器中查询对应类型的bean: 如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据; 如果查询的结果不止一个,那么@Autowired会根据名称来查找;

如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。

实现原理

 ①首先看看spring的源代码定义 

阅读代码我们可以看到,Autowired注解可以应用在构造方法,普通方法,参数,字段,以及注解这五种类型的地方,它的保留策略是在运行时。在Spring源代码当中,Autowired注解位于包org.springframework.beans.factory.annotation之中,如上图。

 ②核心逻辑在buildAutowiringMetadata中

通过反射获取该类的每一个字段和方法,并且分别用 findAutowiredAnnotation方法遍历每一个字段和方法,如果有@Autowired注解修饰,则返回注解相关属性。最后这个方法返回的就是包含所有带有autowire注解修饰的一个InjectionMetadata集合。

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();
                ReflectionUtils.doWithLocalFields(targetClass, (field) -> {
                    MergedAnnotation<?> ann = this.findAutowiredAnnotation(field);
                    if (ann != null) {
                        if (Modifier.isStatic(field.getModifiers())) {
                            if (this.logger.isInfoEnabled()) {
                                this.logger.info("Autowired annotation is not supported on static fields: " + field);
                            }
 
                            return;
                        }
 
                        boolean required = this.determineRequiredStatus(ann);
                        currElements.add(new AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement(field, required));
                    }
 
                });
                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));
                        }
 
                    }
                });
                elements.addAll(0, currElements);
                targetClass = targetClass.getSuperclass();
            } while(targetClass != null && targetClass != Object.class);
 
            return InjectionMetadata.forElements(elements, clazz);
        }
    }

 ③InjectionMetadata类

这个类由两部分组成: targetClass目标类和我们要获得的injectedElements集合。

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

 ④ 实现注入逻辑

 调用InjectionMetadata中的公共inject方法遍历调用protect的inject方法

public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
        InjectionMetadata metadata = this.findAutowiringMetadata(beanName, bean.getClass(), pvs);
 
        try {
            metadata.inject(bean, beanName, pvs);
            return pvs;
        } catch (BeanCreationException var6) {
            throw var6;
        } catch (Throwable var7) {
            throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", var7);
        }
    }

 ⑤调用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);
            }
        }
 
    }

  ⑥遍历调用protect的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);
                    method.invoke(target, this.getResourceToInject(target, requestingBeanName));
                } catch (InvocationTargetException var5) {
                    throw var5.getTargetException();
                }
            }
 
        }

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

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

相关文章

  • Java中的@interface注解使用详解

    Java中的@interface注解使用详解

    这篇文章主要介绍了Java中的@interface注解使用详解,注解@interface不是接口是注解类,在jdk1.5之后加入的功能,使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,需要的朋友可以参考下
    2023-12-12
  • Java输入输出语句举例详解(通俗易懂!)

    Java输入输出语句举例详解(通俗易懂!)

    这篇文章主要给大家介绍了关于Java输入输出语句的相关资料,作为一种常用的编程语言,Java提供了多种输入输出的方式,用于与用户进行数据交互或处理文件数据,需要的朋友可以参考下
    2023-10-10
  • 详解Java实现设计模式之责任链模式

    详解Java实现设计模式之责任链模式

    责任链模式是一种行为设计模式,允许你将请求沿着处理链发送,然后处理者都可对其进行处理,完成后可以再将其传递给下一个处理者。下面将会举例说明什么是责任链模式,责任链模式该如何使用
    2021-06-06
  • 浅谈SpringBoot集成Quartz动态定时任务

    浅谈SpringBoot集成Quartz动态定时任务

    这篇文章主要介绍了SpringBoot集成Quartz动态定时任务,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04
  • Mybatis错误引起的程序启动卡死问题及解决

    Mybatis错误引起的程序启动卡死问题及解决

    这篇文章主要介绍了Mybatis错误引起的程序启动卡死问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-02-02
  • Netty实战源码解析NIO编程

    Netty实战源码解析NIO编程

    这篇文章主要为大家介绍了Netty实战源码解析NIO编程的核心组件及关系详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • java数据结构之java实现栈

    java数据结构之java实现栈

    这篇文章主要介绍了java数据结构实现栈,需要的朋友可以参考下
    2014-03-03
  • 详解Springboot中的异步、定时、邮件任务

    详解Springboot中的异步、定时、邮件任务

    这篇文章主要介绍了Springboot中的异步、定时、邮件任务,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2021-11-11
  • 初步认识JVM的体系结构

    初步认识JVM的体系结构

    大家都知道,Java中JVM的重要性,学习了JVM你对Java的运行机制、编译过程和如何对Java程序进行调优相信都会有一个很好的认知.在面试中JVM也是非常重要的一部分,比如JVM调优,JVM对象分配规则,内存模型、方法区,还有种要GC等,需要的朋友可以参考下
    2021-06-06
  • 详解Spring-Boot集成Spring session并存入redis

    详解Spring-Boot集成Spring session并存入redis

    这篇文章主要介绍了详解Spring-Boot集成Spring session并存入redis,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-05-05

最新评论