Merge remote-tracking branch 'remotes/other_master/master'

pull/20/head
wt 5 years ago
commit 7628e2c378

@ -29,7 +29,10 @@
### Spring事务
- [Spring与事务处理](/docs/Spring/SpringTransaction/Spring与事务处理.md)
- [Spring声明式事务处理](/docs/Spring/SpringTransaction/Spring声明式事务处理.md)
- [Spring事务处理的设计与实现](/docs/Spring/SpringTransaction/Spring事务处理的设计与实现.md)
- [Spring事务处理器的设计与实现](/docs/Spring/SpringTransaction/Spring事务处理器的设计与实现.md)
### Spring源码故事瞎编版
- [面筋哥 IoC 容器的一天(上)](/docs/Spring/Spring源码故事瞎编版/面筋哥IoC容器的一天(上).md)
@ -76,11 +79,13 @@
## 学习心得
### 个人经验
- [初级开发者应该从 spring 源码中学什么](docs/学习心得/个人经验/初级开发者应该从spring源码中学什么.md)
- [初级开发者应该从 spring 源码中学什么](docs/LearningExperience/PersonalExperience/初级开发者应该从spring源码中学什么.md)
### 编码规范
- [由量变到质变写出高质量代码](docs/LearningExperience/EncodingSpecification/由量变到质变写出高质量代码.md)
### 设计模式
- [从框架源码中学习设计模式](docs/LearningExperience/DesignPattern/从框架源码中学习设计模式.md)
## 贡献者
感谢以下所有朋友对 [GitHub 技术社区 Doocs](https://github.com/doocs) 所做出的贡献,[参与项目维护请戳这儿](https://doocs.github.io/#/?id=how-to-join)。

@ -1,6 +1,6 @@
本文用于总结阅读过的框架中,所使用过的设计模式,结合实际生成中的源码,重新理解设计模式。
努力编写中...

@ -0,0 +1,18 @@
JavaEE应用中的事务处理是一个重要并且涉及范围很广的领域。事务管理的实现往往涉及并发和数据一致性方面的问题。作为应用平台的Spring具有在多种环境中配置和使用事务处理的能力也就是说通过使用Spring的事务组件可以把事务处理的工作统一起来并为事务处理提供通用的支持。
在涉及单个数据库局部事务的事务处理中事务的最终实现和数据库的支持是紧密相关的。对局部数据库事务来说一个事务处理的操作单元往往对应着一系列的数据库操作。数据库产品对这些数据库的SQL操作已经提供了原子性的支持对SQL操作而言,它的操作结果有两种: 一种是提交成功,数据库操作成功;另一种是回滚,数据库操作不成功,恢复到操作以前的状态。
在事务处理中事务处理单元的设计与相应的业务逻辑设计有很紧密的联系。在很多情况下一个业务逻辑处理不会只有一个单独的数据库操作而是有一组数据库操作。在这个处理过程中首先涉及的是事务处理单元划分的问题Spring借助IoC容器的强大配置能力为应用提供了声明式的事务划分方式这种声明式的事务处理为Spring应用使用事务管理提供了统一的方式。有了Spring事务管理的支持只需要通过一些简单的配置应用就能完成复杂的事务处理工作从而为用户使用事务处理提供很大的方便。
## 1 Spring事务处理的设计概览
Spring事务处理模块的类层次结构如下图所示。
![avatar](/images/springTransaction/Spring事务处理模块类层次结构.png)
从上图可以看到Spring事务处理模块是通过AOP功能来实现声明式事务处理的比如事务属性的配置和读取事务对象的抽象等。因此在Spring事务处理中可以通过设计一个TransactionProxyFactoryBean来使用AOP功能通过这个TransactionProxyFactoryBean可以生成Proxy代理对象在这个代理对象中通过TransactionInterceptor来完成对代理方法的拦截正是这些AOP的拦截功能将事务处理的功能编织进来。
对于具体的事务处理实现比如事务的生成、提交、回滚、挂起等由于不同的底层数据库有不同的支持方式因此在Spring事务处理中对主要的事务实现做了一个抽象和适配。适配的具体事务处理器包括对DataSource数据源的事务处理支持对Hibernate数据源的事务处理支持对JDO数据源的事务处理支持对JPA和JTA等数据源的事务处理支持等。这一系列的事务处理支持都是通过设计PlatformTransactionManager、AbstractPlatforTransactionManager以及一系列具体事务处理器来实现的而PlatformTransactionManager又实现了TransactionInterceptor接口通过这样一个接口实现设计就把这一系列的事务处理的实现与前面提到的TransactionProxyFactoryBean结合起来从而形成了一个Spring声明式事务处理的设计体系。
## 2 Spring事务处理的应用场景
Spring作为应用平台或框架的设计出发点是支持POJO的开发这点在实现事务处理的时候也不例外。在Spring中它既支持编程式事务管理方式又支持声明式事务处理方式在使用Spring处理事务的时候声明式事务处理通常比编程式事务管理更方便些。
Spring对应用的支持一方面通过声明式事务处理将事务处理的过程和业务代码分离出来。这种声明方式实际上是通过AOP的方式来完成的。显然Spring已经把那些通用的事务处理过程抽象出来并通过AOP的方式进行封装然后用声明式的使用方式交付给客户使用。这样应用程序可以更简单地管理事务并且只需要关注事务的处理策略。另一方面应用在选择数据源时可能会采取不同的方案当以Spring作为平台时Spring在应用和具体的数据源之间搭建一个中间平台通过这个中间平台解耦应用和具体数据源之间的绑定并且Spring为常用的数据源的事务处理支持提供了一系列的TransactionManager。这些Spring封装好的TransactionManager为应用提供了很大的方便因为在这些具体事务处理过程中已经根据底层的实现封装好了事务处理的设置以及与特定数据源相关的特定事务处理过程这样应用在使用不同的数据源时可以做到事务处理的即开即用。这样的另一个好处是如果应用有其他的数据源事务处理需要Spring也提供了一种一致的方式。这种有机的事务过程抽象和具体的事务处理相结合的设计是我们在日常的开发中非常需要模仿学习的。

@ -0,0 +1,276 @@
## 1 设计原理与基本过程
在使用Spring声明式事务处理的时候一种常用的方法是结合IoC容器和Spring已有的TransactionProxyFactoryBean对事务管理进行配置比如可以在这个TransactionProxyFactoryBean中为事务方法配置传播行为、并发事务隔离级别等事务处理属性从而对声明式事务的处理提供指导。具体来说在对声明式事务处理的原理分析中声明式事务处理的实现大致可以分为以下几个部分:
- 读取和处理在IoC容器中配置的事务处理属性并转化为Spring事务处理需要的内部数据结构这里涉及的类是TransactionAttributeSourceAdvisor从名字可以看出它是一个AOP通知器Spring使用这个通知器来完成对事务处理属性值的处理。处理的结果是在IoC容器中配置的事务处理属性信息会被读入并转化成TransactionAttribute表示的数据对象这个数据对象是Spring对事物处理属性值的数据抽象对这些属性的处理是和TransactionProxyFactoryBean拦截下来的事务方法的处理结合起来的。
- Spring事务处理模块实现统一的事务处理过程。这个通用的事务处理过程包含处理事务配置属性以及与线程绑定完成事务处理的过程Spring通过TransactionInfo和TransactionStatus这两个数据对象在事务处理过程中记录和传递相关执行场景。
- 底层的事务处理实现。对于底层的事务操作Spring委托给具体的事务处理器来完成这些具体的事务处理器就是在IoC容器中配置声明式事务处理时配置的PlatformTransactionManager的具体实现比如DataSourceTransactionManager和HibernateTransactionManager等。
## 2 实现分析
### 2.1 事务处理拦截器的配置
和前面的思路一样从声明式事务处理的基本用法入手来了解它的基本实现原理。在使用声明式事务处理的时候需要在IoC容器中配置TransactionProxyFactoryBean见名知义这是一个FactoryBean有一个getObject()方法。在IoC容器进行注入的时候会创建TransactionInterceptor对象而这个对象会创建一个TransactionAttributePointcut为读取TransactionAttribute做准备。在容器初始化的过程中由于实现了InitializingBean接口因此AbstractSingletonProxyFactoryBean会实现afterPropertiesSet()方法正是在这个方法实例化了一个ProxyFactory建立起Spring AOP的应用在这里会为这个ProxyFactory设置通知、目标对象并最终返回Proxy代理对象。在Proxy代理对象建立起来以后在调用其代理方法的时候会调用相应的TransactionInterceptor拦截器在这个调用中会根据TransactionAttribute配置的事务属性进行配置从而为事务处理做好准备。
从TransactionProxyFactoryBean入手通过代码实现来了解Spring是如何通过AOP功能来完成事务管理配置的Spring为声明式事务处理的实现所做的一些准备工作包括为AOP配置基础设施这些基础设施包括设置拦截器TransactionInterceptor、通知器DefaultPointcutAdvisor或TransactionAttributeSourceAdvisor。同时在TransactionProxyFactoryBean的实现中 还可以看到注人进来的PlatformTransactionManager和事务处理属性TransactionAttribute等。
```java
/**
* 代理工厂bean用于简化声明式事务处理,这是标准AOP的一个方便的替代方案
* 使用单独的TransactionInterceptor定义。
*/
@SuppressWarnings("serial")
public class TransactionProxyFactoryBean extends AbstractSingletonProxyFactoryBean
implements BeanFactoryAware {
/** 事务拦截器通过AOP来发挥作用spring在此拦截器中封装了 事务处理实现 */
private final TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
/** 切面 */
private Pointcut pointcut;
/**
* 通过依赖注入的事务属性以properties的形式出现
* 把从beandefinition中读到的事务管理的属性信息注入到transactionInterceptor
*/
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionInterceptor.setTransactionManager(transactionManager);
}
/**
* 创建AOP对事务处理的advisor
* 本方法在IoC容器完成Bean的依赖注入时通过initializeBean方法被调用
*/
@Override
protected Object createMainInterceptor() {
this.transactionInterceptor.afterPropertiesSet();
if (this.pointcut != null) {
// 如果自己定义了切面,就使用默认的通知器,并为其配置事务处理拦截器
return new DefaultPointcutAdvisor(this.pointcut, this.transactionInterceptor);
}
else {
// 如果没定义则使用spring默认的切面使用TransactionAttributeSourceAdvisor
// 作为通知器,并配置拦截器
return new TransactionAttributeSourceAdvisor(this.transactionInterceptor);
}
}
public void setTransactionAttributes(Properties transactionAttributes) {
this.transactionInterceptor.setTransactionAttributes(transactionAttributes);
}
public void setTransactionAttributeSource(TransactionAttributeSource transactionAttributeSource) {
this.transactionInterceptor.setTransactionAttributeSource(transactionAttributeSource);
}
public void setPointcut(Pointcut pointcut) {
this.pointcut = pointcut;
}
public void setBeanFactory(BeanFactory beanFactory) {
this.transactionInterceptor.setBeanFactory(beanFactory);
}
}
```
以上代码完成了AOP配置对于用户来说一个值得关心的问题是Spring的TransactionInterceptor配置是在什么时候被启动并成为Advisor通知器的一部分的呢从对createMainInterceptor()方法的调用分析中可以看到这个createMainInterceptor()方法在IoC容器完成Bean的依赖注入时通过initializeBean()方法被调用,具体的调用过程如下图所示。
![avatar](/images/springTransaction/createMainInterceptor()方法的调用链.png)
在TransactionProxyFactoryBean的父类AbstractSingletonProxyFactoryBean中的afterPropertiesSet()方法是Spring事务处理完成AOP配置的地方在建立TransactionProxyFactoryBean的事务处理拦截器的时候首先需要对ProxyFactoryBean的目标Bean设置进行检查如果这个目标Bean的设置是正确的就会创建一个ProxyFactory对象从而实现AOP的使用。在afterPropertiesSet()的方法实现中可以看到为ProxyFactory生成代理对象、配置通知器、设置代理接口方法等。
```java
public abstract class AbstractSingletonProxyFactoryBean extends ProxyConfig
implements FactoryBean<Object>, BeanClassLoaderAware, InitializingBean {
private Object target;
private Class<?>[] proxyInterfaces;
private Object[] preInterceptors;
private Object[] postInterceptors;
/** Default is global AdvisorAdapterRegistry */
private AdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.getInstance();
private transient ClassLoader proxyClassLoader;
private Object proxy;
/**
* 处理完成AOP配置创建ProxyFactory对象为其生成代理对象
* 配置通知器、设置代理接口方法
*/
public void afterPropertiesSet() {
// 校验target目标对象
if (this.target == null) {
throw new IllegalArgumentException("Property 'target' is required");
}
if (this.target instanceof String) {
throw new IllegalArgumentException("'target' needs to be a bean reference, not a bean name as value");
}
if (this.proxyClassLoader == null) {
this.proxyClassLoader = ClassUtils.getDefaultClassLoader();
}
// 使用ProxyFactory完成AOP的基本功能ProxyFactory提供proxy对象
// 并将TransactionIntercepter设为target方法调用的拦截器
ProxyFactory proxyFactory = new ProxyFactory();
if (this.preInterceptors != null) {
for (Object interceptor : this.preInterceptors) {
proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(interceptor));
}
}
// 加入Advisor通知器可以加入两种通知器分别是
// DefaultPointcutAdvisor、TransactionAttributeSourceAdvisor
// 这里通过调用TransactionProxyFactoryBean实例的createMainInterceptor()方法
// 来生成需要的Advisors。在ProxyFactory的基类AdvisedSupport中维护了一个持有Advisor
// 的链表LinkedList<Advisor>,通过对这个链表中的元素执行增、删、改等操作,用来管理
// 配置给ProxyFactory的通知器
proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(createMainInterceptor()));
if (this.postInterceptors != null) {
for (Object interceptor : this.postInterceptors) {
proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(interceptor));
}
}
proxyFactory.copyFrom(this);
// 这里创建AOP的目标源与在其它地方使用ProxyFactory没什么差别
TargetSource targetSource = createTargetSource(this.target);
proxyFactory.setTargetSource(targetSource);
if (this.proxyInterfaces != null) {
proxyFactory.setInterfaces(this.proxyInterfaces);
}
else if (!isProxyTargetClass()) {
// 需要根据AOP基础设施来确定使用哪个接口作为代理
proxyFactory.setInterfaces(
ClassUtils.getAllInterfacesForClass(targetSource.getTargetClass(), this.proxyClassLoader));
}
// 设置代理对象
this.proxy = proxyFactory.getProxy(this.proxyClassLoader);
}
}
```
DefaultAopProxyFactory创建AOP Proxy的过程在前面分析AOP的实现原理时已分析过这里就不再重复了。可以看到通过以上的一系列步骤Spring为实现事务处理而设计的拦截器TransctionInterceptor已经设置到ProxyFactory生成的AOP代理对象中去了这里的TransactionInterceptor是作为AOP Advice的拦截器来实现它的功能的。在IoC容器中配置其他与事务处理有关的属性比如比较熟悉的transactionManager和事务处理的属性也同样会被设置到已经定义好的TransactionInterceptor中去。这些属性配置在TransactionInterceptor对事务方法进行拦截时会起作用。在AOP配置完成以后可以看到在Spring声明式事务处理实现中的一些重要的类已经悄然登场比如TransactionAttributeSourceAdvisor和TransactionInterceptor正是这些类通过AOP封装了Spring对事务处理的基本实现。
### 2.2 事务处理配置的读入
在AOP配置完成的基础上以TransactionAttributeSourceAdvisor的实现为入 口,了解具体的事务属性配置是如何读入的。
```java
public class TransactionAttributeSourceAdvisor extends AbstractPointcutAdvisor {
/**
* 与其它Advisor一样同样需要定义AOP中用到的Interceptor和Pointcut
*/
private TransactionInterceptor transactionInterceptor;
/**
* 对于切面pointcut这里使用了一个匿名内部类
*/
private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
/**
* 通过transactionInterceptor来得到事务的配置属性在对Proxy的方法进行匹配调用时
* 会使用到这些配置属性
*/
@Override
protected TransactionAttributeSource getTransactionAttributeSource() {
return (transactionInterceptor != null ? transactionInterceptor.getTransactionAttributeSource() : null);
}
};
}
```
在声明式事务处理中通过对目标对象的方法调用进行拦截来实现事务处理的织入这个拦截通过AOP发挥作用。在AOP中对于拦截的启动首先需要对方法调用是否需要拦截进行判断而判断的依据是那些在TransactionProxyFactoryBean中为目标对象设置的事务属性。也就是说需要判断当前的目标方法调用是不是一个配置好的并且需要进行事务处理的方法调用。具体来说这个匹配判断在TransactionAttributeSourcePointcut的matches()方法中完成,该方法实现 首先把事务方法的属性配置读取到TransactionAttributeSource对象中有了这些事务处理的配置以后根据当前方法调用的Method对象和目标对象对是否需要启动事务处理拦截器进行判断。
```java
abstract class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
public boolean matches(Method method, Class targetClass) {
TransactionAttributeSource tas = getTransactionAttributeSource();
return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
}
}
```
在Pointcut的matches()判断过程中会用到TransactionAttributeSource对象这个TransactionAttributeSource对象是在对TransactionInterceptor进行依赖注入时就配置好的。它的设置是在TransactionInterceptor的基类TransactionAspectSupport中完成的配置的是一个NameMatchTransactionAttributeSource对象。
```java
public abstract class TransactionAspectSupport implements BeanFactoryAware, InitializingBean {
/**
* 设置事务属性以方法名为key事务属性描述符为value
* 例如key = "myMethod", value = "PROPAGATION_REQUIRED,readOnly"
*/
public void setTransactionAttributes(Properties transactionAttributes) {
// 可以看到这是一个NameMatchTransactionAttributeSource的实现
NameMatchTransactionAttributeSource tas = new NameMatchTransactionAttributeSource();
tas.setProperties(transactionAttributes);
this.transactionAttributeSource = tas;
}
}
```
在以上的代码实现中可以看到NameMatchTransactionAttributeSource作为TransactionAttributeSource的具体实现是实际完成事务处理属性读入和匹配的地方。在对事务属性TransactionAttributes进行设置时会从事务处理属性配置中读取事务方法名和配置属性在得到配置的事务方法名和属性以后会把它们作为键值对加入到一个nameMap中。
在应用调用目标方法的时候因为这个目标方法已经被TransactionProxyFactoryBean代理所以TransactionProxyFactoryBean需要判断这个调用方法是否是事务方法。这个判断的实现是通过在NameMatchTransactionAttributeSource中能否为这个调用方法返回事务属性来完成的。具体的实现过程是这样的首先以调用方法名为索引在nameMap中查找相应的事务处理属性值如果能够找到那么就说明该调用方法和事务方法是直接对应的如果找不到那么就会遍历整个nameMap对保存在nameMap中的每一个方法名使用PatternMatchUtils的simpleMatch()方法进行命名模式上的匹配。这里使用PatternMatchUtils进行匹配的原因是在设置事务方法的时候可以不需要为事务方法设置一个完整的方法名而可以通过设置方法名的命名模式来完成比如可以通过对通配符*的使用等。所以如果直接通过方法名没能够匹配上而通过方法名的命名模式能够匹配上这个方法也是需要进行事务处理的相对应地它所配置的事务处理属性也会从nameMap中取出来从而触发事务处理拦截器的拦截。
```java
public class NameMatchTransactionAttributeSource implements TransactionAttributeSource, Serializable {
/** key是方法名value是事务属性 */
private Map<String, TransactionAttribute> nameMap = new HashMap<String, TransactionAttribute>();
/**
* 将给定属性transactionAttributes解析为名称/属性的map对象。以 方法名称 为键,
* 字符串属性定义 为值可通过TransactionAttributeEditor解析为TransactionAttribute实例。
*/
public void setProperties(Properties transactionAttributes) {
TransactionAttributeEditor tae = new TransactionAttributeEditor();
Enumeration propNames = transactionAttributes.propertyNames();
while (propNames.hasMoreElements()) {
String methodName = (String) propNames.nextElement();
String value = transactionAttributes.getProperty(methodName);
tae.setAsText(value);
TransactionAttribute attr = (TransactionAttribute) tae.getValue();
addTransactionalMethod(methodName, attr);
}
}
/**
* 为事务方法添加属性
*/
public void addTransactionalMethod(String methodName, TransactionAttribute attr) {
if (logger.isDebugEnabled()) {
logger.debug("Adding transactional method [" + methodName + "] with attribute [" + attr + "]");
}
this.nameMap.put(methodName, attr);
}
/**
* 对调用的方法进行判断,判断它是否是事务方法,如果是事务方法,则取出相应的事务配置属性
*/
public TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass) {
// 直接通过 方法名 匹配
String methodName = method.getName();
TransactionAttribute attr = this.nameMap.get(methodName);
if (attr == null) {
// 查找最具体的名称匹配
String bestNameMatch = null;
for (String mappedName : this.nameMap.keySet()) {
if (isMatch(methodName, mappedName) &&
(bestNameMatch == null || bestNameMatch.length() <= mappedName.length())) {
attr = this.nameMap.get(mappedName);
bestNameMatch = mappedName;
}
}
}
return attr;
}
/**
* 如果给定的方法名与映射的名称匹配,则返回
*/
protected boolean isMatch(String methodName, String mappedName) {
return PatternMatchUtils.simpleMatch(mappedName, methodName);
}
```
通过以上过程可以得到与目标对象调用方法相关的TransactionAttribute对象在这个对象中封装了事务处理的配置。具体来说在前面的匹配过程中如果匹配返回的结果是null那么说明当前的调用方法不是一个事务方法不需要纳入Spring统一的事务管理中因为它并没有配置在TransactionProxyFactoryBean的事务处理设置中。如果返回的TransactionAttribute对象不是null,那么这个返回的TransactionAttribute对象就已经包含了对事务方法的配置信息对应这个事务方法的具体事务配置也已经读入到TransactionAttribute对象中了为TransactionInterceptor做好了对调用的目标方法添加事务处理的准备。
### 2.3 事务处理拦截器的设计与实现

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Loading…
Cancel
Save