Spring有两大核心,IoC和AOP。之前有用过Spring,但对于源码还不够了解,因此专门写一篇博文来记录阅读这部分源码的过程。
什么是IoC
要阅读IoC的源码,首先要搞清楚IoC是干嘛的,IoC全称Inversion of Control ,意为控制反转,有时候也被称为DI (Dependency Injection,依赖注入)。
需要注意的是,两个概念是有区别的。控制反转是目的,依赖注入是手段。所谓控制反转,我们在Java中要使用对象的时候,通常是new一个对象出来用,控制反转就是把对象的管理交给Spring,我们只需要用。依赖注入就是需要用的时候,你只要标明你需要这个对象,由Spring帮你注入就行了。
源码分析
假设我们现在有一个AccountDao对象,其实现类为AccountDaoImpl,其中只有一个save方法
1 | //AccountDao.JAVA |
为了进行依赖注入,在Spring当中我们采用xml的配置方式,其中xmlns表示namespace,也就是命名空间,定义了这个xml文件是用来干嘛的,需要不同的功能就要引入不同的命名空间,其最基本的是beans
的定义功能,文件中的accountDao已经被指定了id
和class
:
1 |
|
我们需要一个applicationContext作为容器来获取beans
,这里使用ClassPathXmlApplicationContext
进行,当然也可以使用FileSystemXmlApplicationContext
,二者区别只是前者不需要指定绝对路径。我们明明这个类为Client,返回的容器用ApplicationContext
接收。
1 | public class Client { |
我们跳入ClassPathXmlApplicationContext
的构造方法看看
1 | //org.springframework.context.support.ClassPathXmlApplicationContext 84 |
继续往下看refresh方法:
1 | //AbstractApplicationContext 516 |
下面,我们将一个个函数来解析这个refresh函数,上面的prepareRefresh()
函数功能主要是设定active状态为true,closed状态为false,两个状态值都是AtomicBoolean。并检查xml文件中是否包含必要的值。
创建Bean容器,加载、注册Bean
refresh方法种的第二个函数:obtainFreshBeanFactory()
这个方法是IoC过程中最重要的一个函数之一,在这里初始化BeanFactory,加载Bean,注册Bean,但是这一步之后Bean初始化并未完成,因为还没有实例化Bean。
//AbstractApplicationContext 620
1 | protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { |
// AbstractRefreshableApplicationContext 124
1 |
|
我们来看看继承关系,ApplicationContext是BeanFactory的子类,但其实这里不应该这个理解,应该理解成ApplicationContext里面包含一个实例化的BeanFactory。
![image-20200512213557235](/Users/apple/Library/Application Support/typora-user-images/image-20200512213557235.png)
第二个问题,Spring为什么要选择DefaultListableBeanFactory
作为默认的BeanFactory呢,下面图中看到,BeanFactory有三个子接口,而这个DefaultListableBeanFactory
刚好把实现了三个子接口,意思是三个子接口当中的功能都被继承下来了。
![image-20200512214229003](/Users/apple/Library/Application Support/typora-user-images/image-20200512214229003.png)
Bean是如何被存在BeanFactory当中的呢,其实是通过BeanDefinition存进去的,我们来看看BeanDefinition
BeanDefinition
1 | public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement { |
继续看AbstractRefreshableApplicationContext
当中的refreshBeanFactory()
方法,还剩下两个主要的内容
1 | customizeBeanFactory(beanFactory); |
配置Bean覆盖和循环依赖 customizeBeanFactory
配置bean是否允许覆盖,是否允许循环依赖
//AbstractRefreshableApplicationContext 224
1 | protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) { |
BeanDefinition的覆盖,在配置文件定义了多个相同的id或name的时候,如果不配置允许覆盖,那么系统会报错,但是在多个配置文件中,系统不会报错,会进行覆盖。
循环覆盖,默认Spring是允许的,但是不能在构造方法种循环依赖,那么就会报错。
加载Bean loadBeanDefinitions
loadBeanDefinitions
这个方法是最重要的方法了,这个方法将配置读出来,加载各个Bean,放到BeanFactory中,读取和解析配置文件的操作在XmlBeanDefinitionReader
类中。
// AbstractXmlApplicationContext 81
1 |
|
// AbstractXmlApplicationContext 121
1 | // 两个分支,但第二个分支转换为Resource之后,会跟上面到一个函数中 |
此时的xml文件已经被解析成了一棵dom树,下面开始解析内容
DefaultBeanDefinitionDocumentReader.java:122
1 | protected void doRegisterBeanDefinitions(Element root) { |
继续往下看parseBeanDefinitions方法
1 | // DefaultBeanDefinitionDocumentReader.java 186 |
可以看到,最终要么解析default的element,要么解析custom的element
default element包括<import />
、<alias/>
、<bean />
、<beans/>
这四个
这里的四个标签之所以是 default 的,是因为它们是处于这个 namespace 下定义的:
1 | http://www.springframework.org/schema/beans |
而对于其他的标签,将进入到 delegate.parseCustomElement(element) 这个分支。如我们经常会使用到的 <mvc />
、<task />
、<context />
、<aop />
等
有了这些custom的element,也要在xml的name space中加入对应的内容,比如mvc的是
xmlns:mvc="http://www.springframework.org/schema/mvc"
同时代码中要提供对应的parser来解析,比如有MvcNamespaceHandler、TaskNamespaceHandler、ContextNamespaceHandler、AopNamespaceHandler等解析类。
继续往下看看,对于default标签的处理方法:
1 | private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { |
挑选其中比较重要的bean标签进行分析
1 | protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { |
<bean/>
标签当中包含以下内容
Property | |
---|---|
class | 类的全限定名 |
name | 可指定 id、name(用逗号、分号、空格分隔) |
scope | 作用域 |
constructor arguments | 指定构造参数 |
properties | 设置属性的值 |
autowiring mode | no(默认值)、byName、byType、 constructor |
lazy-initialization mode | 是否懒加载(如果被非懒加载的bean依赖了那么其实也就不能懒加载了) |
initialization method | bean 属性设置完成后,会调用这个方法 |
destruction method | bean 销毁后的回调方法 |
举个例子:
1 | <bean id="exampleBean" name="name1, name2, name3" class="com.javadoop.ExampleBean" |
当然,除了上述的内容外,还包含factory-bean、factory-method、<lockup-method />
(计算一个函数 返回的是null,但函数会返回声明的空类型)、<replaced-method />
、<meta />
、 <qualifier/>
这几个。
继续往下看bean的配置是如何被解析出来的:
// BeanDefinitionParserDelegate.java 404
1 | public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) { |
来看看parseBeanDefinitionElement方法是怎么创建BeanDefinition的
// BeanDefinitionParserDelegate.java 500
1 | public AbstractBeanDefinition parseBeanDefinitionElement( |
此时一个BeanDefinitionHolder就创建完成了,回到需要BeandefinitionHolder的地方
// DefaultBeanDefinitionDocumentReader.java:319
1 | protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { |
此时得到的BeanDefinitionHolder包含三部分:BeanDefinition、beanName、aliases
1 | public class BeanDefinitionHolder implements BeanMetadataElement { |
注册Bean
// BeanDefinitionReaderUtils.java:144
1 | public static void registerBeanDefinition( |
// DefaultListableBeanFactory.java:788
1 | public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) |
总结一下,到目前为止,已经初始化了BeanFactory,<bean/>
配置也转化成了 一个个beanDefinition,注册了BeanDefinition到注册中心,发送了注册事件。
BeanFactory实例化完成后
到这里,我们回到refresh方法
1 | //AbstractApplicationContext 516 |
我们看看prepareBeanFactory()方法
// AbstractApplicationContext.java:634
1 | protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { |
初始化所有的 singleton beans
回到refresh函数中,上面的一系列处理postProcessor和awareProcessor的内容处理完之后,就到了 finishBeanFactoryInitialization(beanFactory)方法,这个方法就是真正的实例化singleton bean了
// AbstractApplicationContext.java:841
1 | protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { |
继续preInstantiateSingletons函数,又回到了DefaultListableBeanFactory方法
//DefaultListableBeanFactory 726
1 | public void preInstantiateSingletons() throws BeansException { |
继续跳入上面的getBean方法
getBean方法
1 | // AbstractBeanFactory 198 |
继续看createBean方法,参数包括三个如下:
1 | protected abstract Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException; |
从上面的getBean方法,args参数是构造函数的参数,但在这个函数调用的时候为空。
继续看具体的createBean方法
1 | protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) |
创建 Bean
继续看doCreateBean方法
1 | protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) |
doCreateBean当中有两个重要方法createBeanInstance
、populateBean
创建bean实例
继续看createBeanInstance
方法
1 | protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, { Object[] args) |
选择无参构造函数,跳入
1 | // AbstractAutowireCapableBeanFactory.java:1211 |
继续看实例化的函数
// SimpleInstantiationStrategy.java:61
1 | public Object instantiate(RootBeanDefinition bd, { String beanName, BeanFactory owner) |
bean属性注入
看populateBean方法
// AbstractAutowireCapableBeanFactory.java:1277
1 | protected void populateBean(String beanName, RootBeanDefinition mbd, { BeanWrapper bw) |
initializeBean
populateBean后面还有一个initializeBean方法,我们进入initializeBean方法
// AbstractAutowireCapableBeanFactory.java:1678
这个方法里面是各种回调
1 | protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) { |
lookup-method
我们来看一下 Spring Reference 中提供的一个例子:
1 |
|
xml 配置 :
1 | <!-- a stateful bean deployed as a prototype (non-singleton) --> |
虽然返回的是getPrototypeBean方法返回值是null,但因为配置了lookup-method,因此最终返回的结果也是MyPrototypeBean实例
也可以用注解的方式进行:
1 |
|
replaced-method
记住它的功能,就是替换掉 bean 中的一些方法。用一个实现org.springframework.beans.factory.support.MethodReplacer的类,实现其中的reimplement
方法,然后配置好,就可以覆盖某个类当中某个方法的返回值
1 | public class MyValueCalculator { |
实现MethodReplacer接口
1 | public class ReplacementComputeValue implements org.springframework.beans.factory.support.MethodReplacer { |
配置xml如下:
1 | <bean id="myValueCalculator" class="x.y.z.MyValueCalculator"> |
Bean 继承
在初始化 Bean 的地方,我们说过了这个:
1 | RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); |
这里涉及到的就是 <bean parent="" />
中的 parent 属性,我们来看看 Spring 中是用这个来干什么的。
首先,我们要明白,这里的继承和 java 语法中的继承没有任何关系,不过思路是相通的。child bean 会继承 parent bean 的所有配置,也可以覆盖一些配置,当然也可以新增额外的配置。
Spring 中提供了继承自 AbstractBeanDefinition 的 ChildBeanDefinition
来表示 child bean。
看如下一个例子:
1 | <bean id="inheritedTestBean" abstract="true" class="org.springframework.beans.TestBean"> |
parent bean 设置了 abstract="true"
所以它不会被实例化,child bean 继承了 parent bean 的两个属性,但是对 name 属性进行了覆写。
child bean 会继承 scope、构造器参数值、属性值、init-method、destroy-method 等等。
当然,我不是说 parent bean 中的 abstract = true 在这里是必须的,只是说如果加上了以后 Spring 在实例化 singleton beans 的时候会忽略这个 bean。
比如下面这个极端 parent bean,它没有指定 class,所以毫无疑问,这个 bean 的作用就是用来充当模板用的 parent bean,此处就必须加上 abstract = true。
1 | <bean id="inheritedTestBeanWithoutClass" abstract="true"> |
ConversionService
将前端传过来的参数和后端的controller方法绑定
前端如果穿字符串,后端很容易转化为一个String或者Integer,但如果需要一个枚举值,或者是Date之类的,就要用ConversionService来转换
1 | <bean id="conversionService" |
用converter接口实现更简单
1 | public class StringToDateConverter implements Converter<String, Date> { |