引言
如果把一套代码比作秋名山430KM/H高速行驶的布加迪威龙16.4,毫不夸张的说Spring就是整个8.0升W16缸4涡轮增压的发动机的启动器,每每拾起Spring源码开始读,耳畔就布满了汹涌翻滚的声浪,像蓄势待发的猛兽不断冲击我骚动的内心。
看到这大家是不是跟我一样急需想轰一脚油门了。
须知
spring-framework 中逻辑都是串联交织的,一个节点啃完了,中间必定新冒出一个节点。在源码里面,无论你是英语6级还是8级,统统都要忘记,因为英文片面的解释不足以证明问题反而会对造成困扰,就像一杯热气腾腾的咖啡,只有品的醇香才能敲击你的味蕾。这里我的宗旨就是以最少的语言解释更多的事情。
@Autowired
@Autowired大家已经不能再熟悉了吧,看大家的理解是不是跟我一样。@Autowired属于注入方式(Setter、Constructor、@Autowired)的其中一种,如果Bean在 单例池 中,可以通过@Autowired将某一个Bean注入(循环依赖 又是另一回事了),但是@Autowired跟自动注入模型有关系吗?
我们做一个测试:
//配置类
@ComponentScan("com.practice")
//@ImportResource("SpringConfig.xml")
public class AppConfig {
}
@Component
public class DavisService {
public DavisService() {
System.out.println("davis constructor");
}
public void downtown() {
System.out.println("davis downtown");
}
}
@Component
public class JamesService {
@Autowired
private DavisService davisService;
public JamesService() {
System.out.println("james constructor");
}
public void win() {
davisService.downtown();
System.out.println("Los has win the Boston");
}
}
public class ContextTest {
public static void main(String[] args) {
ApplicationContext app
= new AnnotationConfigApplicationContext(AppConfig.class);
app.getBean(JamesService.class).win();
}
}
描述: 将两个service添加到 容器 当中,jamesService注入davisService,也就是说jamesService依赖davisService。
执行结果:
davis constructor
james constructor
davis downtown
Los has win the Boston
简单描述: 实例化顺序spring有他自己的规则,但是先初始化davis 和先初始化james 是有区别的。jamesService注入davisService,如果在初始化jamesService注入davisService当中发现davisService没有被实例化,那么就会去创建bean,然后注入。如果davisService已经在 单例池,那么直接注入。帮助大家有个印象,不做深入。
我们来一波小操作看一下自动注入模型:
@Component
public class DoMyProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
GenericBeanDefinition beanDefinition =
(GenericBeanDefinition) beanFactory.getBeanDefinition("jamesService");
System.out.println("自动注入模型: " + beanDefinition.getAutowireMode());
}
}
描述: 将自定义后置处理器加入 容器 当中,Spring会专门处理,Spring的后置处理器非常多,包括参与扫描、beanDefinition的实例化、Bean的生命周期、属性注入、循环依赖、AOP等等。
运行看打印结果:
自动注入模型: 0
davis constructor
james constructor
davis downtown
Los has win the Boston
源码中对自动注入模型的标识:
/**
* Constant that indicates no externally defined autowiring. Note that
* BeanFactoryAware etc and annotation-driven injection will still be applied.
* @see #createBean
* @see #autowire
* @see #autowireBeanProperties
*/
int AUTOWIRE_NO = 0;
/**
* Constant that indicates autowiring bean properties by name
* (applying to all bean property setters).
* @see #createBean
* @see #autowire
* @see #autowireBeanProperties
*/
int AUTOWIRE_BY_NAME = 1;
/**
* Constant that indicates autowiring bean properties by type
* (applying to all bean property setters).
* @see #createBean
* @see #autowire
* @see #autowireBeanProperties
*/
int AUTOWIRE_BY_TYPE = 2;
/**
* Constant that indicates autowiring the greediest constructor that
* can be satisfied (involves resolving the appropriate constructor).
* @see #createBean
* @see #autowire
*/
int AUTOWIRE_CONSTRUCTOR = 3;
官网有提到过关于自动注入模型:
也就是说在我们的xml配置当中可以指定autowire这个bean定义标签的模型,模型具体四种:
no
描述:(默认)没有自动装配。Bean引用必须由ref标签指定,官方建议推荐使用默认,为了可观性强,项目庞大之后,就不知道谁依赖谁了。
举例:
<?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 class="com.practice.service.JamesService" id="jamesService" >
<property name="davisService" ref="davisService"/>
</bean>
<bean class="com.practice.service.DavisService" id="davisService"></bean>
</beans>
//@Component
public class JamesService {
private DavisService davisService;
public void setDavisService(DavisService davisService) {
this.davisService = davisService;
}
public JamesService() {
System.out.println("james constructor");
}
public void win() {
davisService.downtown();
System.out.println("Los has win the Boston");
}
}
//@Component
public class DavisService {
public DavisService() {
System.out.println("davis constructor");
}
public void downtown() {
System.out.println("davis downtown");
}
}
执行结果:
自动注入模型: 0
james constructor
davis constructor
davis downtown
Los has win the Boston
结论: 采用默认方式,不自动装配,自动注入模型标识为0,证明 @Autowired 没有定义自动装配,自动装配模型为 no。
byName
> 描述: 通过属性名自动装配。Spring按照bean的名字找,setName中Name和xml中id值一定不要写错,否则Spring找不到也就无法注入(空指针异常)。
举例:
执行结果:
自动注入模型: 1
james constructor
davis constructor
davis downtown
Los has win the Boston
结论: byName自动注入模型比较苛刻,注意Name不要写错。
byType
描述: 容器中存有该类型得bean只有一个,则允许获取属性类型,如果存在多个相同类型的bean,就会报异常。
举例:
public interface NBAService {
}
public class DavisService implements NBAService {
public DavisService() {
System.out.println("davis constructor");
}
public void downtown() {
System.out.println("davis downtown");
}
}
//@Component
public class JamesService {
private NBAService nbaService;
public void setNbaService(NBAService nbaService) {
this.nbaService = nbaService;
}
public JamesService() {
System.out.println("james constructor");
}
public void win() {
// davisService.downtown();
System.out.println("Los has win the Boston");
}
}
执行结果:
自动注入模型: 2
james constructor
davis constructor
Los has win the Boston
如果相同类型有多个实例呢?
public class IrvingService implements NBAService {
public IrvingService() {
System.out.println("irving constructor");
}
}
直接报错:
constructor
描述: 适用于构造函数参数。
举例说明:
//@Component
public class JamesService {
private DavisService davisService;
public JamesService(DavisService davisService) {
System.out.println("james constructor");
this.davisService = davisService;
}
public void win() {
// davisService.downtown();
System.out.println("Los has win the Boston");
}
}
打印结果:
自动注入模型: 3
davis constructor
james constructor
Los has win the Boston
如果存在相同类型多个实例,Spring也会报错:
//@Component
public class JamesService {
private NBAService nbaService;
public JamesService(NBAService nbaService) {
System.out.println("james constructor");
this.nbaService = nbaService;
}
public void win() {
// davisService.downtown();
System.out.println("Los has win the Boston");
}
}
执行结果:
源码解析
根据 constructor 自动注入模型,我们做一下源码分析:
在BeanDefinitionParserDelegate这个类当中,解析标签,获取属性值,这些属性描述了一个类的状态,因为此时还没有被实例化。
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
@Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
}
//单例还是原型
else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
}
else if (containingBean != null) {
// Take default from containing bean in case of an inner bean definition.
bd.setScope(containingBean.getScope());
}
if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
}
//是否懒加载
String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
if (isDefaultValue(lazyInit)) {
lazyInit = this.defaults.getLazyInit();
}
bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
//设置自动注入模型
String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
bd.setAutowireMode(getAutowireMode(autowire));
//......
//省略了N多代码
return bd;
}
上面主要是实例化jamesService之前,对beanDefinition的一些属性设置操作,在实例化过程中又是怎么做的?
AbstractAutowireCapableBeanFactory 主要完成了推断构造方法,属性注入,循环依赖,AOP。我们看一下关键步骤:
总结: 将xml配置文件中的节点进行解析,获取属性值,将自动注入模型设置到BeanDefinition当中,代表对类的一种描述,当该类在实例化的时候获取自动注入模型标识,判断数值是否为3,如果为3,jamesService将以构造器形式注入davisService。
补充:
如果不是constructor注入模型呢,该如何判断?
我们以byType为例:
往下走:
获取set方法,判断Setter语法是否正确,以及对set方法参数的处理。
最后还是通过大家熟悉的 method.invoke((Object obj, Object… args) 方法执行完成:
这时我们再回头看AbstractAutowireCapableBeanFactory 的populateBean 方法:
到这里完成自动注入模型byType属性注入。
总结: 如果说道格 Doug Lea的Lock是细节简化艺术,那么Spring FrameWork就是思维逻辑艺术,或者说两者完美融合,就像一杯热气腾腾的咖啡,需要通过精细研磨才能发挥出他最大的香气。