关于事务,简单来说就是为了保证数据完整性而存在的工具。它有四个主要特征:原子性、一致性、隔离性和持久性。对于Spring事务,最终是在数据库层面实现的,Spring只是将其封装得更加优雅而已。本文将首先通过一个简单的例子来说明Spring事务是如何使用的,然后再说明Spring是如何解析xml中的标签并支持事务的。1.使用示例事务的最简单示例是它的一致性。比如在整个事务执行过程中,如果任何一个位置报错,都会导致事务回滚。回滚之后,数据的状态会和事务执行前完全一样。一致。这里我们以用户数据为例。在插入用户数据时,如果程序报错,则插入动作会被回滚。下面是用户的实体:publicclassUser{privatelongid;privateStringname;privateintage;//getter,setter...}下面是模拟插入用户数据的业务代码:publicinterfaceUserService{voidinsert(Useruser);}@Service@TransactionalpublicclassUserServiceImplimplementsUserService{@AutowiredprivateJdbcTemplatejdbcTemplate;@Overridepublicvoidinsert(Useruser){jdbcTemplate.update("insertintouser(name,age)value(?,?)",user.getName(),user.getAge());}}Spring只需要用户在做事务支持时只需在需要事务支持的bean上使用@Transactional注解即可。如果需要修改事务的隔离级别和传播特性的属性,使用注解中的属性来指定。这里默认的隔离级别和各个数据库的隔离级别是一样的。比如MySQL是RepeatableRead,默认的传播特性是Propagation.REQUIRED,即只有当前操作需要有事务。下面是xml文件的配置:上用户可以根据自己的设置看到上面的数据库配置。这里的数据库配置主要包括四个方面:DataSource配置:设置当前应用需要连接的数据库,包括链接、用户名、密码等;JdbcTemplateStatement:封装了客户端调用数据库的方式,用户可以使用其他方式,如JpaRepository、Mybatis等;TransactionManager配置:指定事务的管理方式,这里是DataSourceTransactionManager,针对不同的连接方式,也可以配置不同的配置,比如JpaRepository使用JpaTransactionManager,Hibernate使用HibernateTransactionManager;tx:annotation-driven:主要用于事务驱动,会通过AOP声明一个Advisor来支持事务,通过Advisor的配置和事务进行事务相关的操作。根据上面的配置,我们的交易功能就配置好了。下面是我们的驱动程序:privateUsergetUser(){Useruser=newUser();user.setName("Mary");user.setAge(27);returnuser;}}运行上面程序后,可以看到一条数据已经成功添加到数据库中.这里,如果我们在业务代码的insert语句之后手动抛出异常,那么理论上insert语句会回滚。下面是修改后的服务代码:@Service@TransactionalpublicclassUserServiceImplimplementsUserService{@AutowiredprivateJdbcTemplatejdbcTemplate;@Overridepublicvoidinsert(Useruser){jdbcTemplate.update("insertintouser(name,age)value(?,?)",user.getName(),user.getAge());thrownewRuntimeException();}}这里我们手动抛出一个RuntimeException。再次运行上面的程序后,我们发现数据库中已经没有新的数据了。这说明我们的事务在程序失败后可以保证数据的一致性。性的。2.标签解析关于事务的实现原理,我们先解释一下Spring是如何解析标签和封装相关bean的。后面我们会深入讲解Spring是如何封装数据库事务的。根据上面的例子,我们发现主要有三个部分:DataSource、TransactionManager和tx:annotation-driven标签。这里前两部分主要声明了两个bean,分别用于数据库连接管理和事务管理,tx:annotation-driven是Spring事务的驱动。根据我之前对Spring自定义标签的解释(Springcustomtagparsingandimplementation),我们可以知道这里的tx:annotation-driven是一个自定义标签,我们根据它的命名空间(www.springframework.org/schema/tx)查找在全局范围内,可以找到处理器指定文件spring.handlers,文件内容如下:http\://www.springframework.org/schema/tx=org.springframework.transaction.config.TxNamespaceHandlerhere也就是tx:annotation-driven标签的解析是在TxNamespaceHandler里面。继续打开文件,可以看到init()方法如下:registerBeanDefinitionParser("注解驱动",newAnnotationDrivenBeanDefinitionParser());registerBeanDefinitionParser("jta-transaction-manager",newJtaTransactionManagerBeanDefinitionParser());}可以看到这里的注解驱动是在AnnotationDrivenBeanDefinitionParser中进行处理的,其parse()方法就是解析标签。以及注册相关bean的方法,下面是这个方法的实现:publicBeanDefinitionparse(Elementelement,ParserContextparserContext){//注册事务相关的监听器,如果一个方法被标注了TransactionalEventListener注解,//那么这个方法就是一个事务事件触发方法,即某个交易事件发生后,根据注解的设置回调指定类型的方法。常见的事务事件包括:事务执行前和事务完成后(包括完成后报错)的事件。registerTransactionalEventListenerFactory(parserContext);Stringmode=element.getAttribute("mode");//获取当前事务驱动的模式,如果使用aspectj模式,会注册一个AnnotationTransactionAspect类型的bean,用户可以使用它在aspectj的方式bean中配置了更多的事务if("aspectj".equals(mode)){registerTransactionAspect(element,parserContext);}else{//目前一般使用的方法,在SpringRegister中会用到该方法三个bean,分别是//AnnotationTransactionAttributeSource、TransactionInterceptor//和BeanFactoryTransactionAttributeSourceAdvisor,通过Aop实现事务AopAutoProxyConfigurer.configureAutoProxyCreator(element,parserContext);}returnnull;}可以看到对于事务驱动,这里主要做了两件事:①注册事务相关的事件触发器,这些触发器由用户提供,事务提交时会触发相应的方法或完成;②判断目前事务驱动的模式,有default模式和aspectj模式,对于aspectj模式,Spring会注册一个AnnotationTransactionAspect类型的bean,供用户使用更接近aspectj的方法进行事务处理;对于默认模式,这里主要声明三个bean,最后通过Aop切入事务。下面我们来看看Spring是如何注册这三个bean的。下面是AopAutoProxyConfigurer.configureAutoProxyCreator的源码:publicstaticvoidconfigureAutoProxyCreator(Elementelement,ParserContextparserContext){//这个方法主要是在当前的BeanFactory//bean中注册InfrastructureAdvisorAutoProxyCreator,这个bean继承AbstractAdvisorAutoProxyCreator,即其实现原理几乎相同正如我们前面讲解的SpringAop的实现原理。AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext,element);//这里的txAdvisorBeanName就是我们最终要注册的bean,下面注册了它的类型//BeanFactoryTransactionAttributeSourceAdvisor,可以看到,它的本质是一个//Advisor类型的对象,所以SpringAop将其作为aspect编织到指定的bean中element);//注册AnnotationTransactionAttributeSource,这个bean的主要作用是封装声明在@Transactional注解中的属性RootBeanDefinitionsourceDef=newRootBeanDefinition("org.springframework.transaction.annotation.AnnotationTransactionAttributeSource");sourceDef.setSource(eleSource);sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);StringsourceName=parserContext.getReaderContext().registerWithGeneratedName(sourceDef);//注册TransactionInterceptor类型的bean,并封装上面属性的bean被设置为其属性之一//这个bean本质上是一个Advice(可以查看其继承结构)。SpringAop使用Advisor封装了实现切面//逻辑编织所需要的所有属性,但真正的切面逻辑存储在它的Advice属性中。也就是说这里的//TransactionInterceptor是beanRootBeanDefinitioninterceptorDef=newRootBeanDefinition(TransactionInterceptor.class);interceptorDef.setSource(eleSource);interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);registerDefinitionManager(element,interceptorDefinition);registerDefinitionManager(element,interceptorDefinition)getPropertyValues().add("transactionAttributeSource",newRuntimeBeanReference(sourceName));StringinterceptorName=parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);//注册BeanFactoryTransactionAttributeSourceAdvisor类型的bean,这个bean实现了//Advisor接口,其实就是对All的封装当前需要编织的方面的必需属性。RootBeanDefinitionadvisorDef=newRootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);advisorDef.setSource(eleSource);advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);advisorDef.getPropertyValues().add("交易newRuntimeBeanReference(sourceName));advisorDef.getPropertyValues().add("adviceBeanName",interceptorName);if(element.hasAttribute("order")){advisorDef.getPropertyValues().add("order",element.getAttribute("order"));}parserContext.getRegistry().registerBeanDefinition(txAdvisorBeanName,advisorDef);//将需要注册的bean封装到CompositeComponentDefinition中,注册CompositeComponentDefinitioncompositeDef=newCompositeComponentDefinition(element.getTagName(),eleSource);compositeDef.addNestedComponent(newBeanComponentDefinition(sourceDef,sourceName));compositeDef.addNestedComponent(newBeanComponentDefinition(interceptorDef,interceptorName));compositeDef.addNestedComponent(newBeanComponentDefinition(advisorDef,txAdvisorBeanName));parserContext.registerComponent(compositeDef);解析完成,这里主要是在BeanFactory中声明一个BeanFactoryTransactionAttributeSourceAdvisor类型的bean,其实就是一个Advisor类型,这也是Spring事务可以通过Aop实现事务的根本原因3.实现原理关于Spring事务的实现原理,这里需要掌握的是,它是使用Aop实现的。我们知道Aop在解析的时候,最终会生成一个Adivsor对象,这个对象封装了切面编织Enter需要的所有信息,其中最重要的两个部分就是Pointcut和Adivce属性。这里Pointcut用于判断目标bean是否需要织入当前切面逻辑;Advice封装了需要编织的切面逻辑。下面是这三部分的简要关系图:同样,对于Spring事务,由于是使用SpringAop实现的,所以也会有这三个成员。这里我们只看到注册的Advisor和Advice(即BeanFactoryTransactionAttributeSourceAdvisor和TransactionInterceptor),那么Pointcut在哪里呢?这里我们查看BeanFactoryTransactionAttributeSourceAdvisor的源码可以发现,它内部声明了一个TransactionAttributeSourcePointcut类型的属性,并在内部直接实现。这就是我们需要找到的切入点。这三个对象的对应关系如下:这样就找到了用于实现Spring事务的Advisor、Pointcut和Advice。关于这三个类的具体功能,我们在这里做一个整体的讲解,后面会深入到它的内部,讲解它是如何进行bean过滤和业务逻辑编织的。BeanFactoryTransactionAttributeSourceAdvisor:封装了实现事务所需的所有属性,包括Pointcut、Advice、TransactionManager以及Transactional注解中声明的其他一些属性;TransactionAttributeSourcePointcut:用于确定哪些bean需要被编织到当前的事务逻辑中。这里可以想象,其判断的基本逻辑是判断其方法或类声明上是否使用了@Transactional注解。如果使用,就是一个bean,需要编织到事务逻辑中;TransactionInterceptor:这个bean本质上是一个Advice,它封装了当前需要编织到目标bean中的切面逻辑,即如果Spring事务使用数据库事务来实现目标方法的周边。4.小结本文首先通过一个简单的例子讲解了Spring事务是如何使用的,然后讲解了Spring事务在解析标签时做了哪些工作,最后讲解了Spring事务如何与SpringAop一一对应,以及如何编织事务逻辑通过SpringAop进入目标bean。