说到Spring,没有人不知道IOC与DI,自动注入最常见的注解就是@Autowired
和@Resource
,因此有一道经典的面试题:@Autowired
和@Resource
的区别,绝大多数人的答案就是byType和byName。真的这么简单吗?本人先不回答这个问题,而是从零开始探讨@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;
}
首先这个注解可以使用在一个类、方法或属性上面,通常我们都只注入属性。注意,没有@Target里面没有CONSTRUCTOR
,也就是说这个注解不能使用在构造方法上。也就是不支持构造注入。
另外这个类的报名也不是以org.springframework
开头的,而是属于一个标准(JSR-250
),属于这个标准的注解还有@PostConstruct
和@PreDestroy
。
如果这个注解是使用在属性和方法上,那么在对应Bean实例化的时候容器会注入一个请求资源的对象,如果这个注解是使用在类上面的话,程序在运行时才会查找。
虽然这个注解没有标识是继承性的,但是在父类的属性和方法中使用这个注解,子类是可以获益的。
package com.example.managingtransactions;
import javax.annotation.Resource;
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Resource(name = "myMovieFinder")
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
以上就是一个@Resource
注解使用在方法上的一个例子,当SimpleMovieLister类型的Bean在实例化的时候,因为 @Resource
注解的存在,首先会查找这个注解的各种信息,然后注入beanName为myMovieFinder
的那个Bean作为属性值。
如果注解中不明确指定name属性,那么默认的值会根据属性名称或setter方法的名称来推断。比如说在属性上,就会直接使用这个属性名称(field name),如果是setter方法上,则是property name,一般情况下,都是一样的。比如以下在setterMethod上使用,那么默认查找的beanName就是movieFinder
.
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Resource
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
但是如果在Spring容器中不存在对应名称的Bean,是不是就没法注入的呢?当然不是,@Resource
获取容器根据类型进行匹配。因此在以下的案例中,如果Spring容器中不存在一个名称为customerPreferenceDao
的bean的话,那么就会按照类型CustomerPreferenceDao
进行匹配了。对于一些容器中已知常见的类型的话,则不会如此复杂。比如BeanFactory
, ApplicationContext
, ResourceLoader
就不需要按照对应类型去查找了。
public class MovieRecommender {
@Resource
private CustomerPreferenceDao customerPreferenceDao;
@Resource
private ApplicationContext context;
public MovieRecommender() {
}
// ...
}
也就是说,@Resource
其实既是byName也是byType的。
那么这个属性是如何起作用的呢?下面就依照源码来进行说明。同时从源码来验证以上的观点。
在idea里面搜索一下这个注解,如下图所示
绝大多数的结果指向类CommonAnnotationBeanPostProcessor
.
当然在Spring的官方文档中也有相关的说明,感兴趣的话可以自己去阅读一下。https://docs.spring.io/spring/docs/5.0.17.RELEASE/spring-framework-reference/core.html#beans-resource-annotation
查看这个类的继承结构
它实现了多个接口,其中与@Resource
最相关的就是InstantiationAwareBeanPostProcessor
和MergedBeanDefinitionPostProcessor
,BeanFactoryAware
也有一些关系,因为要注入的对象是通过这个接口引入的beanFactory
进行查找的。
至于前两个接口,可以查看本人的博客:
https://blog.csdn.net/m0_37607945/article/details/107404447
和
https://blog.csdn.net/m0_37607945/article/details/107411096
通过实现InstantiationAwareBeanPostProcessor
接口,可以在一个bean实例化的前后对bean进行处理,在CommonAnnotationBeanPostProcessor
中就是在bean实例化之后进行属性填充的,也就是注入资源到标有@Resource
的类属性或类方法对应的属性上。而MergedBeanDefinitionPostProcessor
是在bean实例化之后与属性填充值前进行@Resource
注解元数据的解析操作的。
大致流程如下:
bean实例化->@Resource
元数据解析->@Resource
资源注入。
在CommonAnnotationBeanPostProcessor
类中对应的方法为:
postProcessMergedBeanDefinition
元数据解析和postProcessProperties
资源注入,分别属于MergedBeanDefinitionPostProcessor
和InstantiationAwareBeanPostProcessor
定义的方法。
在类中打上断点如下所示:
debug运行程序,查看调用栈信息
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 此处会创建bean实例
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
// 此处进行注解元数据解析
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}
.. Bean的初始化过程忽略
}
遍历容器中的所有MergedBeanDefinitionPostProcessor
进行后置处理
/**
* Apply MergedBeanDefinitionPostProcessors to the specified bean definition,
* invoking their {@code postProcessMergedBeanDefinition} methods.
* @param mbd the merged bean definition for the bean
* @param beanType the actual type of the managed bean instance
* @param beanName the name of the bean
* @see MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition
*/
protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof MergedBeanDefinitionPostProcessor) {
MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
}
}
}
然后就会进入到类CommonAnnotationBeanPostProcessor
中
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
}
首先会调用父类的方法,父类会针对bean生命周期相关注解元数据的解析,在当前类中会设置bean生命周期所使用的注解类型
/**
* Create a new CommonAnnotationBeanPostProcessor,
* with the init and destroy annotation types set to
* {@link javax.annotation.PostConstruct} and {@link javax.annotation.PreDestroy},
* respectively.
*/
public CommonAnnotationBeanPostProcessor() {
setOrder(Ordered.LOWEST_PRECEDENCE - 3);
setInitAnnotationType(PostConstruct.class);
setDestroyAnnotationType(PreDestroy.class);
ignoreResourceType("javax.xml.ws.WebServiceContext");
}
分别是PostConstruct
和PreDestroy
,这里不详细谈论了。
调用完父类的方法后,就会执行查找@Resource
的逻辑了、
private InjectionMetadata findResourceMetadata(String beanName, final 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 = buildResourceMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
return metadata;
}
这里只是涉及到一些缓存优化的逻辑,首先从缓存中获取相关元数据信息,如果获取不到则会调用buildResourceMetadata
进行创建,创建完成后,再放到缓存当中。
缓存定义如下
private final transient Map<String, InjectionMetadata> injectionMetadataCache = new ConcurrentHashMap<>(256);
第一次肯定缓存肯定是没有值的,会进入到buildResourceMetadata
当中
private InjectionMetadata buildResourceMetadata(final Class<?> clazz) {
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)) {
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@WebServiceRef annotation is not supported on static fields");
}
currElements.add(new WebServiceRefElement(field, field, null));
}
else if (ejbRefClass != null && field.isAnnotationPresent(ejbRefClass)) {
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@EJB annotation is not supported on static fields");
}
currElements.add(new EjbRefElement(field, field, null));
}
else if (field.isAnnotationPresent(Resource.class)) {
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@Resource annotation is not supported on static fields");
}
if (!this.ignoredResourceTypes.contains(field.getType().getName())) {
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)) {
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@WebServiceRef annotation is not supported on static methods");
}
if (method.getParameterCount() != 1) {
throw new IllegalStateException("@WebServiceRef annotation requires a single-arg method: " + method);
}
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new WebServiceRefElement(method, bridgedMethod, pd));
}
else if (ejbRefClass != null && bridgedMethod.isAnnotationPresent(ejbRefClass)) {
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@EJB annotation is not supported on static methods");
}
if (method.getParameterCount() != 1) {
throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method);
}
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new EjbRefElement(method, bridgedMethod, pd));
}
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);
currElements.add(new ResourceElement(method, bridgedMethod, pd));
}
}
}
});
elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
return new InjectionMetadata(clazz, elements);
}
不难看出这个方法相当的长,但是逻辑还是简单的,大体上,就是遍历一个类的所有属性和所有方法查找元数据信息,而且遍历所有父类,查找到元数据后构造一个InjectionMetadata
类型对象返回。
// 遍历所有属性
ReflectionUtils.doWithLocalFields(targetClass, field -> {...}
// 遍历所有方法
ReflectionUtils.doWithLocalMethods(targetClass, method -> {...}
// 遍历所有父类
while (targetClass != null && targetClass != Object.class);
在针对每个属性和方法进行查找的时候,同时会考虑支持@WebServiceRef
和@EJB
,这个也不是我们讨论的范畴,而且一般使用较少,忽略相关代码,那么简化如下:
在属性中查找@Resource
注解元数据
// 当前是否是否包含@Resource注解
else if (field.isAnnotationPresent(Resource.class)) {
// 不支持静态属性
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@Resource annotation is not supported on static fields");
}
// 在ignoredResourceTypes集合中不包含这个类
if (!this.ignoredResourceTypes.contains(field.getType().getName())) {
currElements.add(new ResourceElement(field, field, null));
}
}
首先如果一个属性中包含@Resource注解,但是不能是静态属性,而且属性类型不能是在ignoredResourceTypes
这个集合中的,可以通过在这个集合中添加那些你不想通过@Resource
来注入的类,比如在CommonAnnotationBeanPostProcessor
构造器中就添加了javax.xml.ws.WebServiceContext
这个类被忽略了。如果不违背上面两个条件的话,就会构造一个ResourceElement
类型的对象并添加到currElements
列表中。
在方法中查找@Resource
注解元数据
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);
currElements.add(new ResourceElement(method, bridgedMethod, pd));
}
}
在方法上包含@Resource
注解也不一定就可以,首先不能是静态方法,其实对应方法的参数个数必须是一个,最后注解类型不能在ignoredResourceTypes
,除了方法参数个数的限制外,其他与上面属性的相同。如果满足条件的话最后也会构造一个ResourceElement
并添加到currElements
列表中。
最后将遍历属性和方法收集到元数据信息也就是currElements
列表以及类类型构造一个InjectionMetadata
对象返回了。
这里涉及到两个类InjectionMetadata
和ResourceElement
,可以简单的理解为存储@Resource
元数据的信息的容器,比如说要注入的resourceName
实例名称、resourceType
实例类型、(isField
)属性注入还是方法注入、(Member
)对应的属性或者方法对象、
public ResourceElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) {
// 记录对应属性或方法的信息
super(member, pd);
// 获取对应注解
Resource resource = ae.getAnnotation(Resource.class);
// 注入名称
String resourceName = resource.name();
// 注入类型
Class<?> resourceType = resource.type();
// 如果没有设置注入的名称 此时会默认
this.isDefaultName = !StringUtils.hasLength(resourceName);
if (this.isDefaultName) {
// 对应方法或者属性的名称
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.
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());
}
从以上这段代码不难看出,如果传递了对应注解的name属性,则使用定义的,如果没有,则系统默认为对应的属性名称或者setter方法中除掉set三个前缀并将首字符小写的字符串(按照JavaBean原则其实就是属性名称)。另外如果embeddedValueResolver
不为空的话,还支持注入名称使用占位符或者SPEL语法模式。在目前Spring容器中应该来说都是存在的。因为CommonAnnotationBeanPostProcessor
实现了BeanFactoryAware
接口,在CommonAnnotationBeanPostProcessor
实例化的时候就会调用如下的方法设置embeddedValueResolver
属性值:
@Override
public void setBeanFactory(BeanFactory beanFactory) {
Assert.notNull(beanFactory, "BeanFactory must not be null");
this.beanFactory = beanFactory;
if (this.resourceFactory == null) {
this.resourceFactory = beanFactory;
}
if (beanFactory instanceof ConfigurableBeanFactory) {
this.embeddedValueResolver = new EmbeddedValueResolver((ConfigurableBeanFactory) beanFactory);
}
}
其中这个EmbeddedValueResolver就可以用解析占位符和SPEL语法了。
最后还会解析注解中的lookup
属性和mappedName
,前者优先,作为mappedName
.
InjectionMetadata
对象更简单,就是简单的存储一下被注入的类和InjectedElement
信息列表。
public InjectionMetadata(Class<?> targetClass, Collection<InjectedElement> elements) {
this.targetClass = targetClass;
this.injectedElements = elements;
}
收集完以上信息之后,会进行checkConfigMembers操作,其实也就是将对应的注解信息添加到beanDefinition
中.
public void checkConfigMembers(RootBeanDefinition beanDefinition) {
Set<InjectedElement> checkedElements = new LinkedHashSet<>(this.injectedElements.size());
for (InjectedElement element : this.injectedElements) {
Member member = element.getMember();
if (!beanDefinition.isExternallyManagedConfigMember(member)) {
beanDefinition.registerExternallyManagedConfigMember(member);
checkedElements.add(element);
if (logger.isTraceEnabled()) {
logger.trace("Registered injected element on class [" + this.targetClass.getName() + "]: " + element);
}
}
}
this.checkedElements = checkedElements;
}
// RootBeanDefinition#registerExternallyManagedConfigMember
@Nullable
private Set<Member> externallyManagedConfigMembers;
public void registerExternallyManagedConfigMember(Member configMember) {
synchronized (this.postProcessingLock) {
if (this.externallyManagedConfigMembers == null) {
this.externallyManagedConfigMembers = new HashSet<>(1);
}
this.externallyManagedConfigMembers.add(configMember);
}
}
这个步骤是在bean实例化完成后、经过实例化后置处理器的后置处理之后进行的,如下图所示:详细可参考源码org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);
}
return pvs;
}
与解析元数据信息相同的是,首先也会调用findResourceMetadata
方法,但是此时缓存中存在相关信息,直接从缓存中就会获取到元数据信息了。
执行InjectionMetadata
的inject
方法,从前面的分析可以知道,在InjectionMetadata
对象当中存在ResourceElement
信息,而ResourceElement
对象存储了需要被注入的各种信息,因此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()) {
for (InjectedElement element : elementsToIterate) {
if (logger.isTraceEnabled()) {
logger.trace("Processing injected element of bean '" + beanName + "': " + element);
}
element.inject(target, beanName, pvs);
}
}
}
进行InjectedElement
(ResourceElement
的父类)对象的注入操作。
/**
* 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);
// 获取Resource资源并进行设置
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();
}
}
}
以上的操作无非就是先查找到对应的Resource资源然后再进行对应属性或者方法的反射操作设置属性了。重点还是在getResourceToInject
方法中。
@Override
protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :
getResource(this, requestingBeanName));
}
首先判断是否懒查找模式(包含@Resource
注解的同时包含@Lazy
注解),如果是懒查找的模式,则是构建一个代理,如果不是则是获取资源对象。此处先看getResource
方法。
获取资源
/**
* Obtain the resource object for the given name and type.
* @param element the descriptor for the annotated field/method
* @param requestingBeanName the name of the requesting bean
* @return the resource object (never {@code null})
* @throws NoSuchBeanDefinitionException if no corresponding target resource found
*/
protected Object getResource(LookupElement element, @Nullable String requestingBeanName)
throws NoSuchBeanDefinitionException {
if (StringUtils.hasLength(element.mappedName)) {
return this.jndiFactory.getBean(element.mappedName, element.lookupType);
}
if (this.alwaysUseJndiLookup) {
return this.jndiFactory.getBean(element.name, element.lookupType);
}
if (this.resourceFactory == null) {
throw new NoSuchBeanDefinitionException(element.lookupType,
"No resource factory configured - specify the 'resourceFactory' property");
}
return autowireResource(this.resourceFactory, element, requestingBeanName);
}
前两个if主要是JNDI,不是我们通常使用Spring的姿势,因此还是要看autowireResource
方法
/**
* Obtain a resource object for the given name and type through autowiring
* based on the given factory.
* @param factory the factory to autowire against
* @param element the descriptor for the annotated field/method
* @param requestingBeanName the name of the requesting bean
* @return the resource object (never {@code null})
* @throws NoSuchBeanDefinitionException if no corresponding target resource found
*/
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();
// 如果容器中不包含对应名称的bean但是fallbackToDefaultTypeMatch设置为true,就会按照类型进行查找了
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 {
resource = beanFactory.resolveBeanByName(name, descriptor);
autowiredBeanNames = Collections.singleton(name);
}
}
else {
// 直接按照bean名称查找
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;
}
从上面不难看出,首先如果容器中不包含对应名称的bean的时候,而且fallbackToDefaultTypeMatch
设置为true,才会按照类型去查找,只不过这个属性默认为true.可以通过setter方法进行设置
public void setFallbackToDefaultTypeMatch(boolean fallbackToDefaultTypeMatch) {
this.fallbackToDefaultTypeMatch = fallbackToDefaultTypeMatch;
}
这里查找的name也是在之前构ResourceElement
设置的,首先是取注解中的name属性,没有的话,则默认为类属性名称。
@Autowired applies to fields, constructors, and multi-argument methods, allowing for narrowing through qualifier annotations at the parameter level. In contrast, @Resource is supported only for fields and bean property setter methods with a single argument. As a consequence, you should stick with qualifiers if your injection target is a constructor or a multi-argument method