Spring启动说明,包括解析、加载,Aop处理,事务处理
前言
Spring最关键的部分,也就是解析并加载Bean的处理了,本篇文章以个人的角度进行说明Spring的Bean都是如何处理的。期间有些部分会进行跳过,可能自己还没理解到,也或者可能觉得不太重要吧。
本篇文章以解析Xml的方式加载Spring的Bean为前提,基于ClassPathXmlApplicationContext类开始进行介绍解析。现在最流行的通过注解的方式,暂时本篇文章不介绍,后续有机会再进行介绍。
1. 加载xml配置文件
1
|
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
|
通过ClassPathXmlApplicationContext加载xml时,一般主要以这个构造函数为入口。主要关注setConfigLocations和refresh这2个方法。
setConfigLocations方法主要是根据传入的地址,进行解析,例如如果传入的地址中存在${}这种占位符的,spring会根据PropertySourcesPlaceholderConfigurer所配置的.properties文件进行替换,解析出实际的文件路径。至于PropertySourcesPlaceholderConfigurer的解析,不属于关键部分,这里不介绍。
refresh方法就是执行具体的Spring的Bean的解析与加载,后面要着重介绍。
2. AbstractApplicationContext中的refresh方法
1
|
|
3. 解析xml文件
obtainFreshBeanFactory()->refreshBeanFactory()->loadBeanDefinitions(DefaultListableBeanFactory beanFactory)->loadBeanDefinitions(XmlBeanDefinitionReader reader)->loadBeanDefinitions(Resource... resources)->loadBeanDefinitions(Resource resource)->loadBeanDefinitions(EncodedResource encodedResource)->doLoadBeanDefinitions(InputSource inputSource, Resource resource)->registerBeanDefinitions(Document doc, Resource resource)->registerBeanDefinitions(Document doc, XmlReaderContext readerContext)->doRegisterBeanDefinitions(Element root)->parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)
通过以上方法调用,最终会进入到DefaultBeanDefinitionDocumentReader#parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)方法中,该方法才是真正开始解析xml文件的方法。
1
|
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
|
该方法会根据xml中标签对应的namespace是否是spring的namespace来做处理,如果是spring自己的namespace,会调用parseDefaultElement,否则调用parseCustomElement。
这里也不通过代码的方式展现逻辑,毕竟这篇文章是为了面试准备的,面试时可不可能打开代码进行走查。
在parseDefaultElement中会根据标签名做对应的解析,主要是四类标签,分别是import、bean、beans、alias。其中针对于bean的解析才是最关键的。
具体解析的逻辑,其实就是读取bean标签下的子标签和属性,其最终都会记录在BeanDefinitionHolder中,而BeanDefinitionHolder其实就3个字段,beanName是bean的名字,alias是bean的别名,beanDefinition是bean解析出来参数主要存放的地方。其实BeanDefinition是一个接口,定义了一些方法,而具体参数的内容是放在AbstractBeanDefinition中的。
这里就假设Bean加载的工作处理完成了。其实的代码逻辑其实挺复杂的。
3.1 非spring的namespace
这里介绍下如何使用非spring的namespace。Spring最强大的一点就在于其扩展性非常的好,namespace就是其中之一。如果想实现自己的namespace,则需要写一个实现NameSpaceHandler的类,个性化对xml中特殊Bean的解析;然后还要定义DTD或者XSD文件用于spring能够识别个性化的标签,后续介绍Aop的时候再详细介绍。
4. Bean实例化
finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory)->preInstantiateSingletons()
在DefaultListableBeanFactory#preInstantiateSingletons()已经开始准备做实例化的工作了
4.1 构造RootBeanDefinition
首先是构建RootBeanDefinition,由于Spring中的Bean可能会有父子的依赖关系,所以会把上面构造出来的BeanDefinition进行父子结构构造。如果一个BeanDefinition没有父Bean,则自己就是RootBeanDefinition;如果一个BeanDefinition有父Bean,则会进行递归构造RootBeanDefinition,毕竟父Bean也有可能还有父的。
4.2 FactoryBean的使用
FactoryBean是用于定义复杂Bean参数的接口,该接口中定义了3个接口方法。
| 方法名 | 作用 |
|---|---|
T getObject() |
Bean对象构建的核心逻辑 |
Class<?> getObjectType() |
该Bean对象的Class类型 |
boolean isSingleton() |
该Bean是否是单例的 |
在preInstantiateSingletons中,会针对于FactoryBean类型的Bean进行特殊化处理,这里不进行介绍。
4.3 Bean真正实例化
preInstantiateSingletons()->getBean(String name)->doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly)
在这个方法中进行Bean真正实例化的操作。Spring在启动的时候,会自动把所有单例且非延迟加载的Bean放置在Spring容器中,这个加载过程其实就是调用一次getBean方法,感觉有点low,可实际就是这样的,只不过单例的Bean会做缓存,这样getBean的时候如果首次,就会进行实例化,而非首次的话,就从缓存中去取的。
4.4 循环依赖
循环依赖的解决方案:三层缓存singletonObjects、earlySingletonObjects、singletonFactories。其中singletonObjects是存放已经完全解析完毕的Bean,而earlySingletonObjects则是存放提前暴露的未加载完毕的Bean(主要是指当前Bean的Class已经创建,可还未填充属性),而singletonFactories则是存放未加载完毕Bean的ObjectFactory。当Spring加载Bean时发现其下有需要依赖的其他Bean时,先判断在singletonObjects是否存在,如果不存在再earlySingletonObjects去找,如果还找不到就到singletonFactories,一般此时能找到了的。找到后,把Bean从singletonFactories移除,移入earlySingletonObjects中,尽量把Bean慢慢移置在singletonObjects中。
5. Aop实现
上面说了Spring可以通过自定义namespace来进行扩展标签定义,这里的Aop就是这么实现的。在spring-aop工程下,有一个spring-aop.xsd文件用于定义xml文件中个性化参数要怎么写,还有一个AopNamespaceHandler类,间接实现了NamespaceHandler接口。
5.1 NamespaceHandler接口介绍
NameSpaceHandler接口下有3个抽象方法,init一般用于定义不同的标签对应的解析器,parse用于解析xml中的标签,decorate则可以根据不同的xml节点,例如Node或者Attr,进行特殊的个性化处理。
5.2 AopNamespaceHandler介绍
AopNamespaceHandler的init方法下,注册了3个解析器
| 处理的标签 | 对应解析器 | 说明 |
|---|---|---|
config |
ConfigBeanDefinitionParser |
用于解析xml文件中Aop相关配置 |
aspectj-autoproxy |
AspectJAutoProxyBeanDefinitionParser |
用于配置Aop开关,并可以通过注解方式实现Aop |
scoped-proxy |
ScopedProxyBeanDefinitionDecorator |
解析代理是基于cglib的还是基于jdk实现的 |
这里介绍一下ConfigBeanDefinitionParser解析器,该解析器是用于解析xml中相关参数的,下面还会介绍Spring的事务,介绍事务时再介绍通过注解方式实现逻辑。
5.2 ConfigBeanDefinitionParser
ConfigBeanDefinitionParser中的parse方法中,第一步就是注册一个AspectJAwareAdvisorAutoProxyCreator这个Bean,这个Bean间接实现了BeanPostProcessor接口,也就说明其中肯定也实现了postProcessBeforeInitialization和postProcessAfterInitialization方法,而aop主要是实现postProcessAfterInitialization方法
在AbstractAutoProxyCreator(AspectJAwareAdvisorAutoProxyCreator的父类)类中实现了postProcessAfterInitialization方法,在该方法中,首先校验当前Bean是否满足Aop的Adviser,如果满足则创建代理。这样在后续Bean的方法被调用的时候,其实是走的代理
自定义的Aop,是通过AspectJAwareAdvisorAutoProxyCreator来进行处理的,算是通过BeanPostProcessor间接处理
6. Spring事务实现
Spring事务其实也是通过namespace进行扩展的
在spring-tx工程下,有spring-tx.xsd描述文件,在工程中有TxNamespaceHandler类来间接实现NamespaceHandler接口
6.1 TxNamespaceHandler介绍
TxNamespaceHandler的init方法注册了3个解析器
| 处理的标签 | 对应解析器 | 说明 |
|---|---|---|
advice |
TxAdviceBeanDefinitionParser |
解析xml中关于事务相关的配置 |
annotation-driven |
AnnotationDrivenBeanDefinitionParser |
注解方式配置事务的开关,并通过注解方式实现事务 |
jta-transaction-manager |
JtaTransactionManagerBeanDefinitionParser |
暂时没有使用过 |
这里介绍AnnotationDrivenBeanDefinitionParser解析器,该解析器是通过注解的方式处理Spring事务,跟AopNamespaceHandler分开介绍不同的解析器处理方式。
6.2 InfrastructureAdvisorAutoProxyCreator代理创建器
在AnnotationDrivenBeanDefinitionParser中首先注册了一个InfrastructureAdvisorAutoProxyCreator,该代理创建器其实并不属于事务特有的,该创建器只是把一些基础设施类忽略创建代理而已,例如Advice、Pointcut、Advisor、AopInfrastructureBean这些接口的实现类,是不进行代理处理的,这个类跟事务其实没有特别大的关系。
6.3 BeanFactoryTransactionAttributeSourceAdvisor增强器
Spring事务依赖于Aop的实现,在Aop中会注册所有Advisor的实现类为增强器,根据Advisor指定的方式来创建代理,而BeanFactoryTransactionAttributeSourceAdvisor就是做这个事情的。在AnnotationDrivenBeanDefinitionParser解析器中,除了注册了InfrastructureAdvisorAutoProxyCreator之外,还注册BeanFactoryTransactionAttributeSourceAdvisor增强器,该增强器中包含了另外注册的2个类,分别是AnnotationTransactionAttributeSource和TransactionInterceptor。
| 拦截器 | 作用 |
|---|---|
AnnotationTransactionAttributeSource |
用于解析@Transactional注解的 |
TransactionInterceptor |
事务处理的具体流程 |
6.4 TransactionInterceptor事务拦截器
Spring会首先记录注解信息到TransactionAttribute中,然后创建TransactionInfo,而TransactionInfo包含TransactionAttribute,并且把当前事务的信息、例如事务传播方式、事务超时校验、事务隔离级别等等的信息进行校验,根据不同的传播方式,TransactionInfo还有可能对挂起的事务进行处理。在真正的业务处理中,其实就是进行的try-catch-finally操作,当业务逻辑正常流转时,会调用commit操作,而抛异常时会进行rollback操作。其实这里所谓commit和rollback操作,也还是有额外处理的。例如rollback操作,在默认情况下,事务仅针对RuntimeException才会进行事务回滚,如果抛出的Exception,则还是会进行commit。例如commit操作时,会去校验事务的传播方式,如果是嵌套事务,就不会进行commit操作,而是会回到上层事务,根据上层事务是否抛异常来决定是否回滚。所以事务的具体操作,还是要根据不同的事务传播方式来决定的。

浙公网安备 33010602011771号