Spring的@Resource原理

说到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最相关的就是InstantiationAwareBeanPostProcessorMergedBeanDefinitionPostProcessorBeanFactoryAware也有一些关系,因为要注入的对象是通过这个接口引入的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资源注入,分别属于MergedBeanDefinitionPostProcessorInstantiationAwareBeanPostProcessor定义的方法。

在类中打上断点如下所示:
在这里插入图片描述
debug运行程序,查看调用栈信息
在这里插入图片描述

bean的实例化
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");
}

分别是PostConstructPreDestroy,这里不详细谈论了。
调用完父类的方法后,就会执行查找@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对象返回了。
在这里插入图片描述
这里涉及到两个类InjectionMetadataResourceElement,可以简单的理解为存储@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方法,但是此时缓存中存在相关信息,直接从缓存中就会获取到元数据信息了。
在这里插入图片描述
在这里插入图片描述
执行InjectionMetadatainject方法,从前面的分析可以知道,在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);
		}
	}
}

进行InjectedElementResourceElement的父类)对象的注入操作。

/**
 * 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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lang20150928

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值