@Resource注解工作原理

本文深入探讨了Java中的@Resource注解的工作机制,详细解释了当注解中指定name属性和未指定时的bean查找逻辑,以及@Autowired的联动作用。通过分析注解在字段和方法上的应用,明确了如何根据名称和类型匹配bean,并介绍了默认名称生成的规则。内容涵盖了bean查找、类型匹配和错误处理等关键点。
摘要由CSDN通过智能技术生成

引言

在探索原理及源码时,最好是带有目的性的去验证,拿结论来探索过程,往往是最有效的

本次我们来探索一下@Resource注解的工作原理,我们先看下结论
1.如果@Resource注解中指定了name属性,那么则只会根据name属性的值去找bean,如果找不到则报错
2.如果@Resource注解没有指定name属性,那么会先判断当前注入点名字(属性名字或方法参数名字)是不是存在Bean,如果存在,则直接根据注入点名字取获取bean,如果不存在,则会走@Autowired注解的逻辑,会根据注入点类型去找Bean

1.找到所有被@Resource注解的字段和方法,进行筛选

InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
  • 判断是否能从缓存中获取到,获取到并且类型匹配则直接返回
    在这里插入图片描述

  • 根据当前bean的class类型,获取所有的注入点
    (上图代码第363行的buildResourceMetadata方法)

    • 1.判断是否有@Resource注解
    • 2.获取被@Resource注解的Field,static属性不注入
      在这里插入图片描述

    • 3.获取被@Resource注解的Method,static方法不处理,并且方法的参数只能有一个,不然报错
      在这里插入图片描述

    • 4.筛选出可以被当作注入点的Field或者Method各自封装到 ResourceElement 对象,然后放到一个集合中(下图代码447行)
      在这里插入图片描述

    • 5.循环处理父类,相同处理逻辑,直至Object.class停止(上图代码448行)

    • 6.将含有注入点的集合,封装到InjectionMetadata对象中(上图代码452行)

    • 7.返回

  • put进缓存injectionMetadataCache中

2.根据返回的注入点,进行循环处理、注入

metadata.inject(bean, beanName, pvs);
element.inject(target, beanName, pvs);
  • 由于上述步骤封装了ResourceElement对象,内部构造方法会判断注解上是否有值
    • 有值,设置为接下来要寻找的beanName
    • 无值,设置属性或方法名为接下来要寻找的默认beanName
  • 开始找bean
    会根据属性还是方法来分别进行处理
    核心方法是 getResourceToInject(target, requestingBeanName)
    在这里插入图片描述

1.如果是@Lazy,则先返回一个代理对象
在这里插入图片描述

2.根据设置的name去BeanFactory找bean

找不到
1.判断name是否为默认生成的名字(未指定value值,根据字段或方法名默认生成)
2.
①不是默认(即value赋值的名字,具体什么时候设置为默认,往下看),报错;
②是默认生成的名字,则开始根据@Autowired自动装配逻辑,进行byType查找,在进行byName(此时byName已经没有意义,因为之前已经查找但找不到);否则直接报错
@Autowired的工作原理可以点此链接来学习一下

//this.fallbackToDefaultTypeMatch判断是否可以在找不到时根据type进行匹配
//element.isDefaultName 判断是否是默认生成的名字
if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {
	autowiredBeanNames = new LinkedHashSet<>();
	//根据name找不到bean时,会调用下面方法,即走@Autowired装配步骤
	resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);
	if (resource == null) {
		throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
	}
}

找到了: 直接getBean(beanName,target.class),也就是说,根据名字找到了bean,还要再根据类型校验一下
返回

什么是默认生成的name

@Resource(“XXX”)
public UserService userService;

@Rsource如果不指定value值,会根据属性名或者方法名的第一个参数的参数名生成一个默认的name,并且封装的对象属性会设置一个标志(this.isDefaultName),标记该对象的name是默认生成的,还是指定的.

所以如果@Resource注解指定了某个字符串,则会根据指定的字符串去查找,如果根据名字不匹配,则会报错,找不到,而不是继续根据类型匹配

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();
	// 如果@Resource注解中没有指定name,那么就使用默认生成的名字
	this.isDefaultName = !StringUtils.hasLength(resourceName);
	if (this.isDefaultName) {
		resourceName = this.member.getName();
		if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) {
			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());
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值