最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring中事務(wù)用法示例及實(shí)現(xiàn)原理詳解

 更新時間:2018年11月18日 10:20:40   作者:愛寶貝  
這篇文章主要給大家介紹了關(guān)于Spring中事務(wù)用法示例及實(shí)現(xiàn)原理的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

前言

Spring并不直接管理事務(wù),而是提供了多種事務(wù)管理器,他們將事務(wù)管理的職責(zé)委托給Hibernate或者JTA等持久化機(jī)制所提供的相關(guān)平臺框架的事務(wù)來實(shí)現(xiàn)。

關(guān)于事務(wù),簡單來說,就是為了保證數(shù)據(jù)完整性而存在的一種工具,其主要有四大特性:原子性,一致性,隔離性和持久性。對于Spring事務(wù),其最終還是在數(shù)據(jù)庫層面實(shí)現(xiàn)的,而Spring只是以一種比較優(yōu)雅的方式對其進(jìn)行封裝支持。本文首先會通過一個簡單的示例來講解Spring事務(wù)是如何使用的,然后會講解Spring是如何解析xml中的標(biāo)簽,并對事務(wù)進(jìn)行支持的。

下面話不多說了,來一起看看詳細(xì)的介紹吧

1. 使用示例

關(guān)于事務(wù)最簡單的示例,就是其一致性,比如在整個事務(wù)執(zhí)行過程中,如果任何一個位置報錯了,那么都會導(dǎo)致事務(wù)回滾,回滾之后數(shù)據(jù)的狀態(tài)將和事務(wù)執(zhí)行之前完全一致。這里我們以用戶數(shù)據(jù)為例,在插入用戶數(shù)據(jù)的時候,如果程序報錯了,那么插入的動作就會回滾。如下是用戶的實(shí)體:

public class User { private long id; private String name; private int age; 
 // getter, setter...} 

如下是模擬插入用戶數(shù)據(jù)的業(yè)務(wù)代碼:

public interface UserService { void insert(User user); 
}@Service@Transactionalpublic 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()); 
 } 
} 

在進(jìn)行事務(wù)支持時,Spring只需要使用者在需要事務(wù)支持的bean上使用@Transactional注解即可,如果需要修改事務(wù)的隔離級別和傳播特性的屬性,則使用該注解中的屬性進(jìn)行指定。這里默認(rèn)的隔離級別與各個數(shù)據(jù)庫一致,比如MySQL是Repeatable Read,而傳播特性默認(rèn)則為Propagation.REQUIRED,即只需要當(dāng)前操作具有事務(wù)即可。如下是xml文件的配置:

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> 
 <property name="url" value="jdbc:mysql://localhost/test?useUnicode=true"/> 
 <property name="driverClassName" value="com.mysql.jdbc.Driver"/> 
 <property name="username" value="****"/> 
 <property name="password" value="******"/></bean><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> 
 <property name="dataSource" ref="dataSource"/></bean><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
 
 <property name="dataSource" ref="dataSource"/></bean><context:component-scan base-package="com.transaction"/><tx:annotation-driven/> 

上述數(shù)據(jù)庫配置用戶按照各自的設(shè)置進(jìn)行配置即可。可以看到,這里對于數(shù)據(jù)庫的配置,主要包括四個方面:

  • DataSource配置:設(shè)置當(dāng)前應(yīng)用所需要連接的數(shù)據(jù)庫,包括鏈接,用戶名,密碼等;
  • JdbcTemplate聲明:封裝了客戶端調(diào)用數(shù)據(jù)庫的方式,用戶可以使用其他的方式,比如JpaRepository,Mybatis等等;
  • TransactionManager配置:指定了事務(wù)的管理方式,這里使用的是DataSourceTransactionManager,對于不同的鏈接方式,也可以進(jìn)行不同的配置,比如對于JpaRepository使用JpaTransactionManager,對于Hibernate,使用HibernateTransactionManager;
  • tx:annotation-driven:主要用于事務(wù)驅(qū)動,其會通過AOP的方式聲明一個為事務(wù)支持的Advisor,通過該Advisor和事務(wù)的相關(guān)配置進(jìn)行事務(wù)相關(guān)操作。

按照上述配置,我們的事務(wù)功能即配置完成,如下是我們的驅(qū)動類程序:

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; 
 } 
} 

運(yùn)行上述程序之后,可以看到數(shù)據(jù)庫中成功新增了一條數(shù)據(jù)。這里如果我們將業(yè)務(wù)代碼的插入語句之后手動拋出一個異常,那么,理論上插入語句是會回滾的。如下是修改后的service代碼:

@Service@Transactionalpublic 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,再次運(yùn)行上述程序之后我們發(fā)現(xiàn)數(shù)據(jù)庫中是沒有新增的數(shù)據(jù)的,這說明我們的事務(wù)在程序出錯后是能夠保證數(shù)據(jù)一致性的。

2. 標(biāo)簽解析

關(guān)于事務(wù)的實(shí)現(xiàn)原理,我們首先講解Spring是如何解析標(biāo)簽,并且封裝相關(guān)bean的,后面我們會深入講解Spring是如何封裝數(shù)據(jù)庫事務(wù)的。

根據(jù)上面的示例,我們發(fā)現(xiàn),其主要有三個部分:DataSource,TransactionManager和tx:annotation-driven標(biāo)簽。這里前面兩個部分主要是聲明了兩個bean,分別用于數(shù)據(jù)庫連接的管理和事務(wù)的管理,而tx:annotation-driven才是Spring事務(wù)的驅(qū)動。根據(jù)本人前面對Spring自定義標(biāo)簽的講解(Spring自定義標(biāo)簽解析與實(shí)現(xiàn)),可以知道,這里tx:annotation-driven是一個自定義標(biāo)簽,我們根據(jù)其命名空間(www.springframework.org/schema/tx)在全局范圍內(nèi)搜索,可以找到其處理器指定文件spring.handlers,該文件內(nèi)容如下:

http\://www.springframework.org/schema/tx=org.springframework.transaction.config.TxNamespaceHandler

這里也就是說tx:annotation-driven標(biāo)簽的解析在TxNamespaceHandler中,我們繼續(xù)打開該文件可以看到起init()方法如下:

@Overridepublic void init() { 
 registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser()); 
 registerBeanDefinitionParser("annotation-driven", 
  new AnnotationDrivenBeanDefinitionParser()); 
 registerBeanDefinitionParser("jta-transaction-manager", 
  new JtaTransactionManagerBeanDefinitionParser()); 
} 

可以看到,這里的annotation-driven是在AnnotationDrivenBeanDefinitionParser中進(jìn)行處理的,其parse()方法就是解析標(biāo)簽,并且注冊相關(guān)bean的方法,如下是該方法的實(shí)現(xiàn):

public BeanDefinition parse(Element element, ParserContext parserContext) { // 注冊事務(wù)相關(guān)的監(jiān)聽器,如果某個方法標(biāo)注了TransactionalEventListener注解, 
 // 那么該方法就是一個事務(wù)事件觸發(fā)方法,即發(fā)生某種事務(wù)事件后,將會根據(jù)該注解的設(shè)置,回調(diào)指定 
 // 類型的方法。常見的事務(wù)事件有:事務(wù)執(zhí)行前和事務(wù)完成(包括報錯后的完成)后的事件。 
 registerTransactionalEventListenerFactory(parserContext); 
 String mode = element.getAttribute("mode"); // 獲取當(dāng)前事務(wù)驅(qū)動程序的模式,如果使用了aspectj模式,則會注冊一個AnnotationTransactionAspect 
 // 類型的bean,用戶可以以aspectj的方式使用該bean對事務(wù)進(jìn)行更多的配置 
 if ("aspectj".equals(mode)) { 
  registerTransactionAspect(element, parserContext); 
 } else {  // 一般使用的是當(dāng)前這種方式,這種方式將會在Spring中注冊三個bean,分別是 
  // AnnotationTransactionAttributeSource,TransactionInterceptor 
  // 和BeanFactoryTransactionAttributeSourceAdvisor,并通過Aop的方式實(shí)現(xiàn)事務(wù) 
  AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext); 
 } return null; 
} 

可以看到,對于事務(wù)的驅(qū)動,這里主要做了兩件事:①注冊事務(wù)相關(guān)的事件觸發(fā)器,這些觸發(fā)器由用戶自行提供,在事務(wù)進(jìn)行提交或事務(wù)完成時會觸發(fā)相應(yīng)的方法;②判斷當(dāng)前事務(wù)驅(qū)動的模式,有默認(rèn)模式和aspectj模式,對于aspectj模式,Spring會注冊一個AnnotationTransactionAspect類型的bean,用于用戶使用更親近于aspectj的方式進(jìn)行事務(wù)處理;對于默認(rèn)模式,這里主要是聲明了三個bean,最終通過Aop的方式進(jìn)行事務(wù)切入。下面我們看一下Spring是如何注冊這三個bean的,如下是AopAutoProxyConfigurer.configureAutoProxyCreator的源碼:

public static void configureAutoProxyCreator(Element element, 
  ParserContext parserContext) { // 這個方法主要是在當(dāng)前BeanFactory中注冊InfrastructureAdvisorAutoProxyCreator這個 
 // bean,這個bean繼承了AbstractAdvisorAutoProxyCreator,也就是其實(shí)現(xiàn)原理與我們前面 
 // 講解的Spring Aop的實(shí)現(xiàn)原理幾乎一致 
 AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element); // 這里的txAdvisorBeanName就是我們最終要注冊的bean,其類型就是下面注冊的 
 // BeanFactoryTransactionAttributeSourceAdvisor,可以看到,其本質(zhì)是一個 
 // Advisor類型的對象,因而Spring Aop會將其作為一個切面織入到指定的bean中 
 String txAdvisorBeanName = TransactionManagementConfigUtils 
  .TRANSACTION_ADVISOR_BEAN_NAME; // 如果當(dāng)前BeanFactory中已經(jīng)存在了目標(biāo)bean,則不進(jìn)行注冊 
 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設(shè)置為其一個屬性。 
  // 這個bean本質(zhì)上是一個Advice(可查看其繼承結(jié)構(gòu)),Spring Aop使用Advisor封裝實(shí)現(xiàn)切面 
  // 邏輯織入所需的所有屬性,但真正的切面邏輯卻是保存在其Advice屬性中的,也就是說這里的 
  // TransactionInterceptor才是真正封裝了事務(wù)切面邏輯的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實(shí)現(xiàn)了 
  // Advisor接口,實(shí)際上就是封裝了當(dāng)前需要織入的切面的所有所需的屬性 
  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中,并且進(jìn)行注冊 
 
  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事務(wù)相關(guān)的標(biāo)簽即解析完成,這里主要是聲明了一個BeanFactoryTransactionAttributeSourceAdvisor類型的bean到BeanFactory中,其實(shí)際為Advisor類型,這也是Spring事務(wù)能夠通過Aop實(shí)現(xiàn)事務(wù)的根本原因。

3. 實(shí)現(xiàn)原理

關(guān)于Spring事務(wù)的實(shí)現(xiàn)原理,這里需要抓住的就是,其是使用Aop實(shí)現(xiàn)的,我們知道,Aop在進(jìn)行解析的時候,最終會生成一個Adivsor對象,這個Advisor對象中封裝了切面織入所需要的所有信息,其中就包括最重要的兩個部分就是Pointcut和Adivce屬性。這里Pointcut用于判斷目標(biāo)bean是否需要織入當(dāng)前切面邏輯;Advice則封裝了需要織入的切面邏輯。如下是這三個部分的簡要關(guān)系圖:


同樣的,對于Spring事務(wù),其既然是使用Spring Aop實(shí)現(xiàn)的,那么也同樣會有這三個成員。我們這里我們只看到了注冊的Advisor和Advice(即BeanFactoryTransactionAttributeSourceAdvisor和TransactionInterceptor),那么Pointcut在哪里呢?這里我們查看BeanFactoryTransactionAttributeSourceAdvisor的源碼可以發(fā)現(xiàn),其內(nèi)部聲明了一個TransactionAttributeSourcePointcut類型的屬性,并且直接在內(nèi)部進(jìn)行了實(shí)現(xiàn),這就是我們需要找的Pointcut。這里這三個對象對應(yīng)的關(guān)系如下:


這樣,用于實(shí)現(xiàn)Spring事務(wù)的Advisor,Pointcut以及Advice都已經(jīng)找到了。關(guān)于這三個類的具體作用,我們這里進(jìn)行整體的上的講解,后面我們將會深入其內(nèi)部講解其是如何進(jìn)行bean的過濾以及事務(wù)邏輯的織入的。

  •  BeanFactoryTransactionAttributeSourceAdvisor:封裝了實(shí)現(xiàn)事務(wù)所需的所有屬性,包括Pointcut,Advice,TransactionManager以及一些其他的在Transactional注解中聲明的屬性;
  •  TransactionAttributeSourcePointcut:用于判斷哪些bean需要織入當(dāng)前的事務(wù)邏輯。這里可想而知,其判斷的基本邏輯就是判斷其方法或類聲明上有沒有使用@Transactional注解,如果使用了就是需要織入事務(wù)邏輯的bean;
  •  TransactionInterceptor:這個bean本質(zhì)上是一個Advice,其封裝了當(dāng)前需要織入目標(biāo)bean的切面邏輯,也就是Spring事務(wù)是如果借助于數(shù)據(jù)庫事務(wù)來實(shí)現(xiàn)對目標(biāo)方法的環(huán)繞的。

4. 小結(jié)

本文首先通過一個簡單的例子講解了Spring事務(wù)是如何使用的,然后講解了Spring事務(wù)進(jìn)行標(biāo)簽解析的時候做了哪些工作,最后講解了Spring事務(wù)是如何與Spring Aop進(jìn)行一一對應(yīng)的,并且是如何通過Spring Aop實(shí)現(xiàn)將事務(wù)邏輯織入目標(biāo)bean的。

好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

相關(guān)文章

  • Java重寫equals及hashcode方法流程解析

    Java重寫equals及hashcode方法流程解析

    這篇文章主要介紹了Java重寫equals及hashcode方法流程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-04-04
  • java Socket UDP實(shí)例詳解

    java Socket UDP實(shí)例詳解

    這篇文章主要介紹了java Socket UDP實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下
    2017-02-02
  • Java多線程Runable售票系統(tǒng)實(shí)現(xiàn)過程解析

    Java多線程Runable售票系統(tǒng)實(shí)現(xiàn)過程解析

    這篇文章主要介紹了Java多線程Runable售票系統(tǒng)實(shí)現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-06-06
  • java中對象調(diào)用成員變量與成員實(shí)例方法

    java中對象調(diào)用成員變量與成員實(shí)例方法

    在本篇文章里小編給各位分享的是關(guān)于java中對象調(diào)用成員變量與成員實(shí)例方法,需要的朋友們可以學(xué)習(xí)參考下。
    2020-02-02
  • Java中String、StringBuffer和StringBuilder的區(qū)別與使用場景

    Java中String、StringBuffer和StringBuilder的區(qū)別與使用場景

    在Java編程中,String、StringBuffer和StringBuilder是用于處理字符串的常見類,它們在可變性、線程安全性和性能方面有所不同,具有一定的參考價值,感興趣的可以了解一下
    2024-05-05
  • Java?Git?Commit?Message使用規(guī)范

    Java?Git?Commit?Message使用規(guī)范

    這篇文章主要介紹了Java?Git?Commit?Message使用規(guī)范,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,感興趣的小伙伴可以參考一下,希望對你的學(xué)習(xí)有所幫助
    2022-08-08
  • 這一次搞懂Spring自定義標(biāo)簽以及注解解析原理說明

    這一次搞懂Spring自定義標(biāo)簽以及注解解析原理說明

    這篇文章主要介紹了這一次搞懂Spring自定義標(biāo)簽以及注解解析原理說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • spring mvc @PathVariable綁定URI模板變量值方式

    spring mvc @PathVariable綁定URI模板變量值方式

    這篇文章主要介紹了spring mvc @PathVariable綁定URI模板變量值方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Java中BigDecimal類的簡單用法

    Java中BigDecimal類的簡單用法

    這篇文章主要介紹了Java中BigDecimal類的簡單用法,是Java應(yīng)用程序開發(fā)中非常實(shí)用的技巧,本文以實(shí)例形式對此進(jìn)行了簡單的分析,需要的朋友可以參考下
    2014-09-09
  • SpringBoot復(fù)雜參數(shù)應(yīng)用詳細(xì)講解

    SpringBoot復(fù)雜參數(shù)應(yīng)用詳細(xì)講解

    我們在編寫接口時會傳入復(fù)雜參數(shù),如Map、Model等,這種類似的參數(shù)會有相應(yīng)的參數(shù)解析器進(jìn)行解析,并且最后會將解析出的值放到request域中,下面我們一起來探析一下其中的原理
    2022-09-09

最新評論

平昌县| 正蓝旗| 沛县| 沛县| 大连市| 宁强县| 双牌县| 扶沟县| 三原县| 神农架林区| 安平县| 嘉鱼县| 灵台县| 哈尔滨市| 托里县| 肥城市| 龙里县| 宁陕县| 石嘴山市| 宜宾县| 宁强县| 汝南县| 吉安县| 正蓝旗| 林甸县| 隆子县| 东源县| 曲靖市| 上杭县| 探索| 安福县| 洱源县| 红桥区| 嘉禾县| 绍兴市| 蓝田县| 怀远县| 怀集县| 彭泽县| 全椒县| 百色市|