在前面的博客中,介绍了@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方法,对于自动注入这种方式,在第六个后置处理器执行的时候,是不会做任何处理的