Spring中最常用的注解之一@Autowired详解

目录
  • 前言
  • 如何使用@Autowired注解?
  • 如何实现@Autowired 注解?
  • Spring中源码解析
  • 总结

前言

在使用Spring开发的时候,配置的方式主要有两种,一种是xml的方式,另外一种是 java config的方式。在使用的过程中java config,我们难免会与注解进行各种打交道,其中,我们使用最多的注解应该就是@Autowired注解了。这个注解的作用就是注入一个定义好的bean

那么,除了我们常用的属性注入方式之外,还有哪些方式可以使用这个注解呢?在代码层面是如何实现的?

如何使用@Autowired注解?

@Autowired注解应用于构造函数,如以下示例所示:

@Component
public class BeanConfig{
    @Autowired
    private BeanConfig beanConfig;

    @Autowired
    public BeanConfig(BeanConfig beanConfig) {
        this.beanConfig = beanConfig;
    }
}

直接应用于字段是我们使用最多的方式,但是从代码层面使用构造函数注入会更好。因为构造器注入的方式,能够保证注入的依赖不可变,并确保需要的依赖不为空。此外,构造器注入的依赖总是能够在返回客户端(组件)代码的时候保证完全初始化的状态。

此外,还有以下几种不太常用的方法,见下面的代码:

 @Autowired
 private List<BeanConfig> beanConfigList;

 @Autowired
 private Set<BeanConfig> beanConfigSet;

 @Autowired
 private Map<String, BeanConfig> beanConfigMap;

虽然我们经常使用这个注解,但是我们真的了解它的作用吗?

首先从它的作用域来看,其实这个注解是属于容器配置的Spring注解,其他属于容器配置注解:@Required@Primary@Qualifier等。

其次,我们可以直接看字面意思,autowire,这个词的意思就是自动装配的意思。

自动装配是什么意思?这个词的本意是指在一些行业中用机器代替人自动完成一些需要装配的任务。在Spring的世界里,自动组装是指使用我们需要这个beanclass自动组装Spring容器中的bean

所以这个注解作用的就是自动将Spring容器中的bean和我们需要这个bean一起使用的类组装起来。

接下来,让我们看看这个注解背后工作的原理。

如何实现@Autowired 注解?

Java注解实现的核心技术是反射。让我们通过一些例子和自己实现一个注解来了解它的工作原理。

我们拿到target之后就可以用反射给他实现一个逻辑,这种逻辑在这些方法本身的逻辑之外,这让我们想起proxy、aop等知识,我们相当于为这些方法做了一个逻辑增强。

其实注解的实现主要逻辑大概就是这个思路。总结一下一般步骤如下:

  • 使用反射机制获取类的Class对象。
  • 通过这个类对象,可以得到它的每一个方法方法,或者字段等。
  • MethodField等类提供了类似getAnnotation的方法来获取某个字段的所有注解。
  • 拿到注解后,我们可以判断该注解是否是我们要实现的注解,如果是,则实现注解逻辑。

下面我们来实现这个逻辑,代码如下:

public void postProcessProperties() throws Exception {
    Class<BeanConfig> beanConfigClass = BeanConfig.class;
	BeanConfig instance = beanConfigClass.newInstance();
	Field[] fields = beanConfigClass.getDeclaredFields();
	for (Field field : fields) {
    	// getAnnotation,判断是否有Autowired
    	Autowired autowired = field.getDeclaredAnnotation(Autowired.class);
    	if (autowired != null) {
        	String fileName = field.getName();
        	Class<?> declaringClass = field.getDeclaringClass();
        	Object bean = new Object();
        	field.setAccessible(true);
        	field.set(bean, instance);
    	}
	}
}

从上面的实现逻辑不难发现,借助Java反射,我们可以直接获取一个类中的所有方法,然后获取方法上的注解。当然,我们也可以获取字段上的注解。在反射的帮助下,我们几乎可以得到属于一个类的任何东西。这样,我们自己简单做了一个实现。

知道了上面的知识,我们就不难想到,上面的注解虽然简单,但是@Autowired和他最大的区别应该只是注解的实现逻辑,其他的如使用反射获取注解等步骤应该是相同的。

接下来我们看在Spring中,@Autowired是如何实现的呢?

Spring中源码解析

我们来看@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对这个注解的逻辑实现。

Spring源码中,@Autowired注解位于包中org.springframework.beans.factory.annotation。经过分析不难发现,Spring对自动装配注解的实现逻辑位于类: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修饰,返回autowired相关属性
        ReflectionUtils.doWithLocalFields(targetClass, field -> {
            AnnotationAttributes ann = findAutowiredAnnotation(field);
             // 检查静态方法上是否使用了自动装配注解
            if (ann != null) {
                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));
            }
        });
        //和上面的逻辑一样,但是方法是通过反射来处理
        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);
}

最后,此方法返回一个InjectionMetadata包含所有autowire注解的集合。

这个类由两部分组成:

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

一个是我们要处理的目标类,一个是elements上面方法得到的集合。

有了目标类和所有需要注入的元素,我们就可以实现自动装配的依赖注入逻辑。实现方法如下。

@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;
}

它调用的inject方法就是定义在InjectionMetadata

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);
   }
  }
 }

/**
 * Either this or {@link #getResourceToInject} needs to be overridden.
 */
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();
  }
 }
}

上面代码中,方法的参数getResourceToInject是要注入的名称,bean这个方法的作用是根据名称获取bean

以上就是@Autowire注解实现逻辑的完整解析。

下面是spring容器实现@Autowired自动注入的时序图。

总结

本文讲解了Spring中最常用的注解之一@Autowired, 平时我们可能都是使用属性注入的,但是后续建议大家慢慢改变习惯,使用构造器注入。同时也讲解了这个注解背后的实现原理,希望对大家有帮助。

到此这篇关于Spring中最常用的注解之一@Autowired的文章就介绍到这了,更多相关@Autowired注解使用内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Spring中@Autowired和@Qualifier注解的3个知识点小结

    目录 @Autowired和@Qualifier注解的3个知识点 1.@Autowired自动注入 2.如果想直接使用byName的注入方式 3.如果没有指定Spring创建的bean的名称 @Autowired @Qualifier @Resource的区别 1.@Autowired 2.@Qualifier 3.@Resource @Autowired和@Qualifier注解的3个知识点 1.@Autowired自动注入 默认是先以byType的方式,如果有多个类型相匹配,那么使用byNa

  • 基于Spring@Autowired注解与自动装配详谈

    1 配置文件的方法 我们编写spring 框架的代码时候.一直遵循是这样一个规则:所有在spring中注入的bean 都建议定义成私有的域变量.并且要配套写上 get 和 set方法. Boss 拥有 Office 和 Car 类型的两个属性: 清单 3. Boss.java package com.baobaotao; public class Boss { private Car car; private Office office; // 省略 get/setter @Override p

  • spring中使用@Autowired注解无法注入的情况及解决

    目录 spring @Autowired注解无法注入 问题简述 原因:(此处只说第二种) 解决方案 @Autowired注解注入失败,提示could not autowire spring @Autowired注解无法注入 问题简述 在使用spring框架的过程中,常会遇到这种两情况: 1.在扫描的包以外使用需要使用mapper 2.同目录下两个controller或者两个service,在使用@Autowired注解注入mapper或者service时,其中一个可以注入,另一个却为空. 原因:

  • Spring @Autowired注解超详细示例

    目录 前言 一.依赖注入的方式 手动注入 自动装配 二.注解@Autowired的自动装配原理 @Autowired自动装配过程 实现原理 ①首先看看spring的源代码定义 ②核心逻辑在buildAutowiringMetadata中 ③InjectionMetadata类 ④ 实现注入逻辑 ⑤调用InjectionMetadata中的公共inject ⑥遍历调用protect的inject方法 前言 说明:我们今天要分享的是依赖注入(DI)通过自动装配的注解方式@Autowird注解方法的作

  • Spring注解@Resource和@Autowired区别对比详解

    前言 @Resource和@Autowired都是做bean的注入时使用,其实@Resource并不是Spring的注解,它的包是javax.annotation.Resource,需要导入,但是Spring支持该注解的注入. 1.共同点 两者都可以写在字段和setter方法上.两者如果都写在字段上,那么就不需要再写setter方法. 2.不同点 (1)@Autowired @Autowired为Spring提供的注解,需要导入包org.springframework.beans.factory

  • Spring中@Autowired注解作用在方法上和属性上说明

    目录 @Autowired注解作用在方法上 @Autowired注解作用在属性上 @Autowired注解的使用和注入规则 1.使用在变量域上面 2.@Autowired注解使用在构造器上面 @Autowired注解作用在方法上 (1)该方法如果有参数,会使用autowired的方式在容器中查找是否有该参数 (2)会执行该方法 所以如果把@Autowired放在setter方法上,就等于给对象的属性赋值 @Autowired注解作用在属性上 即为在容器中创建对象时给该项属性注入值,效果同@Aut

  • Spring中Bean的三种实例化方式详解

    目录 一.环境准备 二.构造方法实例化 三.分析Spring的错误信息 四.静态工厂实例化 4.1 工厂方式创建bean 4.2 静态工厂实例化 五.实例工厂与FactoryBean 5.1 环境准备 5.2 实例工厂实例化 5.3 FactoryBean的使用 六.bean实例化小结 一.环境准备 准备开发环境 创建一个Maven项目 pom.xml添加依赖 resources下添加spring的配置文件applicationContext.xml 最终项目的结构如下: 二.构造方法实例化 在

  • Spring中事务用法示例及实现原理详解

    前言 Spring并不直接管理事务,而是提供了多种事务管理器,他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现. 关于事务,简单来说,就是为了保证数据完整性而存在的一种工具,其主要有四大特性:原子性,一致性,隔离性和持久性.对于Spring事务,其最终还是在数据库层面实现的,而Spring只是以一种比较优雅的方式对其进行封装支持.本文首先会通过一个简单的示例来讲解Spring事务是如何使用的,然后会讲解Spring是如何解析xml中的标签,并对事

  • 如何在Spring中使用编码方式动态配置Bean详解

    bean与spring容器的关系 Bean配置信息定义了Bean的实现及依赖关系,Spring容器根据各种形式的Bean配置信息在容器内部建立Bean定义注册表,然后根据注册表加载.实例化Bean,并建立Bean和Bean的依赖关系,最后将这些准备就绪的Bean放到Bean缓存池中,以供外层的应用程序进行调用. 本文将给大家详细介绍关于在Spring中使用编码方式动态配置Bean的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 1 DefaultListableBea

  • Spring中BeanFactory与FactoryBean接口的区别详解

    前言 Spring框架中的BeanFactory接口和FactoryBean接口因为名称相似,老是容易搞混淆,而且也是面试过程中经常会碰到的一个问题.所以本文就专门给大家整理出来. 一.BeanFactory接口 BeanFactory接口是Spring容器的核心接口,负责:实例化.定位.配置应用程序中的对象及建立这些对象间的依赖. Spring为我们提供了许多易用的BeanFactory实现,XmlBeanFactory就是常用的一个,该实现将以XML方式描述组成应用的对象及对象间的依赖关系.

  • Spring中Bean的作用域与生命周期详解

    目录 一.Bean的作用域 1.单实例Bean声明 2.多实例Bean声明 二.Bean的生命周期 1.bean的初始和销毁 2.bean的后置处理器 总结 一.Bean的作用域 首先我们来讲一下有关于bean的作用域, 一般情况下,我们书写在IOC容器中的配置信息,会在我们的IOC容器运行时被创建,这就导致我们通过IOC容器获取到bean对象的时候,往往都是获取到了单实例的Bean对象, 这样就意味着无论我们使用多少个getBean()方法,获取到的同一个JavaBean都是同一个对象,这就是

  • Spring中xml配置文件的基础使用方式详解

    目录 1. xml配置文件的读取 1.1 通过类路径读取配置文件 1.2 通过文件系统绝对路径读取配置文件 1.3使用BeanFactory接口读取配置文件 2.带参构造对象的创建(constructor-arg标签) 3.使用另一个类中的方法创建对象,并放到Spring容器中 4.调用另一个类中的静态方法创建对象,并放到Spring容器中 5.对象的生命周期 6.单例多例的测试 1. xml配置文件的读取 目录结构 applicationContext.xml配置文件 <?xml versio

  • Spring中实例化bean的四种方式详解

    前言 在介绍Bean的实例化的方式之前,我们首先需要介绍一下什么是Bean,以及Bean的配置方式. 如果把Spring看作一个大型工厂,那么Spring容器中的Bean就是该工厂的产品.要想使用Spring工厂生产和管理Bean,就需要在配置文件中指明需要哪些Bean,以及需要使用何种方式将这些Bean装配到一起. Spring容器支持两种格式的配置文件,分别为Properties文件格式和xml文件格式,而在实际的开发当中,最常使用的额是xml文件格式,因此在如下的讲解中,我们以xml文件格

  • spring中aop的xml配置方法实例详解

    前言 AOP:即面向切面编程,是一种编程思想,OOP的延续.在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等等. aop,面向切面编程的目标就是分离关注点,比如:一个骑士只需要关注守护安全,或者远征,而骑士辉煌一生的事迹由谁来记录和歌颂呢,当然不会是自己了,这个完全可以由诗人去歌颂,比如当骑士出征的时候诗人可以去欢送,当骑士英勇牺牲的时候,诗人可以写诗歌颂骑士的一生.那么骑士只需要关注怎么打仗就好了.而诗人也只需要关注写诗歌颂和欢送就好了,那么这样就把功能分离了.所以可以把诗

  • Spring 中 @Service 和 @Resource 注解的区别

    Spring 中 @Service 和 @Resource 注解的区别 1 前言 在咱们使用 spring 框架的时候,注解是"不可或缺"的一部分,她帮我们脱离了配置繁琐的 XML 文件的工作,但有一点却需要我们自己去把握,那就是"3何",即"何时何地用何注解?"在本篇博文中,作者就简单介绍一下如何恰当的使用 @Service 和 @Resource 这两个注解. 2 注解 2.1 @Service 当咱们需要定义某个类为一个 bean 的时候,

  • 详解Spring中@Valid和@Validated注解用法

    目录 案例引入 @Valid 详解 @Validated 详解 @Valid 和 @Validated 比较 案例引入 下面我们以新增一个员工为功能切入点,以常规写法为背景,慢慢烘托出 @Valid 和 @Validated 注解用法详解. 那么,首先,我们会有一个员工对象 Employee,如下 : /** * 员工对象 * * @author sunnyzyq * @since 2019/12/13 */ public class Employee { /** 姓名 */ public St

随机推荐