文本已收录至个人GitHub仓库,欢迎Star:github.com/bin39232820…
种一棵树最好的时间是十年前,其次是如今
我知道不少人不玩qq了,可是怀旧一下,欢迎加入六脉神剑Java菜鸟学习群,群聊号码:549684836 鼓励你们在技术的路上写博客html
这篇文章 我是参考子路老师的,它讲的Spring真心不错,我写文章就是写把它的知识吸取变成本身的,由于本身可能仍是很菜,只能吸收别人的经验了。java
好比提到spring的自动注入做为一个java程序员确定自信无比了解;可是笔者要说的自动注入可能会和你理解有很大出入。 首先搞明白什么是自动注入,自动注入也能够叫作自动装配(springboot也有一个自动装配可是我认为翻译的不够准确,springboot的应该叫作自动配置和这里说的自动注入是两回事,笔者不是什么大牛或者权威;因此读者若是你坚持认为springboot也叫自动装配那也无可厚非,只是在这篇文章里面所谓的自动注入就是自动装配,关于springboot的自动配置我之后更新文章再来讲); 自动注入须要相对于手动装配来讲;在spring应用程序当中假设你的A类依赖了B类,须要在A类当中提供一个B类的属性,再加上setter,继而在xml当中配置、描述一下这两个类之间的依赖关系。若是作完当容器初始化过程当中会实例化A,在实例化A的过程当中会填充属性,因为在xml中已经配置、描述好二者的关系,故而spring会把B给A装配上;这种由程序员本身配置、描述好依赖关系的写法叫作手动装配;看个例子吧;git
package com.luban.app;
public class A {
B b;
public void setB(B b) {
this.b = b;
}
}
package com.luban.app;
public class B {
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="a" class="com.luban.app.A" >
<!-- 由程序员手动指定的依赖关系 称为手动装配-->
<property name="b">
<ref bean="b" />
</property>
</bean>
<bean id="b" class="com.luban.app.B">
</bean>
</beans>
复制代码
上面的这种状况就是手动配置,经过xml 定义好2个类的属性关系。程序员
可是实际开发中手动装配的场景比较少(好比在缺乏源码的状况下可能会使用这种手动装配状况); 关于依赖注入的资料能够参考官网Springgithub
这一章节提到了一个很是重要的知识点,也是一个常见的spring面试题目。spring有几种依赖注入方式?那么这个问题应该怎么回答呢?面试
上面这个是我谷歌翻译的Spring的文档,英语比较菜没办法,那么咱们来回答一下上面的面试题吧spring
官网的意思是DI(依赖注入)一共有两种主要的变体(注意会考),分别是基于构造方法的依赖注入和基于setter(setXxxx(…))的依赖注入,不论是手动装配仍是自动装配都是基于这两种方式或者变体方式来的;可是这里必定要回答到主要和变体两个名词,由于有的注入方式就不是这两种,而是这两种其中一种的变体方式;好比在一个类的属性上面加@Autowired,这种方式注入属性的方式就是利用了java的反射知识,field.set(value,targetObject);关于这个我在后面的文章中对spring源码解析的时候会说明@Autowired的原理;因此@Autowired这种注入的方式是setter注入方式的一种变体 可是这里须要说明的是所谓的setter其实和属性无关,什么意思呢?通常的setter方法会对应一个属性,可是spring的基于setter的注入方式是不须要属性的,仅仅只须要一个setter方法,下面这个例子来讲明这个问题springboot
B.java
public class B {
}
A.java
public class A {
public void setXxx(B b) {
System.out.println("spring 找到符合的setter");
System.out.println("和属性无关,甚至能够不要属性");
System.out.println("能够直接调用,这个A里面就没有任何属性");
}
}
xml配置文件
<bean id="a" class="com.luban.app.A" >
<!-- 由程序员手动指定的依赖关系 称为手动装配-->
<property name="xxx">
<ref bean="b" />
</property>
</bean>
<bean id="b" class="com.luban.app.B">
</bean>
复制代码
如上面的结构 虽然A里面注入了B,可是A里面并无B的属性bash
运行上面的代码能够看到spring也会调用这个setXxx方法,若是仔细观察调用栈能够看到这个方法是在spring容器初始化的时候实例化A,完成A的注入功能时候调用过来的,下图是笔者运行结果的截图app
可能有的读者会认为上面的xml配置文件中已经手动装配了B给A,确定会调用setXxx方法,其实否则,便是我使用自动装配也仍是会调用的(关于什么是自动装配,下文会详细介绍),由于前文说过,注入方式与手动注入、自动注入无关。笔者改一下代码,把A的注入模型改为自动注入来看看结果
<!-- 程序员不指定装配的具体参数,容器自动查询后装配-->
<bean id="a" class="com.luban.app.A" autowire="byType">
</bean>
<bean id="b" class="com.luban.app.B">
</bean>
复制代码
把代码改为上面这样自动装配结果是同样的,A类当中的setXxx方法仍是会调用,不须要提供任何属性,至于原理笔者后面更新到spring源码的时候再来详细说道;可能有读者会说若是使用注解呢?好比以下代码
A.java
@Component
public class A {
@Autowired
B b;
public void setB(B b) {
System.out.println("若是你使用注解,这个setter变得毫无心义");
System.out.println("这个方法甚至都不会调用");
System.out.println("由于前文说过这种方法用的是field.set");
}
}
B.java
import org.springframework.stereotype.Component;
@Component
public class B {
}
复制代码
上面代码一样会注入b,可是是调用field.set去完成注入的,不是经过setter,固然再次说明一下这是setter的一种变体;上面代码直到A完成注入B后setter方法也不会调用;
重点来了,要考。笔者看过不少资料说@Autowired也算自动装配,关于这点笔者不敢苟同,在一个属性上面加@Autowired注解应该属于手动装配,我会经过大量源码和例子来证实笔者的这个理论。 之因此会有人把@Autowired也理解为自动装配的缘由是由于bytype引发的,由于spring官网有说明自动装配有四种模型分表是no、bytype、byname、constructor;参考官网资料,而如今流行着这么一种说法:@Autowired就是经过bytype来完成注入的。若是你也认为这种说法成立,那么就是默认了@Autowired是自动装配且装配模型是bytype。笔者见过不少程序员和不少资料都支持这种说法,其实严格意义上来说这句话大错特错,甚至有误人子弟的嫌疑。由于bytype仅仅是一种自动注入模型而已,这种模型有其固有的技术手段,而@Autowired是一个注解,这个注解会被spring的后置处理器解析,和处理bytype不是同一回事。若是须要讲清楚他们的区别和证实@Autowired不是自动装配则首先要搞明白什么自动装配。笔者接下来会花必定篇幅来解释自动装配的知识,而后回过头来说他们的区别和证实@Autowired属性手动装配
若是如今已经理解了手动装配也叫手动注入,也已经理解了注入方式(setter和构造方法),那么接下来讨论自动注入或者叫自动装配;自动注入的出现是由于手动装配过于麻烦,好比某个类X当中依赖了10个其余类那么配置文件将会变的特别冗余和臃肿,spring的作法是能够为这个X类提供一种叫作自动装配的模型,无需程序员去手动配置X类的依赖关系。有读者会疑问,用注解不也是能够解决这个xml臃肿的问题?确实用注解能够解决,可是咱们如今讨论的是自动装配的问题,就不能用注解;为何不能用注解来讨论自动装配的问题呢?由于在不配置BeanFactoryPostProcessor和修改beanDefinition的状况下注解的类是不支持自动装配的(关于BeanFactoryPostProcessor和beanDefinition的知识笔者之后有时间更新);这也是证实@Autowired默认不是自动装配的一个证据,那又如何证实注解类是默认不支持自动装配呢?下文我会解释一个注解类默认是不支持自动装配的。也就是说若是讨论自动装配最好是用xml形式来配置spring容器才会有意义;固然读者若是对spring比较精通其实经过修改注解类的beanDefinition来讲明自动装配的问题,鉴于大部分读者对spring不精通故而笔者仍是用xml来讨论吧;好比下面这个例子
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="byType">
<!--default-autowire="byType"-->
<!-- 程序员不指定装配的具体参数,容器自动查询后装配-->
<!--固然除了这种写法还能够直接在bean标签上为特定类指定自动装配模型-->
<bean id="a" class="com.luban.app.A">
</bean>
<bean id="b" class="com.luban.app.B">
</bean>
</beans>
A.java
public class A {
B b;
public void setB(B b) {
System.out.println("在不考虑注解的状况下");
System.out.println("若是配置了自动装配则不须要手动配置");
}
}
B.java
public class B {
}
复制代码
上面代码运行起来,A能注入B,可是在xml配置文件中并无去手动维护、描述他们之间的依赖关系,而是在xml的根标签上面写了一行default-autowire=“byType”,其实关于自动注入的歧义或者被人误解的地方就是这个default-autowire="byType"引发的;那么这行代码表示什么意思呢?表示所在配置在当前xml当中的bean都以bytype这种模式自动装配(若是没有特殊指定,由于bean还能够单独配置装配模式的);这须要注意笔者的措辞,笔者说的bytype这自动装配模式,是一种模式,这个不是笔者信口开河,由于在官网文档里面spring也是这么定义的
Autowiring mode 笔者姑且翻译为自动注入模型吧,若是有2十一、雅思读者能够给读者留言更合适的翻译 那为何要锱铢在这个名词上呢?笔者以为真的很是重要,不少初学spring的程序员都是粗学spring,忽略甚至混淆了不少关键的名词定义,致使不少观念根深蒂固后面想深刻学习spring源码的时候就比较困难 这个名字叫作自动注入模型和前面提到的依赖注入方式(setter和构造方法)是两回事,简而言之:依赖注入是一个过程,主要经过setter和构造方法以及一些变体的方式完成把对象依赖、或者填充上的这个过程叫作依赖注入,无论手动装配仍是自动装配都有这个过程;而自动装配模型是一种完成自动装配依赖的手段体现,每一种模型都使用了不一样的技术去查找和填充bean;而从spring官网上面能够看到spring只提出了4中自动装配模型(严格意义上是三种、由于第一种是no,表示不使用自动装配、使用),这四个模型分别用一个整形来表示,存在spring的beanDefinition当中,任何一个类默认是no这个装配模型,也就是一个被注解的类默认的装配模型是no也就是手动装配;其中no用0来表示;bytype用2来表示;若是某个类X,假设X的bean对应的beanDefinition当中的autowireMode=2则表示这个类X的自动装配模型为bytype;若是autowireMode=1则表示为byname装配模型;须要注意的是官网上面说的四种注入模型其中并无咱们熟悉的@Autowired,这也再一次说明@Autowired不是自动装配;可能有读者会提出假设我在A类的某个属性上面加上@Autowired以后这个A类就会不会成了自动装配呢?@Autowired是否是会改变这个类A当中的autowireMode呢?咱们能够写一个例子来证实一下
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="byType">
<bean id="a" class="com.luban.app.A">
</bean>
<bean id="b" class="com.luban.app.B">
</bean>
</beans>
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如何处理的
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="byType">
<!--自动装配-->
<bean id="a" class="com.luban.app.A">
</bean>
<bean id="b" class="com.luban.app.B">
</bean>
</beans>
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方法,这个方法主要就是完成属性填充的,也就是你们说的注入
可是若是当咱们使用@Autowired注解去注入一个属性的时候spring在完成属性注入的过程当中和自动注入(byname、bytype)的过程不一样,spring注解跳过了那个判断,由于不成立,而是用后面的代码去完成属性注入;这也是能说明@Autowired不是自动装配的证据、更是直接打脸@Autowired是先bytype的这种说法
之后若是再听到@Autowried是bytype请你纠正他,bytype是一种自动注入模型;@Autowried是一个注解,两我的没有关系,一点关系都没有;@Autowried讲道理算是手动装配 我以为只能说@Autowried是自动注入,手动装配,由于它的装配类型为0
Spring的东西仍是不少,你们慢慢学吧
好了各位,以上就是这篇文章的所有内容了,能看到这里的人呀,都是真粉。
创做不易,各位的支持和承认,就是我创做的最大动力,咱们下篇文章见
六脉神剑 | 文 【原创】若是本篇博客有任何错误,请批评指教,不胜感激 !