spring源码-@Autowired、@Resource注解底层原理

  • 时间:
  • 来源:互联网
  • 文章标签:

上篇博客说了自动注入模型的使用,这篇笔记打算记录下自动注入模型中,autowireMode为0的情况,也就是我们经常用到的@Autowired、@Resource注解的原理

首先对于这两个注解,都是由后置处理器来完成解析的,解析的时机是一样的,在第六个后置处理器执行的时候;但是是由不同的后置处理器来完成解析的
@Autowired注解:

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues

@Resource注解:

org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#postProcessPropertyValues

前言

1、这两个注解,都是在第六个后置处理器执行的时候,进行解析的,但是在第三个处理器

org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition

执行的时候,会先解析一下当前bean中有哪些属性要进行注入,在第六个后置处理器执行的时候,就无须进行解析,直接从map中获取要注入的属性即可

2、这两个注解虽然是由不同的后置处理器处理的,但是处理的思想、步骤大致都是一样的:

  1. 会先尝试获取bean中添加了该注解的方法(method)和属性(field)
  2. 找到之后,会根据实现类的优先级、或者是name等从容器中或者beanDefinitionMap中找到对应的属性
  3. 通过filed.set()完成属性的注入

3、这里有几个点要先行说明:

  • 对于@Resource注解,会根据要注入属性的name,从单实例池中获取bean,完成属性注入
  • 对于@Autowired注解,我们通常说是现根据类型注入,如果找到多个或者没有找到,再根据name进行注入;这里要说明的是:spring在解析@Autowired注解的时候,并不是直接从单实例池中查找bean,而是先从beanDefinitionMap中,根据type查找,如果找到多个,再根据name从单实例池中查找,只有找到确定要注入哪个类,才会从单实例池中找到对应的bean,完成注入
  • 当然,和@Autowired注解配合使用的注解有好几个,在下面会一一解析

@Autowired

在这里插入图片描述
这是@Autowired注解解析的整个流程,其中,和@Autowired注解配合使用的有几下几个注解:
@Qualifier
@Primary
@Priority

第一个注解使用在field上,在根据类型从beanDefinitionMap中找到bean之后,会判断哪个beanName是否是合格的;如果要注入的bean有多个实现类,会优先注入@Qualifier注解指定的实现类

第二个直接是在类上使用的,在未添加@Qualifier注解的场景下,如果有多个实现类,会判断哪个实现类上添加了@Primary注解,添加了,就注入该实现类

第三个注解也是加载类上,如果既没有添加@Qualifer、也没有添加@Priority注解,那么,会判断哪个实现类的优先级比较高,值越小,优先级越高

这是

org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency

的部分代码

/**
 * @Autowired注解源码 三
 *  查找要注入的bean,按照type来查找,如果匹配到一个,就获取到这个实例,然后返回,返回之后,会调用field.set();注入进去
 *  如果找到了多个,那就先判断哪个实现类有@primary注解,如果都没有注解,那就判断priority的优先级
 *
 *  这个方法中,对Qualifier注解进行了处理;如果一个接口有多个实现类,但是没有加@Qualifier注解,那么就会返回多个
 *
 *  如果@Qualifier指定的value在spring容器中没有,这里返回的map集合就是空
 *
 */
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
if (matchingBeans.isEmpty()) {
	if (isRequired(descriptor)) {
		// 如果匹配到的要注入的bean是null,就报错
		raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
	}
	return null;
}

String autowiredBeanName;
Object instanceCandidate;

if (matchingBeans.size() > 1) {
	/**
	 * 如果根据类型,匹配到有多个bean,那就判断@Primary注解和@Priority注解;
	 *  所以:可以得出一个结论:
	 * 	  当一个接口有多个实现类的时候,添加了@Qualifier注解,同时在一个类上添加了@Primary注解,那么会注入@Qualifier注解对应的bean
	 *
	 * 	在下面的这个推断方法中:
	 * 	 1.会先判断@Primary注解
	 * 	 2.再判断@Priority注解声明的优先级
	 * 	 3.如果两个注解都没有,那就根据controller中注入的service的name和matchingBeans中的beanName进行匹配
	 * 	   如果匹配上,就注入匹配上的beanName对应的beanClass
	 * 	   这里需要注意的是:根据name查询的时候,会先根据type从beanDefinitionMap中查询到符合类型的beanDefinitionNames
	 * 	   然后从beanDefinitionNames中依次匹配,是否和name一致
	 */
	autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
	if (autowiredBeanName == null) {
		if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
			return descriptor.resolveNotUnique(type, matchingBeans);
		}
		else {
			// In case of an optional Collection/Map, silently ignore a non-unique case:
			// possibly it was meant to be an empty collection of multiple regular beans
			// (before 4.3 in particular when we didn't even look for collection beans).
			return null;
		}
	}
	/**
	 * 如果能找到一个,那就根据name获取到对应的instance实例,然后再field.set();
	 */
	instanceCandidate = matchingBeans.get(autowiredBeanName);
}
else {
	// We have exactly one match.
	Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
	autowiredBeanName = entry.getKey();
	instanceCandidate = entry.getValue();
}

if (autowiredBeanNames != null) {
	autowiredBeanNames.add(autowiredBeanName);
}
if (instanceCandidate instanceof Class) {
	/**
	 * 根据获取到的beanName和type从容器中查找。如果没有找到,就创建
	 * 这里才是真正的从单实例池中获取对象,如果对象没有,就创建
	 */
	instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
}

在注释中,详细注释了哪行代码解析了哪个注解,我想着重看下
determineAutowireCandidate这个方法

/**
 * 如果根据type获取到有多个符合的bean,那就来这个方法中,推断出其中的一个
 * @param candidates:推断出来的多个bean
 * @param descriptor:要注入的属性对应的类型,如果是接口,那这里就是接口的类型
 * @return
 */
@Nullable
protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {
	Class<?> requiredType = descriptor.getDependencyType();
	/**
	 * 判断哪个bean有@Primary注解,返回注解对应的beanName
	 */
	String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
	if (primaryCandidate != null) {
		return primaryCandidate;
	}
	/**
	 * 如果都没有添加@Primary注解,那就判断bean是否有添加@Priority注解,注解值越小,优先级越高;返回优先级高的beanName
	 */
	String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
	if (priorityCandidate != null) {
		return priorityCandidate;
	}
	// Fallback
	/**
	 * 这里的descriptor中保存的是注入的bean对应的名称(在controller注入service,这里就是controller中的service属性名)
	 * 判断 从容器中拿出来的beanName是否和controller中的service属性名一致,如果一致,就注入一致的这个
	 *  这也就是在controller注入service的时候,如果service有多个实现类,那么,如果注入的service属性名和实现类的beanName一致,也可以注入成功的原因
	 *
	 *  class Controller{
	 * @Autowired
	 * Service service01
	 *  }
	 *   如果service有多个实现类,其实注入的是service01,就是这里判断的
	 *
	 *   这里我觉得,也是我们平常所说的,如果根据type找到多个,或者没有找到,就根据name来注入,这里就是根据name来注入的这一步
	 *   candidates:是根据类型从beanDefinitionMap中获取到的符合类型的beanDefinitionNames
	 *   descriptor.getDependencyName():是@Autowired注解属性注入的属性name
	 */
	for (Map.Entry<String, Object> entry : candidates.entrySet()) {
		String candidateName = entry.getKey();
		Object beanInstance = entry.getValue();
		if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||
				matchesBeanName(candidateName, descriptor.getDependencyName())) {
			return candidateName;
		}
	}
	return null;
}

通过这两段代码,我觉得可以说明一个问题:

  1. 对于@Autowired注解,spring在处理的时候:会先解析@Qualifer注解,然后解析@Primary注解,然后再解析@Priority注解
  2. 如果解析之后,匹配的bean还是有多个,那就根据name来查找,需要注意的是这里的查找都是基于从beanDefinitionMap中找到的符合注入条件的bean;
  3. 只有在确认要注入的是哪个实现类之后,才会从单实例池中获取实例化之后的bean

这里有一个点再补充一下,我前面一直在说的这个点:所有的查找都是基于从beanDefinitionMap中查找到符合注入条件的bean:下面这段代码可以证实这个观点:
在这里插入图片描述

这里的代码,我就不再追进去了,因为这里的底层,可以看到是遍历beanDefinitionNames这个集合,依次根据beanDefinitionName获取到beanDefinition,然后判断beanDefinition是否是非抽象类,是否是非懒加载的、是否是单实例的,是否是type类型的等等,如果满足,就把当前beanName返回

@Resource

在这里插入图片描述
这是一个简单的流程图,图里面基本上就把@Resource注解的解析说明白了,我们看为什么说@Resource是根据name来注入属性的,只需要看图中的最后一个方法

protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName)
		throws NoSuchBeanDefinitionException {

	Object resource;
	Set<String> autowiredBeanNames;
	String name = element.name;

	if (this.fallbackToDefaultTypeMatch && element.isDefaultName &&
			factory instanceof AutowireCapableBeanFactory && !factory.containsBean(name)) {
		autowiredBeanNames = new LinkedHashSet<>();
		/**
		* 这里也可以佐证@Resource是根据name来进行注入的,这里代码追进去,可以看到,是也是调用的doGetBean()
		* 所以:无论是走这里,还是下面的getBean,都是从单实例池中尝试获取bean的
		*/
		resource = ((AutowireCapableBeanFactory) factory).resolveDependency(
				element.getDependencyDescriptor(), requestingBeanName, autowiredBeanNames, null);
		if (resource == null) {
			throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
		}
	}
	else {
		/**
		 * 之所以说@Resource是按照name来注入的,就是这里的代码可以佐证这个观点
		 * 这里,直接根据name从单实例池中获取bean,这里是从单实例池中获取,如果存在就返回,不存在就初始化
		 * 这里的getBean()调用的就是org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
		 */
		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;
}

所以:对于@Resource注解,就是根据在bean中设置的name,从单实例池中查找,如果找到就注入,没找到,就是null

总结

@Resource注解没什么可说的,就是根据name从单实例池中,获取能匹配上的beanDefinition

对于@Autowired注解

  1. 首先会根据type,从beanDefinitionMap中获取到所有符合该类型的beanDefinition对应的beAnName
  2. 然后判断对应的@Qualifier注解,优先注入该注解指定的bean,如果在该注解执行了bean,但是没有找到,那就返回null
  3. 如果没有添加@Qualifer注解,但是找到了多个实现类,那就判断是否有添加@Primary注解,优先注入添加了该注解的bean;如果多个类添加了该注解,spring会报错
  4. 如果没有添加@Primary注解,就处理@Priority注解,该注解是声明优先级的,值越小,优先级越高
  5. 如果这三个注解都没有添加,那就根据name,判断从beanDefinitionMap中查找的符合注入类型的所有beanName中,是否有和field对应的name一样的,有,就注入该bean,没有就根据@Autowired注解的required是true还是false,来决定是抛出异常还是返回null
  6. 如果找到、确定了注入的bean,就会从单实例池中获取beanName对应的完成实例化的bean对象,然后注入

最后一点,需要说明的是:@Autowired注解和@Resource注解都是通过field.set()完成属性填充的

本文链接http://www.taodudu.cc/news/show-1782022.html