我是子路,一个靠Java吃饭的男人。
接上篇文章:spring源码系列——毁三观的spring自动注入(上)
正文
我们可以写一个例子来证明一下:
xml配置了A 和B 都是自动装配模型为bytype讲道理要实现autowireMode=2
A.java
public class A {
}
B.java
public class B {
}
提供一个后置处理器来获取A的自动装配模型
ZiluBeanFactoryPostprocessor.java
@Component
public class ZiluBeanFactoryPostprocessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
GenericBeanDefinition a = (GenericBeanDefinition)
beanFactory.getBeanDefinition("a");
//打印A 的注入模型
System.out.println("a mode="+a.getAutowireMode());
}
}
讲道理由于是bytype所以应该打印出来2; 结果如图:
如果笔者把注入模型改成byname则结果应该会改变
接下来笔者验证一个通过注解配置的类加上@Autowried后的注入模型的值
可以看到结果为0,说明这个A类不是自动装配,其实这已经能证明@Autowried不是自动装配了,但是还有更直接的证据证明他不是自动装配,就是通过spring源码当中处理@Autowried的代码可以看出,首先我们写一个bytype的例子看看spring如何处理的:
A.java
public class A {
B b;
/**
* 如果是自动装配需要提供setter
* 或者提供构造方法
*/
public void setB(B b) {
this.b = b;
}
}
B.java
public class B {
}
上面代码运行起来,调试spring源码当中的org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean方法,这个方法主要就是完成属性填充的,也就是大家说的注入
可以看上图1399行有一个判断,判断当前类的注入模式是否bynane或者bytype如果是其中一种则会进入1401行代码执行自动装配的逻辑;因为当前代码我在xml当中配置了自动注入模型为bytype所以这里一定会进入,从上图debug的结果我们可以得知确实也进入了1401行,那我们再看看@Autowried到底是否是一样的呢?
好吧作为良心笔者特意录了一个gif:
从上面那个笔者良心录制的gif可以看到当我们使用@Autowired注解去注入一个属性的时候spring在完成属性注入的过程中和自动注入(byname、bytype)的过程不同,spring注解跳过了那个判断,因为不成立,而是用后面的代码去完成属性注入;这也是能说明@Autowired不是自动装配的证据、更是直接打脸@Autowired是先bytype的这种说法;当然除了这个证据还有更加直接的证据,先看代码:
@Component
public class A {
B b;
public A(B b){
this.b=b;
System.out.println(b);
System.out.println("这个b能正常打印,那这算不算自动装配呢?");
System.out.println("看起来像是constructor这种自动装配模型");
System.out.println("但是实际呢?我们通过源码来说明");
System.out.println("这个构造方法没有加任何注解");
System.out.println("这里主要来说明,一个注解类是不是自动装配");
}
}
说明一下这个A类的构造方法注入的B是可以注入的,这样算不算注解类的自动注入呢?看起来像,其实本质上他用的也是自动注入的代码实现的,但是笔者还是认为他不是;为什么呢?我们先来分析一下这个b能被注入的原因是什么,同样给一个gif来说明:
其实最关键的就是这行代码:
if (ctors != null || mbd.getResolvedAutowireMode() ==
AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { return autowireConstructor(beanName, mbd, ctors, args);
}
这里有个判断 首先判断 ctors != null 然后判断类的自动注入模型是不是等于AUTOWIRE_CONSTRUCTOR也是3;分两种情况来分析:
第一种情况:假设你指定了类的自动注入模型为constructor那么这个if一定成立因为他是||判断只要一个成立整个if成立;很显然我们这里不是这种情况,笔者并没有指定A类的自动注入模型为3,上面的那个gif里面我给读者展示了,后面那个判断不成立,因为mbd.getResolvedAutowireMode()=0;再一次说明了一个注解类默认的自动注入模型为no也就是autowireMode=0;
第二种情况:没有指定类的自动注入模型,笔者代码例子就是这种情况,那么spring会首先推断出构造方法,笔者A里面一个带参数的构造方法,所以再进行第一个判断ctors!= null的时候就已经成立了,于是也会进入。
结论:在一个注解类里面提供了一个构造方法之所以能达到和自动注入的效果一样并不是因为这种方式就是自动装配,而是因为spring源码当中做了判断;使这种情况下调用的代码和自动装配调用的逻辑一下。但是有的读者会说那这样也能算自动装配啊,当然如果读者一定这么认为那么也无可厚非;仁者见仁智者见智。
那哔哩哔哩说了这么多@Autowried到底和bytype有什么关系呢?为什么有资料会把他们联系在一起呢?
首先bytype是一种自动注入模型,spring会更加类型去查找一个符合条件的bean如果没找到则不注入,程序也不会报错;如果找到多个spring也不报错,但是也不完成注入,让这个属性等于null,比如你有个接口I,提供两个实现I1和I2都存在容器当中,然后在A类当中去注入I,并且指定自动注入模型为bytype那么这个时候会找到两个符合条件的bean,spring就迷茫了,注入哪个呢?spring哪个都不注入直接让这个属性为null而且也不出异常;但是如果找到了一个那么注入的时候会调用setter,如果没有提供setter就不会注入;
其次@Autowried是一个注解,加在属性上面spring,
spring会首先根据属性的类型去容器中找,如果没有找到在根据属性的名字找,找到了则注入,没有找到则异常,下图结果就是容器当中没有一个符合的类型和名字都不符合的则异常;
如果先根据类型找到了一个,那么直接注入,下图就是只有一个符合要求的类型能够完美注入;
如果先根据类型找到了多个,那么spring不会立马异常,而是根据名字再找去找,如果根据名字找到一个合理的则注入这个合理的,下图就是I1和I2都符合,但是spring最后却没有异常,是因为属性名字叫i1故而先类型在名字找到了合理的;
如果先根据类型找到了多个,那么spring不会立马异常,而是根据名字再找去找,如果根据名字还是没有找到那么时候spring会异常,下图就是两个i1和i2都符合,于是spring通过名字找i3也找不到,故而出异常;
以后如果再听到@Autowried是bytype请你纠正他,bytype是一种自动注入模型;@Autowried是一个注解,两个人没有关系,一点关系都没有;@Autowried讲道理算是手动装配;那么一个注解的类到底能不能开启自动装配呢?答案是可以的,但是需要你对spring比较精通,以后笔者再更新;
本文最早更新在我的博客上面,现在都会同步在头条号等自媒体平台上。转载请注明出处,欢迎各位关注!