Spring Boot v2.4.4源码解析(八)依赖注入原理上 —— 由一道面试题引起的思考《@Autowired和@Resource的区别》?

@Autowired@Resource 属性注入分别是由 AutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessor 处理,本文将从源码出发,解析这两种注解对属性注入区别。

一、@Autowired

先看下 @Autowired 属性是如何注入的,AutowiredAnnotationBeanPostProcessor UML 类图如下
在这里插入图片描述

1. @Autowired / @Value / @Inject 注解发现

从图中可以看出,其实现 MergedBeanDefinitionPostProcessor 接口,在该接口 org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition 方法实现中使用反射在 bean 的 Class 文件中查找被 @Autowired / @Value / @Inject 注解的 Field / Method,并将查找结果封装为 AutowiredFieldElement / AutowiredMethodElement 并缓存到 injectionMetadataCache 属性中,为了讨论方便,后文只关注 AutowiredFieldElement ,即 @Autowired 注解到成员变量上情况,
调用堆栈如下,

buildAutowiringMetadata:473, AutowiredAnnotationBeanPostProcessor // 实际查找功能在这里完成
findAutowiringMetadata:454, AutowiredAnnotationBeanPostProcessor // 处理缓存
postProcessMergedBeanDefinition:246, AutowiredAnnotationBeanPostProcessor
// 将 MergedBeanDefinitionPostProcessors 应用于指定的 BeanDefinition,调用它们的 {@code postProcessMergedBeanDefinition} 方法
applyMergedBeanDefinitionPostProcessors:1116, AbstractAutowireCapableBeanFactory
doCreateBean:594, AbstractAutowireCapableBeanFactory
createBean:542, AbstractAutowireCapableBeanFactory
lambda$doGetBean$0:335, AbstractBeanFactory
getObject:-1, 1241938981
getSingleton:234, DefaultSingletonBeanRegistry
doGetBean:333, AbstractBeanFactory
getBean:208, AbstractBeanFactory
preInstantiateSingletons:944, DefaultListableBeanFactory
finishBeanFactoryInitialization:918, AbstractApplicationContext
refresh:583, AbstractApplicationContext
refresh:144, ServletWebServerApplicationContext
refresh:771, SpringApplication
refresh:763, SpringApplication
refreshContext:438, SpringApplication
run:339, SpringApplication
run:1329, SpringApplication
run:1318, SpringApplication
main:69, Demo1Application
α. 发现原理 —— 反射

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata 方法源码,

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<>();

		// 遍历Class每个Field
		ReflectionUtils.doWithLocalFields(targetClass, field -> {
			// 查找 @Autowired 等注解
			MergedAnnotation<?> ann = findAutowiredAnnotation(field);
			if (ann != null) {
				// @Autowired / @Value / @Inject 不支持静态属性
				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 -> {
			// 解决JDK 1.5后引入泛型后, 兼容之前字节码引入的桥接方法, 参考https://blog.csdn.net/ruizhige/article/details/120105227
			// 如果method是桥接方法, 则返回原方法, 否则直接返回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  / @Value / @Inject 不支持静态方法
				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);
				// Set方法设置的属性
				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 不仅可以处理 @Autowired, 还可以处理 @Value@Inject 注解;
  • org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition 方法调用时机是 bean 刚实例化完成,org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation,属性赋值以及初始化都还没进行,在后续属性赋值时将用到缓存的 AutowiredFieldElement
  • AutowiredFieldElement 类图如下,
    在这里插入图片描述

2. 依赖注入

真正对@Autowired / @Value / @Inject 注解属性赋值逻辑在org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor#postProcessProperties 方法实现。

α. 注入时机

org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor#postProcessProperties调用时机在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean 方法中,部分源码如下,

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
	
	// ...

	// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
	// state of the bean before properties are set. This can be used, for example,
	// to support styles of field injection.
	if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
		for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
			// 如果有bp.postProcessAfterInstantiation返回false, 将会跳过后续逻辑, 即不会对 @Resource / @Autowired 等注解属性赋值
			// 可以在 bp.postProcessAfterInstantiation 中自定义属性赋值逻辑
			if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
				return;
			}
		}
	}

	// ...
	
	boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
	boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);

	PropertyDescriptor[] filteredPds = null;
	if (hasInstAwareBpps) {
		if (pvs == null) {
			pvs = mbd.getPropertyValues();
		}
		for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
			// AutowiredAnnotationBeanPostProcessor 实现该方法对 @Autowired 等注解属性赋值
			// CommonAnnotationBeanPostProcessor 实现该方法对 @Resource 等注解属性赋值
			PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
			if (pvsToUse == null) {
				if (filteredPds == null) {
					filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
				}
				pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
				if (pvsToUse == null) {
					return;
				}
			}
			pvs = pvsToUse;
		}
	}
	// ...
}

这里需要注意,如果在 org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation 中自定义属性注入且返回 false,那么将跳过 AutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessor@Autowired@Resource 注解属性赋值逻辑。
看下 AutowiredAnnotationBeanPostProcessorpostProcessProperties 实现,

@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
	// postProcessMergedBeanDefinition 已经完成对@Autowired注解查找, findAutowiringMetadata方法可以直接从缓存中获取
	// InjectionMetadata内部Collection<InjectedElement> injectedElements 字段包含所有AutowiredFieldElement
	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;
}

org.springframework.beans.factory.annotation.InjectionMetadata#inject 循环调用属性 injectedElements 中所有 InjectedElementinject 方法,

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()) {
		// 这里element为InjectedElement实现类AutowiredFieldElement
		for (InjectedElement element : elementsToIterate) {
			element.inject(target, beanName, pvs);
		}
	}
}

所以真正 @Autowired 属性注入是通过 AutowiredFieldElement 完成的,看下相关源码,

private class AutowiredFieldElement extends InjectionMetadata.InjectedElement {

	private final boolean required;

	private volatile boolean cached;

	@Nullable
	private volatile Object cachedFieldValue;

	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; // @Autowired 注解字段
		Object value;
		if (this.cached) {
			try {
				// prototype Bean 非首次创建, 属性赋值时会走到这里
				value = resolvedCachedArgument(beanName, this.cachedFieldValue);
			}
			catch (NoSuchBeanDefinitionException ex) {
				// Unexpected removal of target bean for cached argument -> re-resolve
				value = resolveFieldValue(field, bean, beanName);
			}
		}
		else {
			value = resolveFieldValue(field, bean, beanName); // 解析 @Autowired 注解字段值
		}
		if (value != null) {
			ReflectionUtils.makeAccessible(field); // 使给定字段可访问, 以便给final字段赋值
			field.set(bean, value); // 反射设置属性值
		}
	}

	@Nullable
	private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
		DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
		desc.setContainingClass(bean.getClass());
		// 存储解析成功的Bean名称, 由于@Autowired可以给 collections / array 字段注入所有同类型Bean
		// 所以满足条件bean可能存在多个, 这里用Set存储
		Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
		Assert.state(beanFactory != null, "No BeanFactory available");
		TypeConverter typeConverter = beanFactory.getTypeConverter();
		Object value;
		try {
			// beanFactory -> DefaultListableBeanFactory 提供实现解析逻辑,
			value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
		}
		catch (BeansException ex) {
			throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
		}
		// prototype Bean可能同时创建, 所以需要保证缓存 cached 线程安全
		synchronized (this) {
			if (!this.cached) { // check
				Object cachedFieldValue = null;
				if (value != null || this.required) {
					cachedFieldValue = desc; // 默认
					// 向 beanFactory 注册 bean 之间依赖关系
					registerDependentBeans(beanName, autowiredBeanNames);
					if (autowiredBeanNames.size() == 1) { // 跳过 @Value等情况, @Value不依赖其他bean, 
						String autowiredBeanName = autowiredBeanNames.iterator().next();
						if (beanFactory.containsBean(autowiredBeanName) &&
								// collections / array 字段类型和bean类型不会匹配
								beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
							// 非 collections / array 字段注入
							cachedFieldValue = new ShortcutDependencyDescriptor(
									desc, autowiredBeanName, field.getType());
						}
					}
				}
				this.cachedFieldValue = cachedFieldValue;
				this.cached = true;
			}
		}
		return value;
	}
}
β. 依赖解析

可以看出,解析依赖 bean 功能主要由 org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency(org.springframework.beans.factory.config.DependencyDescriptor, java.lang.String, java.util.Set<java.lang.String> 实现,其处理逻辑参考《Spring Boot v2.4.4源码解析(十)依赖注入原理下 —— 依赖解析》。
从这篇博文可以看出,该方法解析依赖属于类型驱动,其大致步骤为先从 BeanFactory 中获取所有和 @Autowired 注解变量类型相同所有 bean 名称,然后再根据 限定符注解@Qualifier)/ autowireCandidate属性等条件筛选,如果变量非集合类型但是解析结果包含多个 bean 时,还可能根据 @Primary / 优先级等条件筛选一个。

二、@Resource

@Autowired 功能虽说非常强大,但是也有些不足之处。比如,它跟 Spring 强耦合了,如果换成了 JFinal 等其他框架,功能就会失效。而@Resource 是 JSR-250 提供的,它是 Java 标准,绝大部分框架都支持。
另外,他们作用范围也不同 —— @Resource 只能注解到类/成员变量/方法上,但 @Autowired 可以注解到构造器/方法/参数/成员变量/注解上。

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired { ... }

@Target({TYPE, FIELD, METHOD})
@Retention(RUNTIME)
public @interface Resource { ... }

@Resource 注解由 CommonAnnotationBeanPostProcessor 处理, 看下该类 UML 类图(为了简洁省去了部分不重要继承关系),在这里插入图片描述AutowiredAnnotationBeanPostProcessor 类似, CommonAnnotationBeanPostProcessor 同样也实现了MergedBeanDefinitionPostProcessor 接口,在该接口 org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition 方法中发现 javax.xml.ws.WebServiceRef / javax.ejb.EJB / Resource 注解,本文只讨论 Resource 注解。和 AutowiredAnnotationBeanPostProcessor 不同的是,发现 Resource 注解后, CommonAnnotationBeanPostProcessor 会把标有该注解的成员变量或方法统一封装成 ResourceElement

private InjectionMetadata buildResourceMetadata(final Class<?> clazz) {
	if (!AnnotationUtils.isCandidateClass(clazz, resourceAnnotationTypes)) {
		return InjectionMetadata.EMPTY;
	}

	List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
	Class<?> targetClass = clazz;

	do {
		final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();

		ReflectionUtils.doWithLocalFields(targetClass, field -> { // 在成员变量中查找注解
			if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) {
				// ... @WebServiceRef
			}
			else if (ejbClass != null && field.isAnnotationPresent(ejbClass)) {
				// ... @EJB
			}
			else if (field.isAnnotationPresent(Resource.class)) {
				if (Modifier.isStatic(field.getModifiers())) { // @Resource同样不支持静态成员变量
					throw new IllegalStateException("@Resource annotation is not supported on static fields");
				}
				if (!this.ignoredResourceTypes.contains(field.getType().getName())) {
					// 将该成员变量封装为ResourceElement
					currElements.add(new ResourceElement(field, field, null)); 
				}
			}
		});

		ReflectionUtils.doWithLocalMethods(targetClass, method -> { // 在方法中查找
			Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
			if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
				return;
			}
			if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
				if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) {
					// ... @WebServiceRef
				}
				else if (ejbClass != null && bridgedMethod.isAnnotationPresent(ejbClass)) {
					// ... @EJB
				}
				else if (bridgedMethod.isAnnotationPresent(Resource.class)) {
					if (Modifier.isStatic(method.getModifiers())) {
						throw new IllegalStateException("@Resource annotation is not supported on static methods");
					}
					Class<?>[] paramTypes = method.getParameterTypes();
					if (paramTypes.length != 1) {
						throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);
					}
					if (!this.ignoredResourceTypes.contains(paramTypes[0].getName())) {
						PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
						// 将方法及其桥接方法和PropertyDescriptor封装为ResourceElement
						currElements.add(new ResourceElement(method, bridgedMethod, pd));
					}
				}
			}
		});

		elements.addAll(0, currElements);
		targetClass = targetClass.getSuperclass(); // 递归处理继承树
	}
	while (targetClass != null && targetClass != Object.class);

	return InjectionMetadata.forElements(elements, clazz);
}

看下 ResourceElement
在这里插入图片描述

private class ResourceElement extends LookupElement {

		private final boolean lazyLookup;

		public ResourceElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) {
			super(member, pd);
			Resource resource = ae.getAnnotation(Resource.class); // 从成员变量/方法中获取@Resource注解
			// @Resource可以通过name和type属性指定依赖bean名称和类型
			String resourceName = resource.name();
			Class<?> resourceType = resource.type();
			// 如果没有指定依赖bean名称, 则使用默认名称
			this.isDefaultName = !StringUtils.hasLength(resourceName); 
			if (this.isDefaultName) {
				// 默认依赖bean名称取成员变量/方法名称
				resourceName = this.member.getName();
				if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) { 
					// 方法名需要去掉开头"set", 并将剩下首字符小写
					resourceName = Introspector.decapitalize(resourceName.substring(3));
				}
			}
			else if (embeddedValueResolver != null) {
				resourceName = embeddedValueResolver.resolveStringValue(resourceName);
			}
			if (Object.class != resourceType) {
				// 指定类型要能赋值给待注入变量, 即指定类型需要和待注入变量类型相同, 或为其子类(父类引用指向子类对象)
				checkResourceType(resourceType);
			}
			else {
				// No resource type specified... check field/method.
				// 未指定, 则注入bean类型为成员变量类型/setter函数设置的成员变量类型/函数第一个参数类型
				resourceType = getResourceType();
			}
			this.name = (resourceName != null ? resourceName : "");
			this.lookupType = resourceType;
			String lookupValue = resource.lookup();
			this.mappedName = (StringUtils.hasLength(lookupValue) ? lookupValue : resource.mappedName());
			Lazy lazy = ae.getAnnotation(Lazy.class);
			this.lazyLookup = (lazy != null && lazy.value());
		}

		@Override
		protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
			return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :
					getResource(this, requestingBeanName));
		}
	}

同样 CommonAnnotationBeanPostProcessor 会在 org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#postProcessProperties 方法中进行属性赋值,其处理逻辑和 AutowiredAnnotationBeanPostProcessor 类似,这里不再赘述。该方法最终会调用到 org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.ResourceElement#getResourceToInject 获取注入变量值,该方法最终又会调用到 org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#autowireResource

// element为由@Resource注解成员变量/方法封装而成的ResourceElement
// requestingBeanName为@Resource注解成员变量/方法所属bean
protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName)
		throws NoSuchBeanDefinitionException {

	Object resource;
	Set<String> autowiredBeanNames;
	String name = element.name;
	// class org.springframework.beans.factory.support.DefaultListableBeanFactory继承AbstractAutowireCapableBeanFactory类
	// AbstractAutowireCapableBeanFactory类实现AutowireCapableBeanFactory接口
	if (factory instanceof AutowireCapableBeanFactory) {
		AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory;
		DependencyDescriptor descriptor = element.getDependencyDescriptor();
		// fallbackToDefaultTypeMatch默认为true
		// 如果@Resource未指定依赖bean名称(此时name默认取成员变量名/方法名)且BeanFactory中不包含默认bean名称
		// 此时和@Autowired一样,通过类型驱动查找依赖bean
		if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {
			autowiredBeanNames = new LinkedHashSet<>();
			resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);
			if (resource == null) {
				throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
			}
		}
		else {
			// 名称驱动查找依赖bean
			// 不会校验resource和待注入变量类型是否一致,如果不一致,反射赋值时会报错
			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;
}

从以上源码可以看出,@Resource 在以下情况下是通过名称查找 bean,

  • @Resource 注解通过 name 属性指定依赖bean名称;
  • 未指定 bean 名称,但默认 bean 名称(成员变量名/去 "set" 前缀及首字母小写方法名)所对应 bean 在 BeanFactory 中存在;

通过名称查找 bean 方法 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#resolveBeanByName 参考《Spring Boot v2.4.4源码解析(十)依赖注入原理下 —— 依赖解析》。

三、总结

根据前文内容,先将 @Resource@Autowired 使用异同总结如下,

1. 功能相同情况

其实,如果 @Resource 未指定依赖 bean 名称且 BeanFactory 中不包含默认 bean 名称所对应 bean,则 @Resource@Autowired 注解 required 属性为 true 时处理依赖注入逻辑一样,均为类型驱动注入,也可以使用限定符注解@Qualifier)。
例如,有如下限定符注解

@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier()
public @interface CustomizedQualifier {
    String property() default "";
}

通过 Configuration 配置类向 Spring IOC 容器中注入如下 bean,

@Configuration
public class AppConfig {
    @Bean("b")
    @CustomizedQualifier(property = "foo")
    public B b(){
        return new B();
    }

    @Bean("b1")
    @CustomizedQualifier(property = "bar")
    public B b1(){
        return new B();
    }
}

bean A 中依赖注入 bean b,

@Component("A")
public class A implements InitializingBean{

	// 报错, Bean named 'b' is expected to be of type 'java.util.Map' but was actually of type 'B'
	// 虽然未在@Resource注解中指定依赖bean名称, 但是BeanFactory中存在类型默认名称为"b"的bean, 但是类型不匹配
	// 这里还是通过bean名称查找bean
    @Resource
    @CustomizedQualifier(property = "foo")
    private Map<String,B> b;

    @Override public void afterPropertiesSet() throws Exception {
        b.keySet().forEach(System.err::println);
    }
}

接下来修改成员变量名 "b""bb",此时 BeanFactory 中不存在名称为 "bb" 的 bean,则按照类型驱动注入,和使用 @Autowired 处理依赖注入逻辑一样,

@Component("A")
public class A implements InitializingBean{

    // 只有bean b注入进来, 说明限定符注解生效
    @Resource
    @CustomizedQualifier(property = "foo")
    private Map<String,B> bb;

    @Override public void afterPropertiesSet() throws Exception {
        // b
        bb.keySet().forEach(System.err::println);
    }
}

2. 区别

除以上情况外, @Resource@Autowired 主要有以下区别,

α. 解析依赖方式不同

这几乎是它们之间最主要差别,@Autowired 属于类型驱动解析, @Autowired 属于名称驱动解析,关于这两种解析具体详细逻辑,参考《Spring Boot v2.4.4源码解析(十)依赖注入原理下 —— 依赖解析》;

β. 注解作用范围不同

@Resource 只能注解到类/成员变量/方法上,但 @Autowired 可以注解到构造器/方法/参数/成员变量/注解上;

γ. 出处不同

@Autowired 是 Spring 定义的注解,而 @Resource 是 JSR-250 定义的注解,@Autowired只能在 Spring 框架下使用,而 @Resource 则可以与其他框架一起使用;

δ. 属性不同

@Autowired 只包含一个属性 —— required,默认 true,表示该依赖是否必须,如果该依赖必须但解析不到同类型 bean 时会报错。
@Resource 包含七个属性,其具体含义参考注释,

@Target({TYPE, FIELD, METHOD})
@Retention(RUNTIME)
public @interface Resource {
    /**
     * The JNDI name of the resource.  For field annotations,
     * the default is the field name.  For method annotations,
     * the default is the JavaBeans property name corresponding
     * to the method.  For class annotations, there is no default
     * and this must be specified.
     */
    String name() default "";

    /**
     * The name of the resource that the reference points to. It can
     * link to any compatible resource using the global JNDI names.
     *
     * @since Common Annotations 1.1
     */

    String lookup() default "";

    /**
     * The Java type of the resource.  For field annotations,
     * the default is the type of the field.  For method annotations,
     * the default is the type of the JavaBeans property.
     * For class annotations, there is no default and this must be
     * specified.
     */
    Class<?> type() default java.lang.Object.class;

    /**
     * The two possible authentication types for a resource.
     */
    enum AuthenticationType {
            CONTAINER,
            APPLICATION
    }

    /**
     * The authentication type to use for this resource.
     * This may be specified for resources representing a
     * connection factory of any supported type, and must
     * not be specified for resources of other types.
     */
    AuthenticationType authenticationType() default AuthenticationType.CONTAINER;

    /**
     * Indicates whether this resource can be shared between
     * this component and other components.
     * This may be specified for resources representing a
     * connection factory of any supported type, and must
     * not be specified for resources of other types.
     */
    boolean shareable() default true;

    /**
     * A product specific name that this resource should be mapped to.
     * The name of this resource, as defined by the <code>name</code>
     * element or defaulted, is a name that is local to the application
     * component using the resource.  (It's a name in the JNDI
     * <code>java:comp/env</code> namespace.)  Many application servers
     * provide a way to map these local names to names of resources
     * known to the application server.  This mapped name is often a
     * <i>global</i> JNDI name, but may be a name of any form. <p>
     *
     * Application servers are not required to support any particular
     * form or type of mapped name, nor the ability to use mapped names.
     * The mapped name is product-dependent and often installation-dependent.
     * No use of a mapped name is portable.
     */
    String mappedName() default "";

    /**
     * Description of this resource.  The description is expected
     * to be in the default language of the system on which the
     * application is deployed.  The description can be presented
     * to the Deployer to help in choosing the correct resource.
     */
    String description() default "";
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值