郑州免费做网站的,商城网站栏目,昆明专业网站排名推广,wordpress网站目录戳蓝字“CSDN云计算”关注我们哦#xff01;1. 使用示例2. 标签解析3. 实现原理4. 小结关于事务#xff0c;简单来说#xff0c;就是为了保证数据完整性而存在的一种工具#xff0c;其主要有四大特性#xff1a;原子性#xff0c;一致性#xff0c;隔离性和持久性。对于… 戳蓝字“CSDN云计算”关注我们哦1. 使用示例2. 标签解析3. 实现原理4. 小结关于事务简单来说就是为了保证数据完整性而存在的一种工具其主要有四大特性原子性一致性隔离性和持久性。对于Spring事务其最终还是在数据库层面实现的而Spring只是以一种比较优雅的方式对其进行封装支持。本文首先会通过一个简单的示例来讲解Spring事务是如何使用的然后会讲解Spring是如何解析xml中的标签并对事务进行支持的。1. 使用示例关于事务最简单的示例就是其一致性比如在整个事务执行过程中如果任何一个位置报错了那么都会导致事务回滚回滚之后数据的状态将和事务执行之前完全一致。这里我们以用户数据为例在插入用户数据的时候如果程序报错了那么插入的动作就会回滚。如下是用户的实体public class User { private long id; private String name; private int age; // getter, setter...}如下是模拟插入用户数据的业务代码public interface UserService { void insert(User user);}ServiceTransactionalpublic class UserServiceImpl implements UserService { Autowired private JdbcTemplate jdbcTemplate; Override public void insert(User user) { jdbcTemplate.update(insert into user (name, age) value (?, ?), user.getName(), user.getAge()); }}在进行事务支持时Spring只需要使用者在需要事务支持的bean上使用Transactional注解即可如果需要修改事务的隔离级别和传播特性的属性则使用该注解中的属性进行指定。这里默认的隔离级别与各个数据库一致比如MySQL是Repeatable Read而传播特性默认则为Propagation.REQUIRED即只需要当前操作具有事务即可。如下是xml文件的配置bean iddataSource classorg.apache.commons.dbcp.BasicDataSource destroy-methodclose property nameurl valuejdbc:mysql://localhost/test?useUnicodetrue/ property namedriverClassName valuecom.mysql.jdbc.Driver/ property nameusername value****/ property namepassword value******//beanbean idjdbcTemplate classorg.springframework.jdbc.core.JdbcTemplate property namedataSource refdataSource//beanbean idtransactionManager classorg.springframework.jdbc.datasource.DataSourceTransactionManager property namedataSource refdataSource//beancontext:component-scan base-packagecom.transaction/tx:annotation-driven/上述数据库配置用户按照各自的设置进行配置即可。可以看到这里对于数据库的配置主要包括四个方面DataSource配置设置当前应用所需要连接的数据库包括链接用户名密码等JdbcTemplate声明封装了客户端调用数据库的方式用户可以使用其他的方式比如JpaRepositoryMybatis等等TransactionManager配置指定了事务的管理方式这里使用的是DataSourceTransactionManager对于不同的链接方式也可以进行不同的配置比如对于JpaRepository使用JpaTransactionManager对于Hibernate使用HibernateTransactionManagertx:annotation-driven主要用于事务驱动其会通过AOP的方式声明一个为事务支持的Advisor通过该Advisor和事务的相关配置进行事务相关操作。按照上述配置我们的事务功能即配置完成如下是我们的驱动类程序public class TransactionApp { Test public void testTransaction() { ApplicationContext ac new ClassPathXmlApplicationContext(applicationContext.xml); UserService userService ac.getBean(UserService.class); User user getUser(); userService.insert(user); } private User getUser() { User user new User(); user.setName(Mary); user.setAge(27); return user; }}运行上述程序之后可以看到数据库中成功新增了一条数据。这里如果我们将业务代码的插入语句之后手动抛出一个异常那么理论上插入语句是会回滚的。如下是修改后的service代码ServiceTransactionalpublic class UserServiceImpl implements UserService { Autowired private JdbcTemplate jdbcTemplate; Override public void insert(User user) { jdbcTemplate.update(insert into user (name, age) value (?, ?), user.getName(), user.getAge()); throw new RuntimeException(); }}这里我们手动抛出了一个RuntimeException再次运行上述程序之后我们发现数据库中是没有新增的数据的这说明我们的事务在程序出错后是能够保证数据一致性的。2. 标签解析关于事务的实现原理我们首先讲解Spring是如何解析标签并且封装相关bean的后面我们会深入讲解Spring是如何封装数据库事务的。根据上面的示例我们发现其主要有三个部分DataSourceTransactionManager和tx:annotation-driven标签。这里前面两个部分主要是声明了两个bean分别用于数据库连接的管理和事务的管理而tx:annotation-driven才是Spring事务的驱动。根据本人前面对Spring自定义标签的讲解Spring自定义标签解析与实现可以知道这里tx:annotation-driven是一个自定义标签我们根据其命名空间www.springframework.org/schema/tx在全局范围内搜索可以找到其处理器指定文件spring.handlers该文件内容如下http\://www.springframework.org/schema/txorg.springframework.transaction.config.TxNamespaceHandler这里也就是说tx:annotation-driven标签的解析在TxNamespaceHandler中我们继续打开该文件可以看到起init()方法如下Overridepublic void init() { registerBeanDefinitionParser(advice, new TxAdviceBeanDefinitionParser()); registerBeanDefinitionParser(annotation-driven, new AnnotationDrivenBeanDefinitionParser()); registerBeanDefinitionParser(jta-transaction-manager, new JtaTransactionManagerBeanDefinitionParser());}可以看到这里的annotation-driven是在AnnotationDrivenBeanDefinitionParser中进行处理的其parse()方法就是解析标签并且注册相关bean的方法如下是该方法的实现public BeanDefinition parse(Element element, ParserContext parserContext) { // 注册事务相关的监听器如果某个方法标注了TransactionalEventListener注解 // 那么该方法就是一个事务事件触发方法即发生某种事务事件后将会根据该注解的设置回调指定 // 类型的方法。常见的事务事件有事务执行前和事务完成(包括报错后的完成)后的事件。 registerTransactionalEventListenerFactory(parserContext); String mode element.getAttribute(mode); // 获取当前事务驱动程序的模式如果使用了aspectj模式则会注册一个AnnotationTransactionAspect // 类型的bean用户可以以aspectj的方式使用该bean对事务进行更多的配置 if (aspectj.equals(mode)) { registerTransactionAspect(element, parserContext); } else { // 一般使用的是当前这种方式这种方式将会在Spring中注册三个bean分别是 // AnnotationTransactionAttributeSourceTransactionInterceptor // 和BeanFactoryTransactionAttributeSourceAdvisor并通过Aop的方式实现事务 AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext); } return null;}可以看到对于事务的驱动这里主要做了两件事①注册事务相关的事件触发器这些触发器由用户自行提供在事务进行提交或事务完成时会触发相应的方法②判断当前事务驱动的模式有默认模式和aspectj模式对于aspectj模式Spring会注册一个AnnotationTransactionAspect类型的bean用于用户使用更亲近于aspectj的方式进行事务处理对于默认模式这里主要是声明了三个bean最终通过Aop的方式进行事务切入。下面我们看一下Spring是如何注册这三个bean的如下是AopAutoProxyConfigurer.configureAutoProxyCreator的源码public static void configureAutoProxyCreator(Element element, ParserContext parserContext) { // 这个方法主要是在当前BeanFactory中注册InfrastructureAdvisorAutoProxyCreator这个 // bean这个bean继承了AbstractAdvisorAutoProxyCreator也就是其实现原理与我们前面 // 讲解的Spring Aop的实现原理几乎一致 AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element); // 这里的txAdvisorBeanName就是我们最终要注册的bean其类型就是下面注册的 // BeanFactoryTransactionAttributeSourceAdvisor可以看到其本质是一个 // Advisor类型的对象因而Spring Aop会将其作为一个切面织入到指定的bean中 String txAdvisorBeanName TransactionManagementConfigUtils .TRANSACTION_ADVISOR_BEAN_NAME; // 如果当前BeanFactory中已经存在了目标bean则不进行注册 if (!parserContext.getRegistry().containsBeanDefinition(txAdvisorBeanName)) { Object eleSource parserContext.extractSource(element); // 注册AnnotationTransactionAttributeSource这个bean的主要作用是封装 // Transactional注解中声明的各个属性 RootBeanDefinition sourceDef new RootBeanDefinition( org.springframework.transaction.annotation.AnnotationTransactionAttributeSource); sourceDef.setSource(eleSource); sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); String sourceName parserContext.getReaderContext() .registerWithGeneratedName(sourceDef); // 注册TransactionInterceptor类型的bean并且将上面的封装属性的bean设置为其一个属性。 // 这个bean本质上是一个Advice(可查看其继承结构)Spring Aop使用Advisor封装实现切面 // 逻辑织入所需的所有属性但真正的切面逻辑却是保存在其Advice属性中的也就是说这里的 // TransactionInterceptor才是真正封装了事务切面逻辑的bean RootBeanDefinition interceptorDef new RootBeanDefinition(TransactionInterceptor.class); interceptorDef.setSource(eleSource); interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); registerTransactionManager(element, interceptorDef); interceptorDef.getPropertyValues().add(transactionAttributeSource, new RuntimeBeanReference(sourceName)); String interceptorName parserContext.getReaderContext() .registerWithGeneratedName(interceptorDef); // 注册BeanFactoryTransactionAttributeSourceAdvisor类型的bean这个bean实现了 // Advisor接口实际上就是封装了当前需要织入的切面的所有所需的属性 RootBeanDefinition advisorDef new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class); advisorDef.setSource(eleSource); advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); advisorDef.getPropertyValues().add(transactionAttributeSource, new RuntimeBeanReference(sourceName)); advisorDef.getPropertyValues().add(adviceBeanName, interceptorName); if (element.hasAttribute(order)) { advisorDef.getPropertyValues().add(order, element.getAttribute(order)); } parserContext.getRegistry().registerBeanDefinition(txAdvisorBeanName, advisorDef); // 将需要注册的bean封装到CompositeComponentDefinition中并且进行注册 CompositeComponentDefinition compositeDef new CompositeComponentDefinition(element.getTagName(), eleSource); compositeDef.addNestedComponent( new BeanComponentDefinition(sourceDef, sourceName)); compositeDef.addNestedComponent( new BeanComponentDefinition(interceptorDef, interceptorName)); compositeDef.addNestedComponent( new BeanComponentDefinition(advisorDef, txAdvisorBeanName)); parserContext.registerComponent(compositeDef); }}如此Spring事务相关的标签即解析完成这里主要是声明了一个BeanFactoryTransactionAttributeSourceAdvisor类型的bean到BeanFactory中其实际为Advisor类型这也是Spring事务能够通过Aop实现事务的根本原因。3. 实现原理关于Spring事务的实现原理这里需要抓住的就是其是使用Aop实现的我们知道Aop在进行解析的时候最终会生成一个Adivsor对象这个Advisor对象中封装了切面织入所需要的所有信息其中就包括最重要的两个部分就是Pointcut和Adivce属性。这里Pointcut用于判断目标bean是否需要织入当前切面逻辑Advice则封装了需要织入的切面逻辑。如下是这三个部分的简要关系图Advisor同样的对于Spring事务其既然是使用Spring Aop实现的那么也同样会有这三个成员。我们这里我们只看到了注册的Advisor和Advice即BeanFactoryTransactionAttributeSourceAdvisor和TransactionInterceptor那么Pointcut在哪里呢这里我们查看BeanFactoryTransactionAttributeSourceAdvisor的源码可以发现其内部声明了一个TransactionAttributeSourcePointcut类型的属性并且直接在内部进行了实现这就是我们需要找的Pointcut。这里这三个对象对应的关系如下Transaction这样用于实现Spring事务的AdvisorPointcut以及Advice都已经找到了。关于这三个类的具体作用我们这里进行整体的上的讲解后面我们将会深入其内部讲解其是如何进行bean的过滤以及事务逻辑的织入的。BeanFactoryTransactionAttributeSourceAdvisor封装了实现事务所需的所有属性包括PointcutAdviceTransactionManager以及一些其他的在Transactional注解中声明的属性TransactionAttributeSourcePointcut用于判断哪些bean需要织入当前的事务逻辑。这里可想而知其判断的基本逻辑就是判断其方法或类声明上有没有使用Transactional注解如果使用了就是需要织入事务逻辑的bean;TransactionInterceptor这个bean本质上是一个Advice其封装了当前需要织入目标bean的切面逻辑也就是Spring事务是如果借助于数据库事务来实现对目标方法的环绕的。4. 小结本文首先通过一个简单的例子讲解了Spring事务是如何使用的然后讲解了Spring事务进行标签解析的时候做了哪些工作最后讲解了Spring事务是如何与Spring Aop进行一一对应的并且是如何通过Spring Aop实现将事务逻辑织入目标bean的。1.微信群添加小编微信color_ld备注“进群姓名公司职位”即可加入【云计算学习交流群】和志同道合的朋友们共同打卡学习2.征稿投稿邮箱liudancsdn.net微信号color_ld。请备注投稿姓名公司职位。推荐阅读Istio下一个Kubernetes云计算的“傲慢”与“偏见”大数据时代谁的眼神锁定你别吐槽了面试要求徒手写代码你与顶级程序员的差别就在这算法工程师独得恩宠 四面楚歌的Android工程师该何去何从腾讯将创办腾讯云启商学院马化腾任荣誉院长扎堆出海的抖音、今日头条、UC 头条们后来怎么样了创业者老板被程序员「割」了韭菜中国云计算的十年江湖2018 中国大数据技术大会将于 12 月 6 -8 日在新云南皇冠假日酒店举行。汇聚超百位国内外实力讲师从学界翘楚到行业一线大拿管晓宏中国科学院院士、张宏江:源码资本投资合伙人、张晓东美国俄亥俄州立大学Robert M. Critchfield讲席教授、陈性元 北京信息科学技术研究院副院长、周靖人阿里巴巴集团副总裁、李浩源Alluxio公司创始人CEO等重磅嘉宾全方位立体解读大数据时代的技术进程为众技术爱好者奉上一场优质干货盛宴。↓↓↓ 点击【阅读原文】查看「CSDN云计算」往期精彩内容