@Resource源码解析

本文主要探讨了@Resource注解的工作原理,特别是在自动注入过程中的差异。与@Autowired不同,@Resource首先尝试按名称进行注入,然后才按类型。详细分析了@Resource如何寻找注入点,以及在找不到匹配名称时如何切换到按类型查找。此外,提到了@Lazy的支持以及对JNDI工厂的兼容性。通过源码解析,揭示了@Resource注解看似先按名称后按类型的注入逻辑实则是由条件判断导致的误解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

@Resource这个注解在实现上很类似,但是于@Autowired在一般情况下是先byType,再byName,但是@Resource则相反,通知@Resource还可以单独制定注入的bean的名字,具体实现也是通过一个后置处理器进行实现的,这个后置处理是CommonAnnotationBeanPostProcessor

@Resource的自动注入

对于@Resource的自动注入而言,其注入的方法也是postProcessProperties这个方法

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;
	}

这个方法可以说是和@Autowired对应后置处理器的postProcessProperties一模一样了
首先是先找到所有的注入点InjectionMetadata,的inject方法

首先来看看@Resource是如何找注入点的

	private InjectionMetadata buildResourceMetadata(final Class<?> clazz) {
		...
			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));
					}
				}
			...

其实就是简单的判断了一下这个属性上面有没有加了@Resource,如果加了这个注解,那么对于spring而言,这就是一个注入点,
同时构造一个注入点,这个注入点的实现类是ResourceElement,在这个注入点的构造里面,如果配置了name属性就会获取name的值,如果没有那么就会默认使用变量名作为name,对于方法而言,就是set方法之后的字符串

对于方法的注入点查找也是类似

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

这里于@Autowired还有一点不同的是,@Resource限制了方法的参数只能有一个,不然会抛出异常

ResourceElement里面重写了自己去实现注入的方法

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

这里lazylookup是配置的@Lazy注解的属性,从这里也可以看书@Reource是支持@Lazy的
所以默认这里是false

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

jndiFactory应该是跟jndi相关的工厂,这里不做探讨,从源码里面是可以看出,@Resource是支持JNDI工厂的,如果@Resource配置得了mappedName这个属性,那么就会去 jndiFactory 相关的工厂里面去找,如果说没有这个工厂,才会再spring 的工厂里面去找

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

同@Autowired一样,先取出属性描述对象,然后通过属性描述对象找到要注入的点,调用resolveDependency,但是这个方法是@Autowired的核心方法,我们都知道@Autowired是先byType,再byName,但是@Resource是先byName再ByType,但是在这里调用
resolveDependency方法,那么@Resource不就和@Autowired一样了
其实是的,在调用这个方法之前有一个判断 factory.containsBean(name) ,这个判断是当前spring容器里面有没有这个name的bean,这里就是byName的判断,如果说有,那么就直接调用resource = beanFactory.resolveBeanByName(name, descriptor);在这里面就是直接调用spring 的getBean方法,也就是说直接通过名字从spring里面拿到Bean直接返回,这就是byName的逻辑。
但是如果factory.containsBean(name)返回的false,也就是当前spring工厂里面没有这个名字的bean,就会通过resolveDependency方法找注入的对象,所以resolveDependency里面进行的是byType,再byName,但是name 再调用这个方法之前就已经判断是没有的,所以相当于这个ByName是没有效果的,所以给人了一种先ByName再ByType的假象
这里找到注入的bean后,会缓存起来,等待spring后续的生命周期继续注入

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值