spring源码-自动注入

在前面的博客中,介绍了@Autowired和@Resource注解的原理,这两种方式,我们认为是手动注入,这是spring默认的注入方式,如果我们要使用自动注入,需要设置自动注入模式autowireMode

一、应用

@Component
public class UserBean {
    
    

    private IndexBean indexBean;

    public UserBean(IndexBean indexBean) {
    
    
        System.out.println("userBean带参构造函数");
        this.indexBean = indexBean;
    }

    public UserBean(){
    
    
        System.out.println("userBean空参构造函数");
    }

    public void setIndexBean123(IndexBean indexBean) {
    
    
        System.out.println("setIndexBean123方法注入属性");
        this.indexBean = indexBean;
    }

    public void setIndexBean(IndexBean indexBean){
    
    
        System.out.println("setIndexBean方法执行,这是通过类型自动注入时会执行的代码");
        this.indexBean = indexBean;
    }

    public void test() {
    
    
        System.out.println("注入的indexBean属性是:" + indexBean);
    }
}

这是通过BeanFactoryPostProcessor来修改bean对应的自动注入模式

/* 修改beanDefinitionRegistry注入模型有多种方式:
 *  1.在BeanFactoryPostProcessor的实现类中进行修改
 *  2.在ImportBeanDefinitionRegistrar的实现类中进行修改
 */
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    
    
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory)
            throws BeansException {
    
    
        GenericBeanDefinition userBean = (GenericBeanDefinition) configurableListableBeanFactory
                .getBeanDefinition("userBean");
        System.out.println("默认的autowireMode是:" + userBean.getAutowireMode());
        userBean.setAutowireMode(1);
    }
}

自动注入模型,默认为0,需要通过@Autowired或者@Resource注解注入;如果是非0的,可以不提供这两个注解

  • autowire_no(0): 默认的装配模式,如果注入的indexBean,没有加@Autowired或者@Resource,这时候,indexBean是无法注入的
  • autowire_name(1): 通过set方法,并且set方法的名称必须和bean的name名称一致, byName
  • autowire_type(2): 通过set方法,这里的set方法可以任意命名,按照入参类型注入bean, byType
  • autowire_constructor(3): 通过构造器注入,如果userBean中注入了indexBean,那么必须提供一个带indexBean的构造函数,否则是null

执行结果:
autowireMode是1的场景:

userBean空参构造函数
setIndexBean方法执行,这是通过类型自动注入时会执行的代码

autowireMode是2的场景:

userBean空参构造函数
setIndexBean方法执行,这是通过类型自动注入时会执行的代码
setIndexBean123方法注入属性

autowireMode是3的这种场景:

userBean带参构造函数

至于autowireMode是3的这种场景,这篇笔记中暂时不记录,因为这里涉及到第二个后置处理器,推断构造函数的逻辑,这里我还没有彻底搞明白,所以这篇笔记暂时不记录,后面待学习了第二个后置处理器的执行逻辑之后,一起记录

二、源码

2.1 结论

我们之前有说过,第五个后置处理器是判断是否需要属性注入的,第六个后置处理器是完成属性注入的,这里需要说明的是:对于手动注入(@Autowired、@Resource),是在第六个后置处理器完成属性注入的
但是对于自动注入(autwireMode为1、2),这种,
1、是在第五个后置处理器和第六个后置处理器之间的一块代码中,完成要注入属性的判断
2、然后在第六个后置处理器执行完成之后,通过method.invoke()完成属性注入,也就是调用程序员提供的set方法
3、对于自动注入的方式,在第六个后置处理器是不会做任务处理的,在第六个后置处理器后面有一行重要代码,是完成method.invoke()方法的调用的

第五个后置处理器
org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation

第六个后置处理器
org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor#postProcessPropertyValues

2.2 查找当前bean需要注入哪些bean

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean

对于自动注入:autowire_name和autowire_type都是在populateBean这个方法中完成的

/**
 * 首先解释一个名词:@Autowired和@Resource,我们通常称之为自动注入,@Autowired注解先根据name找,再根据type找,这样说,其实不严谨
 * 我们姑且先认为@Autowired和@Resource是手动注入,并且这里的先根据name,再根据type找的这个说发,会在后面进行解释
 * 我们认为将AutowireMode注入模型设置为AUTOWIRE_BY_NAME/AUTOWIRE_BY_TYPE/AUTOWIRE_CONSTRUCTOR的为自动注入
 *
 * 下面代码开始,就是完成注入的,spring在这里完成注入,可以大致分为两个类型:
 * 自动注入:
 *	下面的newPvs是根据自动注入模型找到的要注入的属性,然后再下面(applyPropertyValues(beanName, mbd, bw, pvs);),会进行统一的注入
 *
 *
 * 手动注入:
 * 	在上面一行代码中,获取到程序员手动在代码中声明的要注入的属性
 * 	ac.getBeanDefinition("ccc").getPropertyValues().add("name","mpyTest"); 这种就是在代码中手动声明要注入的属性
 *	在后置处理器中,根据@Autowired注解或者@Resource注解,获取到要注入的属性
 *
 * 	对于手动注入,我们所说的byType并不是从单实例池中根据类型查找的,而是从beanDefinitionMap中查找的
 *
 *
 * spring默认的注入模型是 AUTOWIRE_NO
 *
 */
if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
    
    
	MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
	// Add property values based on autowire by name if applicable.
	if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME) {
    
    
		autowireByName(beanName, mbd, bw, newPvs);
	}
	// Add property values based on autowire by type if applicable.
	//这里是判断当前bean需要注入哪些属性的
	if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
    
    
		autowireByType(beanName, mbd, bw, newPvs);
	}
	pvs = newPvs;
}

提前说明:上面这块代码,只是找到要注入的bean,完成属性注入是在下面一块代码,后面会单独介绍,这是populateBean()中的部分代码,下面我们分别看下autowire_name和autowire_type不同的查找注入bean的逻辑

2.3 autowire_name

所以:对于注入模式是name的,我们要着重看autowireByName()这个方法

protected void autowireByName(
		String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
    
    

	/**
	 * 这里的propertyNames就是获取到所有的set方法对应的方法名,底层是通过writeMethod来判断的
	 * 下面的containsBean(propertyName)是来判断获取到的beanName是否存在于单实例池中或者beanDefinitionMap中
	 *
	 * 如果自动注入模型是autowire_byname,但是提供的set方法没有参数,是空参,那这里是获取不到的;这里应该和Java的内省机制有关系
	 */
	String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
	for (String propertyName : propertyNames) {
    
    
		/**
		 * 在截取到所有set方法对应的beanName之后,会判断一下beanName是否存在于单实例池中或者beanDefinitionMap中,
		 * 如果存在,就调用getBean(),从单实例池中获取bean或者初始化bean
		 * 如果不存在,就无需注入
		 */
		if (containsBean(propertyName)) {
    
    
			/**
			 * getBean()这个方法我个人感觉就可以说明autowire_name是根据名字来注入属性的,这里是真正的从单实例池中查询对象的
			 * 这里的propertyName就是set方法对应的name
			 *
			 * 获取到bean之后,会存入到pvs中,在后面进行是注入的时候,会用到pvs
			 */
			Object bean = getBean(propertyName);
			pvs.add(propertyName, bean);
			registerDependentBean(propertyName, beanName);
		}
	}
}

unsatisfiedNonSimpleProperties()这个方法,是找到当前bean中所有的set方法,然后过滤出来,程序员提供的set方法

protected String[] unsatisfiedNonSimpleProperties(AbstractBeanDefinition mbd, BeanWrapper bw) {
    
    
	Set<String> result = new TreeSet<>();
	PropertyValues pvs = mbd.getPropertyValues();
	/**
	 * 获取到当前bean中所以设置属性的方法和获取属性的方法:就是getXXX,和setXXX
	 * 在下面这行代码执行的时候,有一个很重要的概念:设置当前方法的parameterType类型,我觉得可以理解为当前方法的入参对应的类型
	 * org.springframework.core.MethodParameter#getParameterType(); 在这个方法中设置的
	 */
	PropertyDescriptor[] pds = bw.getPropertyDescriptors();
	for (PropertyDescriptor pd : pds) {
    
    
		//判断哪些属性是不需要自动注入的;在Java中,方法可以分为读方法(readMethod)和写方法(writeMethod)
		if (pd.getWriteMethod() != null && !isExcludedFromDependencyCheck(pd) && !pvs.contains(pd.getName()) &&
				!BeanUtils.isSimpleProperty(pd.getPropertyType())) {
    
    
			result.add(pd.getName());
		}
	}
	return StringUtils.toStringArray(result);
}

这个方法是autowire_type和autowire_name都会调用的一个逻辑,这个方法中的一些判断条件我还没有搞明白,但是这个方法执行完成之后,会获取到程序员提供的set方法对应的方法名
在这里插入图片描述

我们接着来看获取到程序员提供的所有set方法对应的方法名之后做的操作
1、containsBean(propertyName); 在该方法中,会把方法名作为beanName,从bean单实例池或者beanDefinitionMap中判断,该beanName是否存在,如果存在,返回true,返回false,表示无需进行注入
2、如果当前set方法名对应的beanName存在于单实例池中,就从单实例池中获取或者初始化,getBean()就是进行初始化的逻辑
3、然后将要注入的属性,放入到MutablePropertyValues pvs这个属性中,在后面执行method.invoke的时候,就和这个属性有关系

2.4 autowire_type

对于这种方式,我们要着重看的是2.2中的autowireByType(beanName, mbd, bw, newPvs);方法

protected void autowireByType(
		String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
    
    

	TypeConverter converter = getCustomTypeConverter();
	if (converter == null) {
    
    
		converter = bw;
	}

	Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
	/**
	 * 这行代码是获取到当前bean中所有的writeMethod方法,个人理解,就是set方法对应的集合
	 * 根据类型自动注入的时候,会获取到set方法的参数
	 */
	String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
	for (String propertyName : propertyNames) {
    
    
		try {
    
    
			PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
			// Don't try autowiring by type for type Object: never makes sense,
			// even if it technically is a unsatisfied, non-simple property.
			if (Object.class != pd.getPropertyType()) {
    
    
				MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
				// Do not allow eager init for type matching in case of a prioritized post-processor.
				boolean eager = !PriorityOrdered.class.isInstance(bw.getWrappedInstance());
				/**
				 * 在这里的desc对象中,有一个parameterTypes数组,存储的是当前方法的入参对应的bean类型,
				 * 对于autowire_type这种模式,入参就是需要注入的类型
				 */
				DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
				/** 
				 * 这个方法中,会根据beanName去加载;这里的desc,就是bean中的set方法名,比如 setIndexBean123
				 * 这个方法返回的就是根据type找到的要注入的bean,下面要调用的这个方法,在@Autowired注解中也会调用
				 */
				Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
				if (autowiredArgument != null) {
    
    
					pvs.add(propertyName, autowiredArgument);
				}
				for (String autowiredBeanName : autowiredBeanNames) {
    
    
					registerDependentBean(autowiredBeanName, beanName);
					if (logger.isDebugEnabled()) {
    
    
						logger.debug("Autowiring by type from bean name '" + beanName + "' via property '" +
								propertyName + "' to bean named '" + autowiredBeanName + "'");
					}
				}
				autowiredBeanNames.clear();
			}
		}
		catch (BeansException ex) {
    
    
			throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);
		}
	}
}

这里的方法,会看到,也是先获取到所有的set方法对应的方法名,然后根据方法名初始化一个PropertyDescriptor等等,关键要看的是resolveDependency()这个方法,这个方法和@Autowired注解在解析的使用,共用的一个方法

这里我其实没完全搞明白共用一块代码的作用:
我自己理解是:不管是@Autowired还是autowire_type,既然是根据类型注入,就会涉及到一个类型多个实现类的场景,终归是要解析出来一个优先级比较高的实现类的,所以就共用了一个判断逻辑

这里在找到对应的注入bean的时候,也是会放到pvs中

2.5 属性注入

在populateBean()的最后,有这么两行代码

/**
 * 在这里有一个点,需要明白:
 *  1.@Autowired和@Resource注解注入的方式我们可以理解为手动注入;通过设置AutowiredModel的形式是自动注入
 *  2.手动注入的属性,是在上面 ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); 这里完成的属性填充
 *    对于自动注入的以及程序员自己提供的待注入属性,是在下面这行代码完成的属性填充
 *    通过method.invoke()方法完成
 *  3.第三次调用后置处理器,查到的是手动注入的注解,需要注入的属性,手动注入是通过field.set()来完成的属性注入;自动注入是通过method.invoke()完成的
 */
if (pvs != null) {
    
    
	applyPropertyValues(beanName, mbd, bw, pvs);
}
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyPropertyValues
	org.springframework.beans.PropertyAccessor#setPropertyValues(org.springframework.beans.PropertyValues)
		org.springframework.beans.AbstractPropertyAccessor#setPropertyValue(org.springframework.beans.PropertyValue)
			org.springframework.beans.AbstractNestablePropertyAccessor#setPropertyValue(org.springframework.beans.AbstractNestablePropertyAccessor.PropertyTokenHolder, org.springframework.beans.PropertyValue)
				org.springframework.beans.AbstractNestablePropertyAccessor#processLocalProperty
					org.springframework.beans.AbstractNestablePropertyAccessor.PropertyHandler#setValue
						

最终会通过method.invoke()去调用对应的set方法完成属性注入

三、结论

对于autowire_name:
1、会根据bean中提供的所有set方法,找到对应的方法名,比如:我提供了setIndexBean123();setIndexBean();
2、spring会获取到indexBean123,和indexBean这两个属性
3、获取到这两个属性之后,从单实例池中或者beanDefinitionMap中判断beanName是否存在
4、如果存在,就尝试从单实例池中获取或者初始化该bean,然后将要注入的beanName和对应的bean存入到一个对象中:MutablePropertyValues
5、在调用完第六个后置处理器之后,会通过method.invoke()完成属性注入

对于autowire_type:
1、会根据bean中提供的set方法,找到方法对应的方法名,比如:我提供了setIndexBean123();setIndexBean();
2、spring会获取到indexBean123,和indexBean这两个属性
3、然后会根据方法对应的入参类型,这里是和autowire_name的区别,这里是根据方法的入参,从beanDefinitionMap中,依次去判断要注入的属性是否存在
4、如果存在,就尝试从单实例池中获取或者初始化该bean,然后将要注入的beanName和对应的bean存入到一个对象中:MutablePropertyValues
5、在调用完第六个后置处理器之后,会通过method.invoke()完成属性注入

所以:
对于手动注入(@Autowired、@Resource),是在第六个后置处理器中完成属性注入的,通过field.set
对于自动注入,是通过method.invoke()完成属性注入,也就是执行对应的set方法,对于自动注入这种方式,在第六个后置处理器执行的时候,是不会做任何处理的

猜你喜欢

转载自blog.csdn.net/CPLASF_/article/details/109776990