看完让你吊打面试官-@Autowired注解到底怎么实现的?
发布日期:2021-06-30 12:23:42 浏览次数:2 分类:技术文章

本文共 9152 字,大约阅读时间需要 30 分钟。

1 @Autowired 干嘛的?

用来执行依赖注入.每当一个Spring管理的bean发现有该注解时,会直接注入相应的另一个Spring管理的bean.

1.1 不同地放置有不同作用

  • 属性
    Spring将通过扫描自定义的package或通过在配置文件中直接查找bean
  • 方法
    使用@Autowired注解的每个方法都要用到依赖注入
    但要注意的是,签名中呈现的所有对象都必须是Spring所管理的bean
    如果你有一个方法,比如setTest(Article article, NoSpringArticle noSpringArt) ,其中只有一个参数 (Article article)是由Spring管理的,那么就将抛出一个org.springframework.beans.factory.BeanCreationException异常
    这是由于Spring容器里并没有指定的一个或多个参数所指向的bean,所以也就无法解析它们

1.2 bean的注入方式

  • 名称
    bean解析是通过bean名称
  • 类型
    解析基于bean的类型

1.3 @Qualifier 协作

如下相同类型的bean

假如只是一个简单的@Autowired,Spring根本不知道你要注入哪个bean。这就需要@Qualifier(value =“beanName”)协作.

譬如,要从 com.javaedge.Article类型的bean中区分article1,article2:

@Qualifier(value="article1")@Autowiredprivate Article firstArticle;@Qualifier(value="article2")@Autowiredprivate Article secondArticle;

2 优雅地使用@Autowired

启动自动注入

放在应用程序上下文配置。可以使在遇到@Autowired注解时启用依赖注入

  • bean
// beans firstpublic class Comment {
private String content; public void setContent(String content) {
this.content = content; } public String getContent() {
return this.content; }}// sample controller@Controllerpublic class TestController {
@Qualifier(value="comment1") @Autowired private Comment firstComment; @Qualifier(value="comment2") @Autowired private Comment secondComment; @RequestMapping(value = "/test", method = RequestMethod.GET) public String test() {
System.out.println("1st comment text: "+firstComment.getText()); System.out.println("2nd comment text: "+secondComment.getText()); return "test"; }}// no-Spring managed classpublic class TestNoSpring {
@Autowired private Comment comment; public void testComment(String content) {
if (comment == null) {
System.out.println("Comment's instance wasn't autowired because this class is not Spring-managed bean"); } else {
comment.setContent(content); System.out.println("Comment's content: "+comment.getContent()); } }}
  • 配置

打开http://localhost:8080/test来运行TestController

TestController的注解字段正确地自动注入,而TestNoSpring的注解字段并没有注入进去

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

哪里不对 ?TestNoSpring类不由Spring所管理

这就是为什么Spring不能注入Comment实例的依赖

3 隐藏在@Autowired注解背后的秘密

Spring管理可用于整个应用程序的Java对象bean,我们不需要处理他们的生命周期(初始化,销毁)。该任务由此容器来完成。

该上下文具有入口点,在Web应用程序中,是dispatcherservlet
容器(也就是该上下文)会在它那里被启动并且所有的bean都会被注入

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

tag for that purpose. See javadoc for org.springframework.context.annotation.AnnotationConfigApplicationContext for information on code-based alternatives to bootstrapping annotation-driven support. ]]>

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

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

当 Spring 容器启动时,AutowiredAnnotationBeanPostProcessor 将扫描 Spring 容器中所有 Bean

当发现 Bean 中拥有@Autowired 注解时就找到和其匹配(默认按类型匹配)的 Bean
并注入到对应的地方中去。

4 源码分析

通过org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor可以实现依赖自动注入

通过这个类来处理@Autowired @Value Spring
它也可以管理JSR-303的@Inject

  • AutowiredAnnotationBeanPostProcessor构造函数中定义要处理的注解

之后,有几种方法对@Autowired处理

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

private InjectionMetadata buildAutowiringMetadata(final Class
clazz) {
LinkedList
elements = new LinkedList<>(); Class
targetClass = clazz; do {
final LinkedList
currElements = new LinkedList<>(); //分析所有字段 ReflectionUtils.doWithLocalFields(targetClass, field -> {
//findAutowiredAnnotation(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; } 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)); } }); elements.addAll(0, currElements); targetClass = targetClass.getSuperclass(); } while (targetClass != null && targetClass != Object.class); //返回一个InjectionMetadata初始化的对象实例 return new InjectionMetadata(clazz, elements); }... /** * 'Native' processing method for direct calls with an arbitrary target instance, * resolving all of its fields and methods which are annotated with {@code @Autowired}. * @param bean the target instance to process * @throws BeanCreationException if autowiring failed */ public void processInjection(Object bean) throws BeanCreationException {
Class
clazz = bean.getClass(); InjectionMetadata metadata = findAutowiringMetadata(clazz.getName(), clazz, null); try {
metadata.inject(bean, null, null); } catch (BeanCreationException ex) {
throw ex; } catch (Throwable ex) {
throw new BeanCreationException( "Injection of autowired dependencies failed for class [" + clazz + "]", ex); } }

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()方法

public class InjectionMetadata {
...public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Collection
checkedElements = this.checkedElements; Collection
elementsToIterate = (checkedElements != null ? checkedElements : this.injectedElements); if (!elementsToIterate.isEmpty()) {
boolean debug = logger.isDebugEnabled(); for (InjectedElement element : elementsToIterate) {
if (debug) {
logger.debug("Processing injected element of bean '" + beanName + "': " + element); } //看下面静态内部类的方法 element.inject(target, beanName, pvs); } } } ... public static abstract class InjectedElement {
protected final Member member; protected final boolean isField; ... /** * 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(); } } } ... }}

findAutowiredAnnotation(AccessibleObject ao)

分析属于一个字段或一个方法的所有注解来查找@Autowired注解。如果未找到@Autowired注解,则返回null,字段或方法也就视为不可注入。

转载地址:https://javaedge.blog.csdn.net/article/details/105071416 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:全网最全JDK1~JDK15十一种JVM垃圾收集器的原理总结
下一篇:阿里三面:说说线程封闭与ThreadLocal的关系

发表评论

最新留言

不错!
[***.144.177.141]2024年04月13日 17时34分16秒