Spring中@Autowired 和 @Resource 的区别

@Autowired 和 @Resource 的区别

  • 区别一:所属不同。
    • @Autowired 是 spring-beans 模块提供的注解。
    • @Resource 是 JSR 250 规范提出的注解,由 JDK 自带
  • 区别二:装配方式不同。两者都可以标注在属性或 setter 方法上。
    • @Autowired 注解只能按照类型装配依赖,如果需要按照名称装配还需要指定 @Qualifier 注解。
    • @Resource 默认依赖 bean 的名称为属性名,并且可以通过其属性 name 进行指定。默认依赖的 bean 的类型为属性的类型,并且可以通过 type 属性进行指定。 如果未指定依赖的 bean 的名称并且属性名对应的 bean 在容器中不存在时才会按照类型进行注入,否则按照名称进行注入依赖。(先按name再按type)
  • 区别三:是否强依赖不同。
    • @Autowired 指定的依赖默认必须存在,可以通过 requied 属性指定不存在
    • @Resource 指定的依赖必须存在。

@Autowired 注入分析

Spring 使用 AutowiredAnnotationBeanPostProcessor 对 @Autowired 进行处理。具体来说注入属性的方法位于 AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject注入方法参数的方法位于 AutowiredAnnotationBeanPostProcessor.AutowiredMethodElement#inject,它们的处理方式基本类似。以注入属性的流程为例进行分析,查看属性注入相关代码如下:

private class AutowiredMethodElement extends InjectionMetadata.InjectedElement {
	
    public AutowiredFieldElement(Field field, boolean required) {
    	super(field, null);
    	this.required = required;
    }
    
    // 注入属性
    @Override
    protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    	Field field = (Field) this.member;
    	Object value;
    	if (this.cached) {
    		value = resolvedCachedArgument(beanName, this.cachedFieldValue);
    	} else {
    		// 将属性转换为依赖描述符
    		DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
    		desc.setContainingClass(bean.getClass());
    		Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
    		Assert.state(beanFactory != null, "No BeanFactory available");
    		TypeConverter typeConverter = beanFactory.getTypeConverter();
    		try {
    			// 解析依赖
    			value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
    		} catch (BeansException ex) {
    			throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
    		}
    		... 省略部分代码
    	}
    	if (value != null) {
    		ReflectionUtils.makeAccessible(field);
    		field.set(bean, value);
    	}
    }
}

可以看到,Spring 对 @Autowired 标注的属性的注入只是将属性转换为依赖描述符 DependencyDescriptor ,然后调用 BeanFactory 解析依赖的方法。转换为依赖描述符时需要指定是否依赖是必须的,这个值在实例化 AutowiredFieldElement 时指定。AutowiredAnnotationBeanPostProcessor 实例化 AutowiredFieldElement 的相关代码如下:

// 注解属性名
private String requiredParameterName = "required";
// 依赖必须存在的属性值
private boolean requiredParameterValue = true;

// 构建注入元数据
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
    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) {
    			if (Modifier.isStatic(field.getModifiers())) {
    				... 省略日志打印
    				return;
    			}
    			// 循环属性,将 @Autowired 标注的属性转换为 AutowiredFieldElement
    			boolean required = determineRequiredStatus(ann);
    			currElements.add(new AutowiredFieldElement(field, required));
    		}
    	});

    	... 省略方法参数上 @Autowired 注解的处理

    	elements.addAll(0, currElements);
    	targetClass = targetClass.getSuperclass();
    }
    while (targetClass != null && targetClass != Object.class);

    return InjectionMetadata.forElements(elements, clazz);
}

// 判断依赖是否必须存在
protected boolean determineRequiredStatus(MergedAnnotation<?> ann) {
	// The following (AnnotationAttributes) cast is required on JDK 9+.
    return determineRequiredStatus((AnnotationAttributes)
			ann.asMap(mergedAnnotation -> new AnnotationAttributes(mergedAnnotation.getType())));
}

// 判断依赖是否必须存在
@Deprecated
protected boolean determineRequiredStatus(AnnotationAttributes ann) {
    return (!ann.containsKey(this.requiredParameterName) ||
			this.requiredParameterValue == ann.getBoolean(this.requiredParameterName));
}

Spring 构建注入元数据时通过反射获取属性,然后将属性转换为 AutowiredFieldElement ,而 required 的值的获取则会读取注解中的 required 属性,如果不存在或值为 true 则依赖必须存在。

至此,可以看出 @Autowired 的处理只是把属性或方法参数转换为依赖描述符,然后调用 BeanFactory 依赖注入的方法,至于依赖是否必须存在则可以由 @Autowired 的 required 属性值进行指定。

AutowireCapableBeanFactory#resolveDependency 方法完成真正的依赖注入,其会根据依赖的类型注入 0 到多个 bean 。例如如果依赖是一个 Map,则 Spring 会把 Map 值类型对应的 bean 全部存在到 map 中,而如果依赖的是普通的类型而 Spring 中存在多个相同类型的 bean,Spring 又无法确定使用哪一个则需要使用 @Primary 指定主要的 bean 或使用 @Qualifier 指定依赖的 bean 的名称。

@Resource 注入分析

除了对 Spring 自带注解的支持,Spring 对 JSR 250 等各种规范也进行了支持,对 @Resource 的处理 Spring 使用 CommonAnnotationBeanPostProcessor 进行。注入 @Resource 标注的属性或方法的相关代码如下:

public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBeanPostProcessor
		implements InstantiationAwareBeanPostProcessor, BeanFactoryAware, Serializable {
	protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName)
			throws NoSuchBeanDefinitionException {

		Object resource;
		Set<String> autowiredBeanNames;
		String name = element.name;

		if (factory instanceof AutowireCapableBeanFactory) {
			AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory;
			DependencyDescriptor descriptor = element.getDependencyDescriptor();
			// fallbackToDefaultTypeMatch 默认为 true, isDefaultName 表示是否使用默认的属性名称,name 表示属性名称
			if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {
				// 优先按照 bean 名称进行依赖注入,不存在则按照类型进行注入
				autowiredBeanNames = new LinkedHashSet<>();
				resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);
				if (resource == null) {
					throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
				}
			} else {
				resource = beanFactory.resolveBeanByName(name, descriptor);
				autowiredBeanNames = Collections.singleton(name);
			}
		} else {
			resource = factory.getBean(name, element.lookupType);
			autowiredBeanNames = Collections.singleton(name);
		}

		if (factory instanceof ConfigurableBeanFactory) {
			ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory) factory;
			for (String autowiredBeanName : autowiredBeanNames) {
				if (requestingBeanName != null && beanFactory.containsBean(autowiredBeanName)) {
					beanFactory.registerDependentBean(autowiredBeanName, requestingBeanName);
				}
			}
		}

		return resource;
	}

}

可以看到,Spring 会对根据 LookupElement 进行判断,如果使用默认的属性名,并且属性名在 Spring 中不存在对应的 bean,则会委托给 AutowireCapableBeanFactory#resolveDependency 进行处理,此时和 @Autowired 注解保持一致。如果 @Resource 指定了 bean 名称或者属性名对应的 bean 在容器中存在,Spring 会按照名称进行依赖注入。而非绝对的未指定名称则按照类型注入,这点需要注意。

 

参考:

 

posted @ 2022-01-11 12:48  残城碎梦  阅读(493)  评论(0编辑  收藏  举报