diff --git a/README.md b/README.md index d683c3e..4d85028 100644 --- a/README.md +++ b/README.md @@ -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)。 diff --git a/docs/学习心得/设计模式/框架源码中的设计模式.md b/docs/LearningExperience/DesignPattern/从框架源码中学习设计模式.md similarity index 87% rename from docs/学习心得/设计模式/框架源码中的设计模式.md rename to docs/LearningExperience/DesignPattern/从框架源码中学习设计模式.md index 66eb477..7f1c60f 100644 --- a/docs/学习心得/设计模式/框架源码中的设计模式.md +++ b/docs/LearningExperience/DesignPattern/从框架源码中学习设计模式.md @@ -1,6 +1,6 @@ 本文用于总结阅读过的框架中,所使用过的设计模式,结合实际生成中的源码,重新理解设计模式。 - + 努力编写中... diff --git a/docs/学习心得/设计模式/设计模式.md b/docs/LearningExperience/DesignPattern/设计模式.md similarity index 100% rename from docs/学习心得/设计模式/设计模式.md rename to docs/LearningExperience/DesignPattern/设计模式.md diff --git a/docs/LearningExperience/EncodingSpecification/由量变到质变写出高质量代码.md b/docs/LearningExperience/EncodingSpecification/由量变到质变写出高质量代码.md new file mode 100644 index 0000000..fcb5dbe --- /dev/null +++ b/docs/LearningExperience/EncodingSpecification/由量变到质变写出高质量代码.md @@ -0,0 +1 @@ +努力编写中... \ No newline at end of file diff --git a/docs/学习心得/个人经验/初级开发者应该从spring源码中学什么.md b/docs/LearningExperience/PersonalExperience/初级开发者应该从spring源码中学什么.md similarity index 100% rename from docs/学习心得/个人经验/初级开发者应该从spring源码中学什么.md rename to docs/LearningExperience/PersonalExperience/初级开发者应该从spring源码中学什么.md diff --git a/docs/Mybatis/核心处理层/2、SqlNode和SqlSource.md b/docs/Mybatis/核心处理层/2、SqlNode和SqlSource.md index e69de29..1faf209 100644 --- a/docs/Mybatis/核心处理层/2、SqlNode和SqlSource.md +++ b/docs/Mybatis/核心处理层/2、SqlNode和SqlSource.md @@ -0,0 +1 @@ +努力编写中... diff --git a/docs/Mybatis/核心处理层/3、ResultSetHandler.md b/docs/Mybatis/核心处理层/3、ResultSetHandler.md index e69de29..1faf209 100644 --- a/docs/Mybatis/核心处理层/3、ResultSetHandler.md +++ b/docs/Mybatis/核心处理层/3、ResultSetHandler.md @@ -0,0 +1 @@ +努力编写中... diff --git a/docs/Spring/SpringMVC/温习一下servlet.md b/docs/Spring/SpringMVC/温习一下servlet.md index e69de29..1faf209 100644 --- a/docs/Spring/SpringMVC/温习一下servlet.md +++ b/docs/Spring/SpringMVC/温习一下servlet.md @@ -0,0 +1 @@ +努力编写中... diff --git a/docs/Spring/SpringTransaction/Spring与事务处理.md b/docs/Spring/SpringTransaction/Spring与事务处理.md new file mode 100644 index 0000000..744886e --- /dev/null +++ b/docs/Spring/SpringTransaction/Spring与事务处理.md @@ -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也提供了一种一致的方式。这种有机的事务过程抽象和具体的事务处理相结合的设计,是我们在日常的开发中非常需要模仿学习的。 \ No newline at end of file diff --git a/docs/Spring/SpringTransaction/Spring事务处理器的设计与实现.md b/docs/Spring/SpringTransaction/Spring事务处理器的设计与实现.md new file mode 100644 index 0000000..1faf209 --- /dev/null +++ b/docs/Spring/SpringTransaction/Spring事务处理器的设计与实现.md @@ -0,0 +1 @@ +努力编写中... diff --git a/docs/Spring/SpringTransaction/Spring事务处理的设计与实现.md b/docs/Spring/SpringTransaction/Spring事务处理的设计与实现.md new file mode 100644 index 0000000..1faf209 --- /dev/null +++ b/docs/Spring/SpringTransaction/Spring事务处理的设计与实现.md @@ -0,0 +1 @@ +努力编写中... diff --git a/docs/Spring/SpringTransaction/Spring声明式事务处理.md b/docs/Spring/SpringTransaction/Spring声明式事务处理.md new file mode 100644 index 0000000..e901fbb --- /dev/null +++ b/docs/Spring/SpringTransaction/Spring声明式事务处理.md @@ -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, 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,通过对这个链表中的元素执行增、删、改等操作,用来管理 + // 配置给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 nameMap = new HashMap(); + + /** + * 将给定属性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 事务处理拦截器的设计与实现 \ No newline at end of file diff --git a/docs/学习心得/编码规范/异常日志.md b/docs/学习心得/编码规范/异常日志.md deleted file mode 100644 index e69de29..0000000 diff --git a/docs/学习心得/编码规范/数据库规范.md b/docs/学习心得/编码规范/数据库规范.md deleted file mode 100644 index e69de29..0000000 diff --git a/docs/学习心得/编码规范/编程规约.md b/docs/学习心得/编码规范/编程规约.md deleted file mode 100644 index e69de29..0000000 diff --git a/images/springTransaction/Spring事务处理模块类层次结构.png b/images/springTransaction/Spring事务处理模块类层次结构.png new file mode 100644 index 0000000..6eef9c7 Binary files /dev/null and b/images/springTransaction/Spring事务处理模块类层次结构.png differ diff --git a/images/springTransaction/createMainInterceptor()方法的调用链.png b/images/springTransaction/createMainInterceptor()方法的调用链.png new file mode 100644 index 0000000..99a5cb2 Binary files /dev/null and b/images/springTransaction/createMainInterceptor()方法的调用链.png differ