Spring5源码解析-@Autowired
你有没有思考过Spring中的@Autowired注解?通常用于方便依赖注入,而隐藏在这个过程之后的机制到底是怎样,将在本篇中进行讲述。
@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"> |
上面这种情况,假如只是一个简单的@Autowired
,Spring
根本不知道你要注入哪个bean
。这就是为什么我们要使用@Qualifier(value =“beanName”)
这个注解。在我们的例子中,要从 com.migo.Comment
这个类型的bean中区分comment1
,comment2
,我们可以写下面的代码:
1 | "comment1") (value= |
在Spring中如何使用@Autowired
正如前面部分所看到的,我们知道了在Spring中实现@Autowired
的不同方法。在这一部分中,我们将使用XML
配置的方式激活@Autowired
注解来自动注入。然后,我们将编写一个简单的类并配置一些bean。最后,我们将分别在另外两个类中使用它们:由@Controller注解的控件和不由Spring所管理的类。(为什么用XML
配置来做例子,我觉得这样更直观,其实XML和使用注解没多少区别,都是往容器里添加一些bean和组织下彼此之间的依赖而已,不必要非要拘泥于一种形式,哪种顺手用哪种,不过Springboot
自定义的这些还是推荐使用注解了)
我们从启动注解的自动注入开始:
1 | <context:annotation-config /> |
你必须将上面这个放在应用程序上下文配置中。它可以使在遇到@Autowired注解
时启用依赖注入。
现在,我们来编写和配置我们的bean:
1 | // beans first |
XML配置(在前面部分已经看到过):
1 | <bean name="comment1" class="com.specimen.exchanger.Comment"> |
现在,我们打开http://localhost:8080/test
来运行TestController
。如预期的那样,TestController
的注解字段正确地自动注入,而TestNoSpring
的注解字段并没有注入进去:
1 | 1st comment text: Content of the 1st comment |
哪里不对 ?TestNoSpring类不由Spring所管理。这就是为什么Spring不能注入Comment实例的依赖。我们将在下一部分中解释这个概念。
@Autowired注解背后的工作原理?
在讨论代码细节之前,我们再来了解下基础知识。Spring管理可用于整个应用程序的Java对象bean。他们所在的Spring容器,被称为应用程序上下文。这意味着我们不需要处理他们的生命周期(初始化,销毁)。该任务由此容器来完成。另外,该上下文具有入口点,在Web应用程序中,是dispatcher servlet。容器(也就是该上下文)会在它那里被启动并且所有的bean都会被注入。
说的再清楚点,请看<context:annotation-config />
的定义:
1 | <xsd:element name="annotation-config"> |
可以看出 : 类内部的注解,如:@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 |
之后,有几种方法来对@Autowired注解
进行处理。
第一个,private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz)
解析等待自动注入类的所有属性。它通过分析所有字段和方法并初始化org.springframework.beans.factory.annotation.InjectionMetadata类的实例来实现。
1 | private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) { |
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 { |
AutowiredAnnotationBeanPostProcessor
类中的另一个重要方法是private AnnotationAttributes findAutowiredAnnotation(AccessibleObject ao)。它通过分析属于一个字段或一个方法的所有注解来查找@Autowired
注解。如果未找到@Autowired
注解,则返回null
,字段或方法也就视为不可注入。
1 |
|
在上面的文章中,我们看到了Spring中自动注入过程。通过整篇文章可以看到,这种依赖注入是一种便捷易操作方式(可以在字段以及方法上完成),也促使我们逐渐在抛弃XML配置文件。还增强了代码的可读性。