Spring IOC三种注解注入Inject、Resource、AutoWired区别与IOC注入过程

Spring三种注解方式的区别:@AutoWired、@Resource、@Inject
1、
  @AutoWired是Spring自带的方式
  @Resource是JSR-250标准,JDK6以上自带,Spring版本要求2.5以上
  @Inject是JSR-303标准,Spring版本3以上。需要导入外部依赖。
2、
  @AutoWired可以用在构造器、方法、属性、参数、注解上面.
  @Resource可以用在方法、属性、类上。
  @Inject可以用在方法、属性、构造器上。
  ps:当用在属性上面注入时,无须为该属性添加setter方法。

3、
  @AutoWired注入可以根据名字/类型,可以设置required属性为false指定找不到相应bean时不抛异常
  @Resource注入与@AutoWired一致。但可以指定name属性来指定beanName,但如果name对应的bean不存在,则会抛出异常,且没有required属性

  @Inject与@AutoWired一致,区别在于@Inject没有required属性

4、

  @Inject:通过AutowiredAnnotationBeanPostProcessor类实现依赖注入
  @Autowired:通过AutowireAnnotationBeanPostProcessor类实现依赖注入
  @Resource:通过CommonAnnotationBeanPostProcessor类实现依赖注入。

5、Spring的IOC注入过程

 在Spring所管理的类里面,在要注入的对象属性或者setter方法加上上面三个任意一个注解或者在xml文件中配置属性,Spring会自动找寻相应的bean然后将bean对象注入到当前对象中去。

 上述步骤可以分解为以下几个步骤,如图所示:

Spring IOC三种注解注入Inject、Resource、AutoWired区别与IOC注入过程_第1张图片

每次调用getBean时并不总是会调用doCreateBean方法,如果要获取的对象是singleton的,会先从工厂里面获取,否则会走这个流程。但无论是singleton还是prototype还是scope为其他类型,都会走一遍这个流程。

具体来看populateBean()方法:

protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
	PropertyValues pvs = mbd.getPropertyValues(); //当前bean要注入的属性
	if (bw == null) {
		if (!pvs.isEmpty()) { //要注入的属性不为空,但beanWrapper为空 抛出异常
			throw new BeanCreationException(
				mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
		}
		else {
			return;
		}
	}

	boolean continueWithPropertyPopulation = true;
	//这儿处理注解配置,使用不同的注解注入的类会有所不同
	if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { 
		for (BeanPostProcessor bp : getBeanPostProcessors()) {
			if (bp instanceof InstantiationAwareBeanPostProcessor) {
				InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
				if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
					continueWithPropertyPopulation = false;break;
				}
			}
		}
	}
	if (!continueWithPropertyPopulation) { //是否需要注入属性,不需要则bean构造已完成
		return;
	}
		
	//处理注入类型,byName还是byType, 注解的注入不会进入这儿,配置文件不指定autowire或者指定为default也不会进入这儿
	//这儿并没有完成IOC,只是根据名字或者类型找到对应bean
	if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
			mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
		MutablePropertyValues newPvs = new MutablePropertyValues(pvs);

		if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
			autowireByName(beanName, mbd, bw, newPvs);
		}

		if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
			autowireByType(beanName, mbd, bw, newPvs);
		}

		pvs = newPvs;
	}

	boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
	boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);
	//注解注入,xml的配置不会走这儿
	if (hasInstAwareBpps || needsDepCheck) {
		PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
		if (hasInstAwareBpps) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof InstantiationAwareBeanPostProcessor) {
					InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
					pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
					if (pvs == null) {
						return;
					}
				}
			}
		}
		if (needsDepCheck) {
			checkDependencies(beanName, mbd, filteredPds, pvs);
		}
	}
	//其他注入,如List Map等,xml配置的注入也是在这个方法完成的
	applyPropertyValues(beanName, mbd, bw, pvs);
	}
不管是xml的配置还是注解配置,要注入前都会有一个寻找目标bean的过程。这个过程在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory的resolveDependency方法中,具体实现在DefaultListableBeanFactory中,最终会调用DefaultListableBeanFactory的doResolveDependency方法。

下面是该方法的部分源代码:

public Object doResolveDependency(DependencyDescriptor descriptor, String beanName,
	Set autowiredBeanNames, TypeConverter typeConverter) throws BeansException {

	//如果要注入的List、Map类型
	Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
	if (multipleBeans != null) {
		return multipleBeans;
	}
			
	//如果要注入的是单个对象,不是集合类型
	//根据类型来查找匹配的bean
	Map matchingBeans = findAutowireCandidates(beanName, type, descriptor);
	if (matchingBeans.isEmpty()) {
		if (isRequired(descriptor)) { //没找到匹配的bean,会根据是否设置了required=true来抛出异常
			raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
		}
		return null;
	}

	String autowiredBeanName;
	Object instanceCandidate;
			
	//找到的bean的个数大于1
	if (matchingBeans.size() > 1) {
		//通过名字来匹配
		autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
		if (autowiredBeanName == null) { //没找到名字匹配的bean,根据是否设置了required抛出异常
			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;
			}
		}
		instanceCandidate = matchingBeans.get(autowiredBeanName);
	}
	else {
		//这种情况下根据类型找到了唯一的bean
		// We have exactly one match.
		Map.Entry entry = matchingBeans.entrySet().iterator().next();
		autowiredBeanName = entry.getKey();
		instanceCandidate = entry.getValue();
	}

	if (autowiredBeanNames != null) {
		autowiredBeanNames.add(autowiredBeanName);
	}
	return (instanceCandidate instanceof Class ?
			descriptor.resolveCandidate(autowiredBeanName, type, this) : instanceCandidate);
}
xml的配置方式注入寻找bean过程在autowireByType方法中调用了,而注解的方式注入是根据直接注入属性还是通过方法注入划分为了两个类,如图:

Spring IOC三种注解注入Inject、Resource、AutoWired区别与IOC注入过程_第2张图片

然后在各自的inject方法中调用resolveDependency方法。最后根据java reflect反射设置进去,这也是为什么在private属性上加上注解依旧能注入进去的原因。

而xml最终是使用下面的图上的类来完成注入的:

Spring IOC三种注解注入Inject、Resource、AutoWired区别与IOC注入过程_第3张图片

注入完成后代表构造bean的过程结束,最终会将构造完成的bean返回给调用者。

整个过程如图:

Spring IOC三种注解注入Inject、Resource、AutoWired区别与IOC注入过程_第4张图片

需要注意的是:

1、注解注入比xml配置注入先发生。

2、找寻bean的过程,根据类型匹配到的bean的个数如果大于1,并不是马上根据name匹配,而是先寻找有@Primary注解的bean,如果没有则寻找@Order注解的bean并且@Order的value属性最小的bean,如果@Primary和@Order都没找到,则会根据field的name匹配。ps:好像@Order配置bean不起作用

3、如果使用@Autowired注入并且使用@Qualifier指定name,则查找时会根据类型与名字查找,如果查找结果为空,则会抛出异常。

4、如果使用@Resource指定name属性与第三点一致。

所以当不指定bean的name属性时:

Spring IOC三种注解注入Inject、Resource、AutoWired区别与IOC注入过程_第5张图片

当指定name时:

Spring IOC三种注解注入Inject、Resource、AutoWired区别与IOC注入过程_第6张图片

最后注入时注解最好加在setter方法上面,因为field属性一般都是声明为private,通过反射不能直接访问,会调用setAccessible(true),但这个方法可能会因为权限不足而抛出异常。

以上就是个人理解的Spring IOC注入过程。但因个人水平原因,可能不全面或者有不对的地方,欢迎评论留言。

你可能感兴趣的:(java)