Spring注解装配:@Autowired和@Resource使用及原理详解

1.背景

@Resource@Autowired都是实现bean的注入,在日常开发中使用非常频繁,但是使用体验不太一样,笔者喜欢用@Resource,因为在使用@Autowired时IDEA会出现一些警告爆红提示:

Field injection is not recommended (字段注入是不被推荐的)

Spring团队不推荐属性字段注入的方式(ps:日常开发中我们一般都是字段注入,简单了然呀),建议使用基于构造函数的依赖项注入。

还有报红如下:IDEA提示找不到该类型的bean,这并不是错误。。。项目启动时mapper bean会被注入到Spring上下文容器中

综上情况和个人有强迫症看到警告和爆红就认为代码写得有问题,所以我选择了@Resource。但是这里要申明一下@Autowied本身是没问题的,可以尽情使用,出现以上报红是IDEA工具的问题。

项目推荐:基于SpringBoot2.x、SpringCloud和SpringCloudAlibaba企业级系统架构底层框架封装,解决业务开发时常见的非功能性需求,防止重复造轮子,方便业务快速开发和企业技术栈框架统一管理。引入组件化的思想实现高内聚低耦合并且高度可配置化,做到可插拔。严格控制包依赖和统一版本管理,做到最少化依赖。注重代码规范和注释,非常适合个人学习和企业使用

Github地址:https://github.com/plasticene/plasticene-boot-starter-parent

Gitee地址:https://gitee.com/plasticene3/plasticene-boot-starter-parent

2.概述

Spring 支持使用@Autowired, @Resource注解进行依赖注入

2.1 @Autowired和@Resource定义

@Autowired

@Autowired为Spring 框架提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired。源码如下

/**
 * @since 2.5
 * @see AutowiredAnnotationBeanPostProcessor
 * @see Qualifier
 * @see Value
 */
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {

	/**
	 * Declares whether the annotated dependency is required.
	 * <p>Defaults to {@code true}.
	 */
	boolean required() default true;

}

① 按照type在上下文中查找匹配的bean,查找type为Svc的bean

② 如果有多个bean,则按照name进行匹配

  • 如果有@Qualifier注解,则按照@Qualifier指定的name进行匹配,查找name为svcA的bean
  • 如果没有,则按照变量名进行匹配,查找name为svcA的bean

③ 匹配不到,则报错。(@Autowired(required=false),如果设置requiredfalse(默认为true),则注入失败时不会抛出异常

@Resource

@Resource 是JDK1.6支持的注解,由J2EE提供,需要导入包javax.annotation.Resource。源码如下:

@Target({TYPE, FIELD, METHOD})
@Retention(RUNTIME)
public @interface Resource {
  
    String name() default "";
    String lookup() default "";
    Class<?> type() default java.lang.Object.class;
    enum AuthenticationType {
            CONTAINER,
            APPLICATION
    }
    AuthenticationType authenticationType() default AuthenticationType.CONTAINER;
    boolean shareable() default true;
    String mappedName() default "";
    String description() default "";
}

默认按照名称进行装配,名称可以通过name属性进行指定。也提供按照byType 注入。

@Resource有两个重要的属性:name 和 type,而Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型

如果没有指定name属性,当注解写在字段上时,默认取字段名,按照名称查找。

当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找依赖对象。

当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。

@Resource的作用相当于@Autowired,只不过@Autowired按照byType自动注入

2.2 @Autowired和@Resource区别

依赖识别方式:@Autowired默认是byType可以使用@Qualifier指定Name,@Resource默认ByName如果找不到则ByType

适用对象:@Autowired可以对构造器、方法、参数、字段使用,@Resource只能对方法、字段使用

提供方:@Autowired是Spring提供的,@Resource是JSR-250提供的

3.实现原理

3.1 @Autowired实现原理

Spring中通过AutowiredAnnotationBeanPostProcessor来解析注入注解为目标注入值。该class继承InstantiationAwareBeanPostProcessorAdapter,实现了MergedBeanDefinitionPostProcessor,PriorityOrdered, BeanFactoryAware等接口,重写的方法将在IOC创建bean的时候被调用。

public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter
		implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware{
    /**
     * Create a new {@code AutowiredAnnotationBeanPostProcessor} for Spring's
     * standard {@link Autowired @Autowired} and {@link Value @Value} annotations.
     * <p>Also supports JSR-330's {@link javax.inject.Inject @Inject} annotation,
     * if available.
     */
    @SuppressWarnings("unchecked")
    public AutowiredAnnotationBeanPostProcessor() {
      this.autowiredAnnotationTypes.add(Autowired.class);
      this.autowiredAnnotationTypes.add(Value.class);
      try {
        this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
            ClassUtils.forName("javax.inject.Inject",        AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
        logger.trace("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
      }
      catch (ClassNotFoundException ex) {
        // JSR-330 API not available - simply skip.
      }
    }
  
  ......
  
}

前面已经提到了是Spring创建bean的时候调用,即当使用DefaultListableBeanFactory来获取想要的bean的时候会调用AutowiredAnnotationBeanPostProcessor,经过调用AbstractBeanFactory中的getBean方法,继续追踪源码最后在AbstractAutowireCapableBeanFactory类中的createBean方法中找到了调用解析@autowired的方法:doCreateBean()

	protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {

    ......

		// Allow post-processors to modify the merged bean definition.
		// 判断是否有后置处理
		// 如果有后置处理,则允许后置处理修改 BeanDefinition
		synchronized (mbd.postProcessingLock) {
			if (!mbd.postProcessed) {
				try {
          // 重点  重点  重点
          // 这里会调用AutowiredAnnotationBeanPostProcessor查找解析注入的元信息
					applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); 
				}
				catch (Throwable ex) {
					throw new BeanCreationException(mbd.getResourceDescription(), beanName,
							"Post-processing of merged bean definition failed", ex);
				}
				mbd.postProcessed = true;
			}
		}

		......

		// Initialize the bean instance.
		// 开始初始化 bean 实例对象
		Object exposedObject = bean;
		try {
			// 对 bean 进行填充,将各个属性值注入,其中,可能存在依赖于其他 bean 的属性
			// 则会递归初始依赖 bean
			populateBean(beanName, mbd, instanceWrapper);
			// 调用初始化方法
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		catch (Throwable ex) {
			if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
				throw (BeanCreationException) ex;
			}
			else {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
			}
		}

	  .......
      
		return exposedObject;
	}

执行到applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName)时会调用AutowiredAnnotationBeanPostProcessorpostProcessMergedBeanDefinition()完成注入元信息的查找解析。

进入populateBean()方法:会调用AutowiredAnnotationBeanPostProcessor方法postProcessPropertyValues()方法完成属性值注入,因为AutowiredAnnotationBeanPostProcessor是继承了InstantiationAwareBeanPostProcessorAdapter,是一个后置处理器。

接下来基于这两个核心入口分别讲述一下:

查找解析元信息

实现了接口类MergedBeanDefinitionPostProcessor的方法postProcessMergedBeanDefinition,这个方法是合并我们定义类的信息,比如:一个类继承了其它类,这个方法会把父类属性和信息合并到子类中

@Override
	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
    // 根据bean的名称、类型查找注入的元信息
		InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
		metadata.checkConfigMembers(beanDefinition);
	

findAutowiringMetadata()代码如下:

 private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
		// Fall back to class name as cache key, for backwards compatibility with custom callers.
    //先读缓存
		String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
		// Quick check on the concurrent map first, with minimal locking.
		InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
		if (InjectionMetadata.needsRefresh(metadata, clazz)) {
			synchronized (this.injectionMetadataCache) {
				metadata = this.injectionMetadataCache.get(cacheKey);
				if (InjectionMetadata.needsRefresh(metadata, clazz)) {
					if (metadata != null) {
						metadata.clear(pvs);
					}
          //把查找出来的元信息进行构建
					metadata = buildAutowiringMetadata(clazz);
					this.injectionMetadataCache.put(cacheKey, metadata);
				}
			}
		}
		return metadata;
	}

构建注解元信息 buildAutowiringMetadata()

 
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
    //判断是否符合条件注解类型(@AutoWired和@Value)
		if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
			return InjectionMetadata.EMPTY;
		}
 
		List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
		Class<?> targetClass = clazz;
 
		do {
			final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
      //先处理注解字段
			ReflectionUtils.doWithLocalFields(targetClass, field -> {
        //是不是要找的字段
				MergedAnnotation<?> ann = findAutowiredAnnotation(field);
				if (ann != null) {
          //静态字段不支持@Autowired
					if (Modifier.isStatic(field.getModifiers())) {
						if (logger.isInfoEnabled()) {
							logger.info("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;
				}
				MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
				if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
          //@Autowired不支持静态方法
					if (Modifier.isStatic(method.getModifiers())) {
						if (logger.isInfoEnabled()) {
							logger.info("Autowired annotation is not supported on static methods: " + method);
						}
						return;
					}
					if (method.getParameterCount() == 0) {
						if (logger.isInfoEnabled()) {
							logger.info("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);
 
		return InjectionMetadata.forElements(elements, clazz);
	}

基于上面方法找到了所有需要注入的元信息并进行解析,这个过程也是一个依赖查找过程。

属性值注入

属性注入的通过AutowiredAnnotationBeanPostProcessor方法postProcessPropertyValues()方法完成的


	@Override
	public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
        //元信息查找、解析,在上一步已经分析过了
		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;
	}
 
 
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();
				}
			}
		}

自此基于@Autowired依赖注入核心逻辑就实现了。

3.2 @Resource实现原理

@Resource@Autowired的实现逻辑和流程基本是一样的,只是@Resource是通过CommonAnnotationBeanPostProcessor实现的,这里由于具体逻辑和实现细节和上面的@Autowired差不多,这里就不再赘述。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值