@Autowired
和 @Resource
属性注入分别是由 AutowiredAnnotationBeanPostProcessor
和 CommonAnnotationBeanPostProcessor
处理,本文将从源码出发,解析这两种注解对属性注入区别。
一、@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.youkuaiyun.com/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
,那么将跳过 AutowiredAnnotationBeanPostProcessor
和 CommonAnnotationBeanPostProcessor
对 @Autowired
和 @Resource
注解属性赋值逻辑。
看下 AutowiredAnnotationBeanPostProcessor
对 postProcessProperties
实现,
@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
中所有 InjectedElement
的 inject
方法,
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 "";
}