@Resource注解的原理、源码

本文深入剖析Spring框架中@Resource注解的工作原理,包括如何使用、查找类的注入点及具体的注入过程。通过源码解读,揭示@Resource在Spring中的运作机制。


前言

@Resource注解和@Autowired注解是咱们使用Spring的两大利器,用来进行属性注入。这篇文章来简单分析下@Resource的原理


一、@Resource怎么用?

很简单的啦,譬如如下:

@Service
public class TestService {
   
   
    @Resource
    ResourceLockDAO testDao;
}

二、那么注入做了些啥

1.查找一个类的所有注入点

1.1 在Spring中Bean的生态位置

Spring的生态代码过程中,在推断出类的构造方法,且根据构造方法创建实例之后,就进行了该bean的注入点查询过程。
详见AbstractAutowireCapableBeanFactory的doCreateBean方法中的applyMergedBeanDefinitionPostProcessors,看源码么,点进去!!

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

可以看到循环调用了所有BeanPostProcessor,找到MergedBeanDefinitionPostProcessor子类,调用其postProcessMergedBeanDefinition方法,此处不累赘,我直接说了,调用的CommonAnnotationBeanPostProcessor的对应方法。细心的你可能会发现,还有个叫AutowiredAnnotationBeanPostProcessor的类也有该方法,从类名,聪明的你应该猜到,这个类的该方法是为了处理@Autowired注解。
废话不多说,咱们来看看@Resource注解查询

1.2 detail,源码解读

@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
   
   
	super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
	InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
	metadata.checkConfigMembers(beanDefinition);
}

咱们主要看findResourceMetadata方法

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;
	}
  1. 可以看到此处先从injectionMetadataCache这个map结构的cache里获取,如果获取不到才会进行下面的操作,大家是不是觉得没有必要,因为Spring是单例的,但是Spring只是默认是单例的,万一你使用的scope是原型呢?是吧?查找bean的注入点这个操作就不需要多次执行了。
  2. 看到此处的synchronized原语的上下代码,熟悉单例设计模式的同学应该兴奋了。因为这是咱们的单例模式–懒汉实现方式的原理,也就是双重检查锁,也就是在synchronized的前后分别进行判断,防止多线程同时处理,最后出错。
    下面是最重要的代码逻辑,根据重要程度,此处源码不会全部贴上来,会进行适当删减
private InjectionMetadata buildResourceMetadata(final Class<?> clazz) {
   
   
		List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
		Class<?> targetClass = clazz;

		do {
   
   
			final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
			// 处理属性上的@Resource注解
			ReflectionUtils.doWithLocalFields(targetClass, field -> {
   
   
				if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) {
   
   
				// 如果有@WebServiceRef注解,也处理
				}
				else if (ejbRefClass != null && field.isAnnotationPresent(ejbRefClass)) {
   
   
				// 如果有@EJB注解也处理
				}
				else if (field.isAnnotationPresent(Resource.class)) {
   
   
					// 如果加了@Resource注解的field是static的,那
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值