一、@Autowired所具有的功能

@Autowired是一个用来执行依赖注入的注解。每当一个Spring管理的bean发现有这个注解时候,它会直接注入相应的另一个Spring管理的bean。

该注解可以在不同的层次上应用:

  • 类字段:Spring将通过扫描自定义的packages(例如在我们所注解的controllers)或通过在配置文件中直接查找bean。
  • 方法:使用@Autowired注解的每个方法都要用到依赖注入。但要注意的是,方法签名中呈现的所有对象都必须是Spring所管理的bean。如果你有一个方法,比如setTest(Article article, NoSpringArticle noSpringArt) ,其中只有一个参数 (Article article)是由Spring管理的,那么就将抛出一个org.springframework.beans.factory.BeanCreationException异常。这是由于Spring容器里并没有指定的一个或多个参数所指向的bean,所以也就无法解析它们。
  1. org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'testController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void org.krams.tutorial.controller.TestController.ix(com.mysite.controller.IndexController,com.mysite.nospring.data.Article); nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.mysite.nospring.data.Article] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
  • 构造函数:@Autowired的工作方式和方法相同。

对象注入需要遵循一些规则。一个bean可以按照下面的方式注入:

  • 名称:bean解析是通过bean名称(看后面的例子)。
  • 类型:解析过程基于bean的类型。

在某些情况下,@Autowired应该通过@Qualifier注解协作注入。例如下面几个是相同类型的bean:

  1. <bean name="comment1" class="com.migo.Comment">
  2. <property name="text" value="Content of the 1st comment" />
  3. </bean>
  4. <bean name="comment2" class="com.migo.Comment">
  5. <property name="text" value="Content of the 2nd comment" />
  6. </bean>

上面这种情况,假如只是一个简单的@Autowired,Spring根本不知道你要注入哪个bean。这就是为什么我们要使用@Qualifier(value =“beanName”)这个注解。在我们的例子中,要从 com.migo.Comment这个类型的bean中区分comment1,comment2,我们可以写下面的代码:

  1. @Qualifier(value="comment1")
  2. @Autowired
  3. private Comment firstComment;
  4. @Qualifier(value="comment2")
  5. @Autowired
  6. private Comment secondComment;

二、在Spring中如何使用@Autowired

在这一部分中,我们将使用XML配置的方式激活@Autowired注解来自动注入。然后,我们将编写一个简单的类并配置一些bean。最后,我们将分别在另外两个类中使用它们:由@Controller注解的控件和不由Spring所管理的类。(为什么用XML配置来做例子,我觉得这样更直观,其实XML和使用注解没多少区别,都是往容器里添加一些bean和组织下彼此之间的依赖而已)

我们从启动注解的自动注入开始:

  1. <context:annotation-config />

你必须将上面这个放在应用程序上下文配置中。它可以使在遇到@Autowired注解时启用依赖注入。

现在,我们来编写和配置我们的bean:

  1. // beans first
  2. public class Comment {
  3. private String content;
  4. public void setContent(String content) {
  5. this.content = content;
  6. }
  7. public String getContent() {
  8. return this.content;
  9. }
  10. }
  11. // sample controller
  12. @Controller
  13. public class TestController {
  14. @Qualifier(value="comment1")
  15. @Autowired
  16. private Comment firstComment;
  17. @Qualifier(value="comment2")
  18. @Autowired
  19. private Comment secondComment;
  20. @RequestMapping(value = "/test", method = RequestMethod.GET)
  21. public String test() {
  22. System.out.println("1st comment text: "+firstComment.getText());
  23. System.out.println("2nd comment text: "+secondComment.getText());
  24. return "test";
  25. }
  26. }
  27. // no-Spring managed class
  28. public class TestNoSpring {
  29. @Autowired
  30. private Comment comment;
  31. public void testComment(String content) {
  32. if (comment == null) {
  33. System.out.println("Comment's instance wasn't autowired because this class is not Spring-managed bean");
  34. } else {
  35. comment.setContent(content);
  36. System.out.println("Comment's content: "+comment.getContent());
  37. }
  38. }
  39. }

XML配置:

  1. <bean name="comment1" class="com.specimen.exchanger.Comment">
  2. <property name="content" value="Content of the 1st comment" />
  3. </bean>
  4. <bean name="comment2" class="com.specimen.exchanger.Comment">
  5. <property name="content" value="Content of the 2nd comment" />
  6. </bean>

现在,我们打开http://localhost:8080/test来运行TestController。如预期的那样,TestController的注解字段正确地自动注入,而TestNoSpring的注解字段并没有注入进去:

  1. 1st comment text: Content of the 1st comment
  2. 2nd comment text: Content of the 2nd comment
  3. Comment's instance wasn't autowired because this class is not Spring-managed bean

TestNoSpring类不由Spring所管理。这就是为什么Spring不能注入Comment实例的依赖。

三、@Autowired注解背后的工作原理

Spring管理可用于整个应用程序的Java对象bean。他们所在的Spring容器,被称为应用程序上下文。这意味着我们不需要处理他们的生命周期(初始化,销毁)。该任务由此容器来完成。另外,该上下文具有入口点,在Web应用程序中,是dispatcher servlet。容器(也就是该上下文)会在它那里被启动并且所有的bean都会被注入。

<context:annotation-config />的定义:

  1. <xsd:element name="annotation-config">
  2. <xsd:annotation>
  3. <xsd:documentation><![CDATA[
  4. Activates various annotations to be detected in bean classes: Spring's @Required and
  5. @Autowired, as well as JSR 250's @PostConstruct, @PreDestroy and @Resource (if available),
  6. JAX-WS's @WebServiceRef (if available), EJB 3's @EJB (if available), and JPA's
  7. @PersistenceContext and @PersistenceUnit (if available). Alternatively, you may
  8. choose to activate the individual BeanPostProcessors for those annotations.
  9. Note: This tag does not activate processing of Spring's @Transactional or EJB 3's
  10. @TransactionAttribute annotation. Consider the use of the <tx:annotation-driven>
  11. tag for that purpose.
  12. See javadoc for org.springframework.context.annotation.AnnotationConfigApplicationContext
  13. for information on code-based alternatives to bootstrapping annotation-driven support.
  14. ]]></xsd:documentation>
  15. </xsd:annotation>
  16. </xsd:element>

可以看出 : 类内部的注解,如:@Autowired、@Value、@Required、@Resource以及EJB和WebSerivce相关的注解,是容器对Bean对象实例化和依赖注入时,通过容器中注册的Bean后置处理器处理这些注解的。

所以配置了上面这个配置(<context:component-scan>假如有配置这个,那么就可以省略<context:annotation-config />)后,将隐式地向Spring容器注册AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、RequiredAnnotationBeanPostProcessor、PersistenceAnnotationBeanPostProcessor以及这4个专门用于处理注解的Bean后置处理器。

当 Spring 容器启动时,AutowiredAnnotationBeanPostProcessor 将扫描 Spring 容器中所有 Bean,当发现 Bean 中拥有@Autowired 注解时就找到和其匹配(默认按类型匹配)的 Bean,并注入到对应的地方中去。 源码分析如下:

通过org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor可以实现依赖自动注入。通过这个类来处理@Autowired和@Value这俩Spring注解。它也可以管理JSR-303的@Inject注解(如果可用的话)。在AutowiredAnnotationBeanPostProcessor构造函数中定义要处理的注解:

  1. public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter
  2. implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {
  3. ...
  4. /**
  5. * Create a new AutowiredAnnotationBeanPostProcessor
  6. * for Spring's standard {@link Autowired} annotation.
  7. * <p>Also supports JSR-330's {@link javax.inject.Inject} annotation, if available.
  8. */
  9. @SuppressWarnings("unchecked")
  10. public AutowiredAnnotationBeanPostProcessor() {
  11. this.autowiredAnnotationTypes.add(Autowired.class);
  12. this.autowiredAnnotationTypes.add(Value.class);
  13. try {
  14. this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
  15. ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
  16. logger.info("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
  17. }
  18. catch (ClassNotFoundException ex) {
  19. // JSR-330 API not available - simply skip.
  20. }
  21. }
  22. ...
  23. }

之后,有几种方法来对@Autowired注解进行处理。

第一个,private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz)解析等待自动注入类的所有属性。它通过分析所有字段和方法并初始化org.springframework.beans.factory.annotation.InjectionMetadata类的实例来实现。

  1. private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
  2. LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<>();
  3. Class<?> targetClass = clazz;
  4. do {
  5. final LinkedList<InjectionMetadata.InjectedElement> currElements = new LinkedList<>();
  6. //分析所有字段
  7. ReflectionUtils.doWithLocalFields(targetClass, field -> {
  8. //findAutowiredAnnotation(field)此方法后面会解释
  9. AnnotationAttributes ann = findAutowiredAnnotation(field);
  10. if (ann != null) {
  11. if (Modifier.isStatic(field.getModifiers())) {
  12. if (logger.isWarnEnabled()) {
  13. logger.warn("Autowired annotation is not supported on static fields: " + field);
  14. }
  15. return;
  16. }
  17. boolean required = determineRequiredStatus(ann);
  18. currElements.add(new AutowiredFieldElement(field, required));
  19. }
  20. });
  21. //分析所有方法
  22. ReflectionUtils.doWithLocalMethods(targetClass, method -> {
  23. Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
  24. if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
  25. return;
  26. }
  27. AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
  28. if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
  29. if (Modifier.isStatic(method.getModifiers())) {
  30. if (logger.isWarnEnabled()) {
  31. logger.warn("Autowired annotation is not supported on static methods: " + method);
  32. }
  33. return;
  34. }
  35. if (method.getParameterCount() == 0) {
  36. if (logger.isWarnEnabled()) {
  37. logger.warn("Autowired annotation should only be used on methods with parameters: " +
  38. method);
  39. }
  40. }
  41. boolean required = determineRequiredStatus(ann);
  42. PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
  43. currElements.add(new AutowiredMethodElement(method, required, pd));
  44. }
  45. });
  46. elements.addAll(0, currElements);
  47. targetClass = targetClass.getSuperclass();
  48. }
  49. while (targetClass != null && targetClass != Object.class);
  50. //返回一个InjectionMetadata初始化的对象实例
  51. return new InjectionMetadata(clazz, elements);
  52. }
  53. ...
  54. /**
  55. * 'Native' processing method for direct calls with an arbitrary target instance,
  56. * resolving all of its fields and methods which are annotated with {@code @Autowired}.
  57. * @param bean the target instance to process
  58. * @throws BeanCreationException if autowiring failed
  59. */
  60. public void processInjection(Object bean) throws BeanCreationException {
  61. Class<?> clazz = bean.getClass();
  62. InjectionMetadata metadata = findAutowiringMetadata(clazz.getName(), clazz, null);
  63. try {
  64. metadata.inject(bean, null, null);
  65. }
  66. catch (BeanCreationException ex) {
  67. throw ex;
  68. }
  69. catch (Throwable ex) {
  70. throw new BeanCreationException(
  71. "Injection of autowired dependencies failed for class [" + clazz + "]", ex);
  72. }
  73. }

InjectionMetadata类包含要注入的元素的列表。注入是通过Java的API Reflection (Field set(Object obj, Object value) 或Method invoke(Object obj,Object … args)方法完成的。此过程直接在AutowiredAnnotationBeanPostProcessor的方法中调用public void processInjection(Object bean) throws BeanCreationException。它将所有可注入的bean检索为InjectionMetadata实例,并调用它们的inject()方法。

  1. public class InjectionMetadata {
  2. ...
  3. public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
  4. Collection<InjectedElement> checkedElements = this.checkedElements;
  5. Collection<InjectedElement> elementsToIterate =
  6. (checkedElements != null ? checkedElements : this.injectedElements);
  7. if (!elementsToIterate.isEmpty()) {
  8. boolean debug = logger.isDebugEnabled();
  9. for (InjectedElement element : elementsToIterate) {
  10. if (debug) {
  11. logger.debug("Processing injected element of bean '" + beanName + "': " + element);
  12. }
  13. //看下面静态内部类的方法
  14. element.inject(target, beanName, pvs);
  15. }
  16. }
  17. }
  18. ...
  19. public static abstract class InjectedElement {
  20. protected final Member member;
  21. protected final boolean isField;
  22. ...
  23. /**
  24. * Either this or {@link #getResourceToInject} needs to be overridden.
  25. */
  26. protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs)
  27. throws Throwable {
  28. if (this.isField) {
  29. Field field = (Field) this.member;
  30. ReflectionUtils.makeAccessible(field);
  31. field.set(target, getResourceToInject(target, requestingBeanName));
  32. }
  33. else {
  34. if (checkPropertySkipping(pvs)) {
  35. return;
  36. }
  37. try {
  38. //具体的注入看此处咯
  39. Method method = (Method) this.member;
  40. ReflectionUtils.makeAccessible(method);
  41. method.invoke(target, getResourceToInject(target, requestingBeanName));
  42. }
  43. catch (InvocationTargetException ex) {
  44. throw ex.getTargetException();
  45. }
  46. }
  47. }
  48. ...
  49. }
  50. }

AutowiredAnnotationBeanPostProcessor类中的另一个重要方法是private AnnotationAttributes findAutowiredAnnotation(AccessibleObject ao)。它通过分析属于一个字段或一个方法的所有注解来查找@Autowired注解。如果未找到@Autowired注解,则返回null,字段或方法也就视为不可注入。

  1. @Nullable
  2. private AnnotationAttributes findAutowiredAnnotation(AccessibleObject ao) {
  3. if (ao.getAnnotations().length > 0) {
  4. for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
  5. AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ao, type);
  6. if (attributes != null) {
  7. return attributes;
  8. }
  9. }
  10. }
  11. return null;
  12. }

转载:

芋道源码

Spring5源码,@Autowired的更多相关文章

  1. Spring5源码解析-Spring框架中的单例和原型bean

    Spring5源码解析-Spring框架中的单例和原型bean 最近一直有问我单例和原型bean的一些原理性问题,这里就开一篇来说说的 通过Spring中的依赖注入极大方便了我们的开发.在xml通过& ...

  2. spring5 源码深度解析----- 被面试官给虐懵了,竟然是因为我不懂@Configuration配置类及@Bean的原理

    @Configuration注解提供了全新的bean创建方式.最初spring通过xml配置文件初始化bean并完成依赖注入工作.从spring3.0开始,在spring framework模块中提供 ...

  3. 3.3 Spring5源码---循环依赖过程中spring读取不完整bean的最终解决方案

    根据之前解析的循环依赖的源码, 分析了一级缓存,二级缓存,三级缓存的作用以及如何解决循环依赖的. 然而在多线程的情况下, Spring在创建bean的过程中, 可能会读取到不完整的bean. 下面, ...

  4. Spring5源码解析-论Spring DispatcherServlet的生命周期

    Spring Web框架架构的主要部分是DispatcherServlet.也就是本文中重点介绍的对象. 在本文的第一部分中,我们将看到基于Spring的DispatcherServlet的主要概念: ...

  5. 学习Spring5源码时所遇到的坑

    学习Spring5源码时所遇到的坑 0)本人下载的源码版本是 spring-framework-5.0.2.RELEASE 配置好gradle环境变量之后,cmd进入到spring项目,执行gradl ...

  6. Spring5源码阅读环境搭建-gradle构建编译

      前沿:Spring系列生态十分丰富,涉及到各个方面.但是作为Spring生态的核心基础Spring,是最重要的环节,需要理解Spring的设计原理,我们需要解读源码.   在构建Spring源码阅 ...

  7. Spring5源码分析(1)设计思想与结构

    1 源码地址(带有中文注解)git@github.com:yakax/spring-framework-5.0.2.RELEASE--.git Spring 的设计初衷其实就是为了简化我们的开发 基于 ...

  8. 使用IDEA+Gradle构建Spring5源码并调试(手把手教程全图解)

    一.前言   说一说我要写这篇文章的初衷吧,前段时间有小伙伴在微信群求教怎样构建spring源码,他在网上找了n个教程跟着后面花了两天时间都没构建好,正好我最近因工作原因从mac换成windows,开 ...

  9. 3.4 spring5源码系列--循环依赖的设计思想

    前面已经写了关于三篇循环依赖的文章, 这是一个总结篇 第一篇: 3.1 spring5源码系列--循环依赖 之 手写代码模拟spring循环依赖 第二篇: 3.2spring源码系列----循环依赖源 ...

  10. 5.2 Spring5源码--Spring AOP源码分析二

    目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...

随机推荐

  1. JavaScript中对象是否需要加引号?

    对象的属性名是包括空字符串在内的所有字符串. 那么问题来了,我们平时定义的对象如下,是没有引号""or''的,这样不加引号有没有错呢? 答案是,加不加分情况!但加了肯定没问题... ...

  2. java线程与内核线程的关系,及怎么定义ThreadPoolExecutor相关参数

    p.p1 { margin: 0; font: 12px Menlo } p.p1 { margin: 0; font: 12px Menlo } p.p2 { margin: 0; font: 12 ...

  3. 敏捷史话(二):Scrum社区的悲剧性损失——Mike Beedle

    2018年3月23日,在美国的芝加哥发生了一起意外刺杀事件.一名男子刺杀了一位首席执行官,而这位不幸的首席执行官就是<敏捷宣言>的合著者--Mike Beedle.Mike 的这场意外令 ...

  4. kafka-spark偏移量提交至redis kafka1.0版本

    kafka版本 1.0.0 spark版本 spark-streaming-kafka-0-10_2.11/** * @created by imp ON 2019/12/21 */class Kaf ...

  5. Win Task 任务管理器 批量杀进程方法

    Example Kill All Chrome & Chrome Driver taskkill /IM chromedriver.exe /F taskkill /IM chrome.exe ...

  6. WebApi 中请求的 JSON 数据字段作为 POST 参数传入

    使用 POST 方式请求 JSON 数据到服务器 WebAPI 接口时需要将 JSON 格式封装成数据模型接收参数.即使参数较少,每个接口仍然需要单独创建模型接收.下面方法实现了将 JSON 参数中的 ...

  7. MySQL 集群知识点整理

    随着项目架构的不断扩大,单台 MySQL 已经不能满足需要了,所以需要搭建集群将前来的请求进行分流处理.博客主要根据丁奇老师的专栏<<MySQL实战45讲>>学习的总结. 架构 ...

  8. Error: Could not open input file: /usr/java/jdk1.7.0_07/jre/lib/jsse.pack

    [root@localhost ~]# rpm -ivh jdk-7u7-linux-i586.rpm Preparing... ################################### ...

  9. 【Oracle】表空间配额问题

    由于需求,需要新建用户,但是新建的用户,会有相关的配额跟着,莫名其妙的问题让人很头疼 下面介绍下如何修改成不限制配额 select * from user_ts_quotas ; alter user ...

  10. 【ORA】ORA-01078和LRM-00109 解决方法

    今天切换到asm实例的时候,发现是一个空实例,尝试启动实例,结果报错ORA-01078和LRM-00109 SQL> startupORA-01078: failure in processin ...