看完让你吊打面试官-@Autowired注解到底怎么实现的?

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

<bean name="article1" class="com.sss.Article">
    <property name="text" value="Content of the 1st Article" />
</bean>

<bean name="article2" class="com.sss.Article">
    <property name="text" value="Content of the 2nd Article" />
</bean>

假如只是一个简单的@Autowired,Spring根本不知道你要注入哪个bean。这就需要

@Qualifier(value = "beanName")

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

@Qualifier(value="article1")
@Autowired
private Article firstArticle;

@Qualifier(value="article2")
@Autowired
private Article secondArticle;

2 优雅地使用@Autowired

启动自动注入

<context:annotation-config />

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

  • bean
// beans first
public class Comment {

    private String content;

    public void setContent(String content) {
        this.content = content;
    }

    public String getContent() {
        return this.content;
    }

}

// sample controller
@Controller
public 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 class
public 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());
        }
    }

}

  • 配置
<bean name="comment1" class="com.sss.exchanger.Comment">
    <property name="content" value="Content of the 1st comment" />
</bean>

<bean name="comment2" class="com.sss.exchanger.Comment">
    <property name="content" value="Content of the 2nd comment" />
</bean>

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

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

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

3 @Autowired注解背后的秘密

应用程序上下文具有入口点,在Web应用程序中,是dispatcherservlet。容器(也就是该上下文)会在它那里被启动并且所有的bean都会被注入。
<context:annotation-config />的定义

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

类内部的注解,如@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<InjectionMetadata.InjectedElement> elements = new LinkedList<>();
		Class<?> targetClass = clazz;
		do {
			final LinkedList<InjectionMetadata.InjectedElement> 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<InjectedElement> checkedElements = this.checkedElements;
		Collection<InjectedElement> 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,字段或方法也就视为不可注入。

在Spring框架中,@Autowired和@Resource注解都用于实现依赖注入,但它们有几个区别。 1. 来源不同:@Autowired来自Spring框架,而@Resource来自Java的JSR-250规范。 2. 依赖查找的顺序不同:@Autowired先根据类型再根据名称查询,而@Resource先根据名称再根据类型查询。 3. 支持的参数不同:@Autowired只支持设置一个参数,而@Resource支持设置七个参数。 4. 依赖注入的用法支持不同:@Autowired既支持构造方法注入,又支持属性注入和Setter注入,而@Resource只支持属性注入和Setter注入。 5. 编译器的提示不同:当注入Mapper对象时,使用@Autowired注解编译器会提示错误,而使用@Resource注解则不会提示错误。 总结起来,@Autowired和@Resource注解在依赖注入的实现方式、使用参数和编译器提示等方面有所不同。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Spring框架中 @Autowired 和 @Resource 注解的区别](https://download.csdn.net/download/weixin_38524871/14888410)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [面试突击:@Autowired 和 @Resource 有什么区别?你学会了吗?](https://blog.csdn.net/Candyz7/article/details/126578224)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值