前言html
在spring jdbcTemplate 事務,各類詭異,包你醍醐灌頂!最後遺留了一個問題:spring是怎麼樣保證事務一致性的? 固然,spring事務內容挺多的,若是都要講的話要花很長時間,而本片博客的主旨是解決上一篇博客遺留的問題,那麼咱們把問題細化下來, 就是spring如何保證一個事務中的jdbc connection是同一個?node
沒有事務程序員
如若沒有事務,這個很好理解,能夠理解成spring只是對咱們通常的jdbc操做進行了一些封裝,減小了咱們的代碼量spring
一、通常寫法sql
代碼中的Connection的獲取有不少種方式,不必定是代碼中jdbcTemplate的方式,你們看的時候能夠假設成其餘的方式數據庫
public int insertOnePerson(String name, int age) { int result = 0; Connection conn = null; PreparedStatement pstmt = null; try { conn = jdbcTemplate.getDataSource().getConnection(); if(conn != null) { conn.setAutoCommit(false); pstmt = conn.prepareStatement(DELETE_ONE_PERSON); pstmt.setString(1, name); int count = pstmt.executeUpdate(); pstmt.close(); if(count >= 0) { pstmt = conn.prepareStatement(INSERT_ONE_PERSON); pstmt.setString(1, name); pstmt.setString(2, "1adh"); // 引起異常 result = pstmt.executeUpdate(); } conn.commit(); } } catch (SQLException e) { e.printStackTrace(); try { conn.rollback(); } catch (SQLException e1) { System.out.println("rollback failed.."); e1.printStackTrace(); } } finally { try{ conn.setAutoCommit(true); if(pstmt != null){ pstmt.close(); } if(conn != null){ conn.close(); } }catch(SQLException e){ } } return result ; }
二、spring jdbc的寫法編程
public int insertOnePerson(String name, int age) { int result = 0; int count = jdbcTemplate.update(DELETE_ONE_PERSON, new Object[]{name}); if(count >= 0) { result = jdbcTemplate.update(INSERT_ONE_PERSON, new Object[]{name,"l123a"}); } return result ; }
二者對比,就會發現咱們用spring jdbc的代碼量減小了不少,流程以下session
不考慮事務,一次數據庫操做就對應一個Connection,先後不一樣的操做對應的不一樣Connection,那麼每次對數據庫的請求是互不影響的。那麼spring真的是這麼實現的嗎? 暫且先留個懸念。app
存在事務ide
如如有須要用事務的需求,那麼咱們會怎麼實現了?
一、jdbc 事務
這個是最基本的,也是最容易想到的;關閉Connection的自動提交(con.setAutoCommit(false);),用同一個Connection執行不一樣的數據庫請求,最後提交(con.commit();),有異常則進行回滾(con.rollback();),最後釋放資源(若是有鏈接池,那麼con設置成自動提交便可(con.setAutoCommit(true);));
二、spring 事務
這種方式可能能想到,可是不少人都不理解,追根究底就是不瞭解spring事務的源碼
spring事務源碼解析
1、事務自定義標籤的解析
spring-tx-xx.jar中的包org.springframework.transaction.config下的init方法中有這麼一句:registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser()); 那麼咱們就從AnnotationDrivenBeanDefinitionParser的parse方法開始(爲何是從這個方法開始,後續會回答)
/** * Parses the {@code <tx:annotation-driven/>} tag. Will * {@link AopNamespaceUtils#registerAutoProxyCreatorIfNecessary register an AutoProxyCreator} * with the container as necessary. */ public BeanDefinition parse(Element element, ParserContext parserContext) { String mode = element.getAttribute("mode"); if ("aspectj".equals(mode)) { // mode="aspectj" registerTransactionAspect(element, parserContext); } else { // mode="proxy" AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext); } return null; }
1、跟進AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);這個方法註冊了代理類以及支撐起整個事務功能的三個bean:BeanFactoryTransactionAttributeSourceAdvisor、AnnotationTransactionAttributeSource、TransactionInterceptor,其中AnnotationTransactionAttributeSource和TransactionInterceptor被註冊到了BeanFactoryTransactionAttributeSourceAdvisor中
public static void configureAutoProxyCreator(Element element, ParserContext parserContext) { AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element); String txAdvisorBeanName = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME; if (!parserContext.getRegistry().containsBeanDefinition(txAdvisorBeanName)) { Object eleSource = parserContext.extractSource(element); // Create the TransactionAttributeSource definition. RootBeanDefinition sourceDef = new RootBeanDefinition( "org.springframework.transaction.annotation.AnnotationTransactionAttributeSource"); sourceDef.setSource(eleSource); sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef); // Create the TransactionInterceptor definition. 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); // Create the TransactionAttributeSourceAdvisor definition. 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); 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); } }
咱們先看代理類的註冊,跟進AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);接着跟進BeanDefinition beanDefinition = AopConfigUtils.registerAutoProxyCreatorIfNecessary(parserContext.getRegistry(), parserContext.extractSource(sourceElement));發現registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source),咱們先來看看InfrastructureAdvisorAutoProxyCreator類的層次結構圖,發現它間接實現了BeanPostProcessor接口,那麼spring在實例化bean的時候,都會保證調用其postProcessAfterInitialization方法(spring爲何必定會調用這個方法,後續會回答)
InfrastructureAdvisorAutoProxyCreator類的層次結構圖
2、跟進postProcessAfterInitialization方法(在類AbstractAutoProxyCreator中,注意看繼承關係),對指定的、咱們定義的bean進行封裝,封裝的工做委託給了調用了wrapIfNecessary(bean, beanName, cacheKey)方法
/** * Create a proxy with the configured interceptors if the bean is * identified as one to proxy by the subclass. * @see #getAdvicesAndAdvisorsForBean */ public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (!this.earlyProxyReferences.containsKey(cacheKey)) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }
3、跟進wrapIfNecessary方法,發現主要作了兩件事:(1)找出指定bean對應的加強器、(2)根據找出的加強器建立代理
/** * Wrap the given bean if necessary, i.e. if it is eligible for being proxied. * @param bean the raw bean instance * @param beanName the name of the bean * @param cacheKey the cache key for metadata access * @return a proxy wrapping the bean, or the raw bean instance as-is */ protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { if (beanName != null && this.targetSourcedBeans.containsKey(beanName)) { return bean; } if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { return bean; } if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } // Create proxy if we have advice. Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; }
(1)找出指定bean對應的加強器
跟進getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null)(當前正處於AbstractAutoProxyCreator中,而AbstractAdvisorAutoProxyCreator是AbstractAutoProxyCreator的子類、是InfrastructureAdvisorAutoProxyCreator的父類,而咱們的起點類是InfrastructureAdvisorAutoProxyCreator,因此跟進到AbstractAdvisorAutoProxyCreator中去)
@Override protected Object[] getAdvicesAndAdvisorsForBean(Class beanClass, String beanName, TargetSource targetSource) { List advisors = findEligibleAdvisors(beanClass, beanName); if (advisors.isEmpty()) { return DO_NOT_PROXY; } return advisors.toArray(); }
跟進findEligibleAdvisors(beanClass, beanName)方法,它主要作了兩件事:(a)獲取全部加強器、(b)加強器是否匹配
/** * Find all eligible Advisors for auto-proxying this class. * @param beanClass the clazz to find advisors for * @param beanName the name of the currently proxied bean * @return the empty List, not {@code null}, * if there are no pointcuts or interceptors * @see #findCandidateAdvisors * @see #sortAdvisors * @see #extendAdvisors */ protected List<Advisor> findEligibleAdvisors(Class beanClass, String beanName) { List<Advisor> candidateAdvisors = findCandidateAdvisors(); List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); extendAdvisors(eligibleAdvisors); if (!eligibleAdvisors.isEmpty()) { eligibleAdvisors = sortAdvisors(eligibleAdvisors); } return eligibleAdvisors; }
(a)獲取全部加強器,
跟進findCandidateAdvisors(),接着跟進this.advisorRetrievalHelper.findAdvisorBeans(),其中有這麼一句advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Advisor.class, true, false); 那麼BeanFactoryUtils.beanNamesForTypeIncludingAncestors的做用是什麼了,做用就是獲取beanFactory容器中的所有的Advisor.class類的名字,而當咱們知道了加強器在容器中的beanName時,獲取加強器就不是問題了, BeanFoctory中提供了方法:<T> T getBean(String name, Class<T> requiredType) throws BeansException;
不知道你們是否還記得BeanFactoryTransactionAttributeSourceAdvisor,看名字就知道他是一個Advisor,那麼他也理所固然的被提取出來,並隨着其餘加強器一塊兒在後續的步驟中被織入到代理中
/** * Find all eligible Advisor beans in the current bean factory, * ignoring FactoryBeans and excluding beans that are currently in creation. * @return the list of {@link org.springframework.aop.Advisor} beans * @see #isEligibleBean */ public List<Advisor> findAdvisorBeans() { // Determine list of advisor bean names, if not cached already. String[] advisorNames = null; synchronized (this) { advisorNames = this.cachedAdvisorBeanNames; if (advisorNames == null) { // Do not initialize FactoryBeans here: We need to leave all regular beans // uninitialized to let the auto-proxy creator apply to them! advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Advisor.class, true, false); this.cachedAdvisorBeanNames = advisorNames; } } if (advisorNames.length == 0) { return new LinkedList<Advisor>(); } List<Advisor> advisors = new LinkedList<Advisor>(); for (String name : advisorNames) { if (isEligibleBean(name)) { if (this.beanFactory.isCurrentlyInCreation(name)) { if (logger.isDebugEnabled()) { logger.debug("Skipping currently created advisor '" + name + "'"); } } else { try { advisors.add(this.beanFactory.getBean(name, Advisor.class)); } catch (BeanCreationException ex) { Throwable rootCause = ex.getMostSpecificCause(); if (rootCause instanceof BeanCurrentlyInCreationException) { BeanCreationException bce = (BeanCreationException) rootCause; if (this.beanFactory.isCurrentlyInCreation(bce.getBeanName())) { if (logger.isDebugEnabled()) { logger.debug("Skipping advisor '" + name + "' with dependency on currently created bean: " + ex.getMessage()); } // Ignore: indicates a reference back to the bean we're trying to advise. // We want to find advisors other than the currently created bean itself. continue; } } throw ex; } } } } return advisors; }
BeanFactoryTransactionAttributeSourceAdvisor類的層次結構圖
(b)加強器是否匹配
當找出全部的加強器後,接下來的任務就是看這些加強器是否與對應的class匹配了,固然不止class,class內部的方法若是匹配也能夠經過;咱們回到步驟4,跟進findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);咱們接着跟進AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass); 首先處理引介加強,引介是一種特殊的加強,它爲類添加一些屬性和方法,咱們能夠不用管;而後對於普通bean的處理,這是咱們須要關注的
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz)
/** * Determine the sublist of the {@code candidateAdvisors} list * that is applicable to the given class. * @param candidateAdvisors the Advisors to evaluate * @param clazz the target class * @return sublist of Advisors that can apply to an object of the given class * (may be the incoming List as-is) */ public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) { if (candidateAdvisors.isEmpty()) { return candidateAdvisors; } List<Advisor> eligibleAdvisors = new LinkedList<Advisor>(); // 處理引介加強 for (Advisor candidate : candidateAdvisors) { if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) { eligibleAdvisors.add(candidate); } } boolean hasIntroductions = !eligibleAdvisors.isEmpty(); for (Advisor candidate : candidateAdvisors) { // 引介加強已經處理 if (candidate instanceof IntroductionAdvisor) { // already processed continue; } // 普通bean的處理 if (canApply(candidate, clazz, hasIntroductions)) { eligibleAdvisors.add(candidate); } } return eligibleAdvisors; }
(b).一、跟進canApply(candidate, clazz, hasIntroductions),因爲咱們只關注咱們本身的加了事務的類,那麼clazz就表明這個類(這裏具體到類的話,就是我上篇博客附件中的類DaoImpl),candidate就表明BeanFactoryTransactionAttributeSourceAdvisor(只關注咱們須要關注的,簡化難度);根據BeanFactoryTransactionAttributeSourceAdvisor類的層次結構圖,咱們知道,BeanFactoryTransactionAttributeSourceAdvisor間接實現了PointcutAdvisor,那麼在canApply方法中的第二個if判斷時就會經過,會將BeanFactoryTransactionAttributeSourceAdvisor中的getPoint()方法的返回值做爲參數繼續調用canApply方法,那麼getPoint()方法返回的是什麼了,咱們跟進去看看。
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions)
/** * Can the given advisor apply at all on the given class? * <p>This is an important test as it can be used to optimize out a advisor for a class. * This version also takes into account introductions (for IntroductionAwareMethodMatchers). * @param advisor the advisor to check * @param targetClass class we're testing * @param hasIntroductions whether or not the advisor chain for this bean includes * any introductions * @return whether the pointcut can apply on any method */ public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) { if (advisor instanceof IntroductionAdvisor) { return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass); } else if (advisor instanceof PointcutAdvisor) { PointcutAdvisor pca = (PointcutAdvisor) advisor; return canApply(pca.getPointcut(), targetClass, hasIntroductions); } else { // It doesn't have a pointcut so we assume it applies. return true; } }
(b).二、跟進BeanFactoryTransactionAttributeSourceAdvisor的getPoint()方法,發現返回的是TransactionAttributeSourcePointcut類型的實例,對於transactionAttributeSource屬性你們還有印象嗎,在自定義標籤的時候注入進來的(步驟1),是AnnotationTransactionAttributeSource的實例
private TransactionAttributeSource transactionAttributeSource; private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() { @Override protected TransactionAttributeSource getTransactionAttributeSource() { return transactionAttributeSource; } }; public Pointcut getPointcut() { return this.pointcut; }
咱們回到步驟(b).1的canApply方法,繼續跟進第二個if中的canApply方法,此時此方法的參數Pointcut pc表示的TransactionAttributeSourcePointcut。此方法先獲取MethodMatcher:MethodMatcher methodMatcher = pc.getMethodMatcher(),經過代碼跟進就發現,pc.getMethodMatcher()返回的就是自身(TransactionAttributeSourcePointcut父類StaticMethodMatcherPointcut中的getMethodMatcher()),也就是pc本身;接着進行引介的處理,咱們不須要關注他;接着獲取目標類(這裏具體到類的話,就是我上篇博客附件中的類DaoImpl)的所有接口(Set<Class> classes = new LinkedHashSet<Class>(ClassUtils.getAllInterfacesForClassAsSet(targetClass)))以及目標類(classes.add(targetClass);),都放進set集合classes中;而後對classes進行遍歷,遍歷過程當中又對類中的方法進行遍歷,一旦匹配成功,就職務當前目標類適用於當前加強器(就是說DaoImpl適用於BeanFactoryTransactionAttributeSourceAdvisor)。
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions)
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) { Assert.notNull(pc, "Pointcut must not be null"); if (!pc.getClassFilter().matches(targetClass)) { return false; } MethodMatcher methodMatcher = pc.getMethodMatcher(); IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null; if (methodMatcher instanceof IntroductionAwareMethodMatcher) { introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher; } Set<Class> classes = new LinkedHashSet<Class>(ClassUtils.getAllInterfacesForClassAsSet(targetClass)); classes.add(targetClass); for (Class<?> clazz : classes) { Method[] methods = clazz.getMethods(); for (Method method : methods) { if ((introductionAwareMethodMatcher != null && introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) || methodMatcher.matches(method, targetClass)) { return true; } } } return false; }
咱們都知道事務的配置不只僅侷限於在方法上設置,類、接口上事務的配置能夠延伸到類中的每一個方法,那麼,若是針對每一個方法進行檢測,在類自己上配置的事務屬性豈不是檢測不到了嗎?帶着疑問咱們繼續跟進matches方法
(b).三、methodMatcher.matches(method, targetClass)用到的是TransactionAttributeSourcePointcut類的matches方法
public boolean matches(Method method, Class targetClass) { TransactionAttributeSource tas = getTransactionAttributeSource(); // 此時的tas表示AnnotationTransactionAttributeSource類型,在步驟6 return (tas == null || tas.getTransactionAttribute(method, targetClass) != null); // getTransactionAttribute方法在AnnotationTransactionAttributeSource的父類AbstractFallbackTransactionAttributeSource中
}
跟進getTransactionAttribute,裏面有個方法computeTransactionAttribute(method, targetClass)值得咱們關注
public TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass)
/** * Determine the transaction attribute for this method invocation. * <p>Defaults to the class's transaction attribute if no method attribute is found. * @param method the method for the current invocation (never {@code null}) * @param targetClass the target class for this invocation (may be {@code null}) * @return TransactionAttribute for this method, or {@code null} if the method * is not transactional */ public TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass) { // First, see if we have a cached value. Object cacheKey = getCacheKey(method, targetClass); Object cached = this.attributeCache.get(cacheKey); if (cached != null) { // Value will either be canonical value indicating there is no transaction attribute, // or an actual transaction attribute. if (cached == NULL_TRANSACTION_ATTRIBUTE) { return null; } else { return (TransactionAttribute) cached; } } else { // We need to work it out. TransactionAttribute txAtt = computeTransactionAttribute(method, targetClass); // Put it in the cache. if (txAtt == null) { this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE); } else { if (logger.isDebugEnabled()) { Class<?> classToLog = (targetClass != null ? targetClass : method.getDeclaringClass()); logger.debug("Adding transactional method '" + classToLog.getSimpleName() + "." + method.getName() + "' with attribute: " + txAtt); } this.attributeCache.put(cacheKey, txAtt); } return txAtt; } }
跟進computeTransactionAttribute,提取事務標籤,內容已經很明瞭,若是方法中存在事務屬性,則使用方法上的屬性,不然使用方法所在的類上的屬性,若是方法所在的類的屬性上仍是沒有搜尋到對應的事務屬性,那麼再搜尋接口中的方法,再沒有的話,最後嘗試搜尋接口的類上面的聲明
private TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass)
/** * Same signature as {@link #getTransactionAttribute}, but doesn't cache the result. * {@link #getTransactionAttribute} is effectively a caching decorator for this method. * @see #getTransactionAttribute */ private TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) { // Don't allow no-public methods as required. if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) { return null; } // Ignore CGLIB subclasses - introspect the actual user class. Class<?> userClass = ClassUtils.getUserClass(targetClass); // The method may be on an interface, but we need attributes from the target class. // If the target class is null, the method will be unchanged. Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass); // If we are dealing with method with generic parameters, find the original method. specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod); // First try is the method in the target class. TransactionAttribute txAtt = findTransactionAttribute(specificMethod); if (txAtt != null) { return txAtt; } // Second try is the transaction attribute on the target class. txAtt = findTransactionAttribute(specificMethod.getDeclaringClass()); if (txAtt != null) { return txAtt; } if (specificMethod != method) { // Fallback is to look at the original method. txAtt = findTransactionAttribute(method); if (txAtt != null) { return txAtt; } // Last fallback is the class of the original method. return findTransactionAttribute(method.getDeclaringClass()); } return null; }
具體的搜尋事務屬性的任務委託給了findTransactionAttribute方法,跟進進去,來到determineTransactionAttribute方法,繼續進去parseTransactionAnnotation方法(SpringTransactionAnnotationParser類的方法),進行事務註解解析
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) { Transactional ann = AnnotationUtils.getAnnotation(ae, Transactional.class); // 獲取Transactional註解 if (ann != null) { return parseTransactionAnnotation(ann); // 獲取到了Transactional註解了,則進行註解屬性的解析 } else { return null; } }
註解屬性的解析就不跟進去了,你們有興趣的能夠本身去跟;
至此,咱們完成了事務標籤的解析。再回顧下,咱們如今的任務是找出某個加強器是否適合於對應的類 ,而是否匹配的關鍵則在因而否從指定的類或類的方法中找到對應的事務屬性,如今,咱們以DaoImpl爲例,已經在他的方法上找到了事務屬性,因此,他是與事務加強器匹配的,也就是說他會被事務功能修飾。
(2)根據找出的加強器建立代理
這裏我就不跟了,你們本身去跟下
2、事務加強方法
事務加強器BeanFactoryTransactionAttributeSourceAdvisor做爲Advisor的實現類,天然要聽從Advisor的處理方式(處理方式是什麼,後面會有解答),當代理被調用時會調用這個類的加強方法,也就是此bean的Advise,又由於在解析事務標籤時咱們把TransactionInterceptor類型的bean注入到了BeanTransactionAttributeSourceAdvisor中,因此,在調用事務加強器加強的代理類時會首先執行TransactionInterceptor進行加強,同時,也就是在TransactionInterceptor類中的invoke方法中完成整個事務的邏輯(能夠去看下aop源碼實現,這裏就不作闡述了,篇幅已經很大了)
從TransactionInterceptor(步驟1有此類的出現)的invoke方法開始,跟進到invokeWithinTransaction方法,包括聲明式事務處理以及編程式事務處理,咱們這裏只看聲明式事務,包括事務建立,執行被加強方法(DaoImpl加了事務的方法)、異常回滾、提交事務。
protected Object invokeWithinTransaction(Method method, Class targetClass, final InvocationCallback invocation) throws Throwable
/** * General delegate for around-advice-based subclasses, delegating to several other template * methods on this class. Able to handle {@link CallbackPreferringPlatformTransactionManager} * as well as regular {@link PlatformTransactionManager} implementations. * @param method the Method being invoked * @param targetClass the target class that we're invoking the method on * @param invocation the callback to use for proceeding with the target invocation * @return the return value of the method, if any * @throws Throwable propagated from the target invocation */ protected Object invokeWithinTransaction(Method method, Class targetClass, final InvocationCallback invocation) throws Throwable { // If the transaction attribute is null, the method is non-transactional. final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass); final PlatformTransactionManager tm = determineTransactionManager(txAttr); final String joinpointIdentification = methodIdentification(method, targetClass); if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) { // Standard transaction demarcation with getTransaction and commit/rollback calls. TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification); Object retVal = null; try { // This is an around advice: Invoke the next interceptor in the chain. // This will normally result in a target object being invoked. retVal = invocation.proceedWithInvocation(); } catch (Throwable ex) { // target invocation exception completeTransactionAfterThrowing(txInfo, ex); throw ex; } finally { cleanupTransactionInfo(txInfo); } commitTransactionAfterReturning(txInfo); return retVal; } else { // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in. try { Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, new TransactionCallback<Object>() { public Object doInTransaction(TransactionStatus status) { TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status); try { return invocation.proceedWithInvocation(); } catch (Throwable ex) { if (txAttr.rollbackOn(ex)) { // A RuntimeException: will lead to a rollback. if (ex instanceof RuntimeException) { throw (RuntimeException) ex; } else { throw new ThrowableHolderException(ex); } } else { // A normal return value: will lead to a commit. return new ThrowableHolder(ex); } } finally { cleanupTransactionInfo(txInfo); } } }); // Check result: It might indicate a Throwable to rethrow. if (result instanceof ThrowableHolder) { throw ((ThrowableHolder) result).getThrowable(); } else { return result; } } catch (ThrowableHolderException ex) { throw ex.getCause(); } } }
一、事務建立
跟進createTransactionIfNecessary,接着跟進status = tm.getTransaction(txAttr); 能夠看裏面相應的註釋來了解這個方法作了什麼
/** * This implementation handles propagation behavior. Delegates to * {@code doGetTransaction}, {@code isExistingTransaction} * and {@code doBegin}. * @see #doGetTransaction * @see #isExistingTransaction * @see #doBegin */ public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException { Object transaction = doGetTransaction(); //獲取事務實例 // Cache debug flag to avoid repeated checks. boolean debugEnabled = logger.isDebugEnabled(); if (definition == null) { // Use defaults if no transaction definition given. definition = new DefaultTransactionDefinition(); } if (isExistingTransaction(transaction)) { // 內嵌事務處理,先不關注 // Existing transaction found -> check propagation behavior to find out how to behave. return handleExistingTransaction(definition, transaction, debugEnabled); } // Check definition settings for new transaction. if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) { // 超時設置 throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout()); } // No existing transaction found -> check propagation behavior to find out how to proceed. if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) { throw new IllegalTransactionStateException( "No existing transaction found for transaction marked with propagation 'mandatory'"); } else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED || definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW || definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { SuspendedResourcesHolder suspendedResources = suspend(null); if (debugEnabled) { logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition); } try { boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); DefaultTransactionStatus status = newTransactionStatus( definition, transaction, true, newSynchronization, debugEnabled, suspendedResources); // 構建transaction,包括ConnectionHolder、隔離級別、timeout,若是是新鏈接,綁定到當前線程 這是咱們須要關注的點 doBegin(transaction, definition); prepareSynchronization(status, definition); return status; } catch (RuntimeException ex) { resume(null, suspendedResources); throw ex; } catch (Error err) { resume(null, suspendedResources); throw err; } } else { // Create "empty" transaction: no actual transaction, but potentially synchronization. boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS); return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null); } }
那麼咱們把目光彙集在咱們須要關注的點:doBegin方法上(DataSourceTransactionManager類中)
/** * This implementation sets the isolation level but ignores the timeout. */ @Override protected void doBegin(Object transaction, TransactionDefinition definition) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; Connection con = null; try { // 嘗試獲取鏈接 if (txObject.getConnectionHolder() == null || txObject.getConnectionHolder().isSynchronizedWithTransaction()) { Connection newCon = this.dataSource.getConnection(); if (logger.isDebugEnabled()) { logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction"); } txObject.setConnectionHolder(new ConnectionHolder(newCon), true); } txObject.getConnectionHolder().setSynchronizedWithTransaction(true); con = txObject.getConnectionHolder().getConnection(); // 設置隔離級別 Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition); txObject.setPreviousIsolationLevel(previousIsolationLevel); // Switch to manual commit if necessary. This is very expensive in some JDBC drivers, // so we don't want to do it unnecessarily (for example if we've explicitly // configured the connection pool to set it already). // 更改自動提交設置,有spring控制提交 if (con.getAutoCommit()) { txObject.setMustRestoreAutoCommit(true); if (logger.isDebugEnabled()) { logger.debug("Switching JDBC Connection [" + con + "] to manual commit"); } con.setAutoCommit(false); } // 設置當前線程是否存在事務的依據 txObject.getConnectionHolder().setTransactionActive(true); int timeout = determineTimeout(definition); if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) { txObject.getConnectionHolder().setTimeoutInSeconds(timeout); } // Bind the session holder to the thread. if (txObject.isNewConnectionHolder()) { // 將當前獲取到的鏈接綁定到當前線程 TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder()); } } catch (Throwable ex) { if (txObject.isNewConnectionHolder()) { DataSourceUtils.releaseConnection(con, this.dataSource); txObject.setConnectionHolder(null, false); } throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex); } }
從doBegin方法中,咱們其實已經找到了最初問題(spring如何保證一個事務中的jdbc connection是同一個)的答案,就是將鏈接綁定到線程,那麼同一個線程中用到的就是同一個connection;到了這裏不知道你們有沒有這樣一個疑問:一個業務處理中須要對數據庫進行屢次操做,每次數據庫操做都從新從鏈接池獲取connection,那麼將connection綁定到線程有什麼用,仍是不能保證屢次獲取的connection是同一個connection? 我本身也有過這樣的疑問,但是咱們仔細回顧一下這個疑問,connection綁定到線程了,那麼每次獲取connection仍是從鏈接池嗎? 不知道你們有沒有這麼考慮過;若是你們還沒想明白,咱們就來跟下jdbc操做中connection的獲取是怎麼樣的
jdbc操做中connection的獲取
我就不具體的一步一步的去跟進了,咱們來看一下關鍵代碼,JdbcTemplate類中有個execute方法,他是執行數據庫操做的核心,這裏咱們也只關注數據庫鏈接的獲取,其餘的你們本身去閱讀就行了
public <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action) throws DataAccessException
public <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action) throws DataAccessException { Assert.notNull(psc, "PreparedStatementCreator must not be null"); Assert.notNull(action, "Callback object must not be null"); if (logger.isDebugEnabled()) { String sql = getSql(psc); logger.debug("Executing prepared SQL statement" + (sql != null ? " [" + sql + "]" : "")); } Connection con = DataSourceUtils.getConnection(getDataSource()); PreparedStatement ps = null; try { Connection conToUse = con; if (this.nativeJdbcExtractor != null && this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativePreparedStatements()) { conToUse = this.nativeJdbcExtractor.getNativeConnection(con); } ps = psc.createPreparedStatement(conToUse); applyStatementSettings(ps); PreparedStatement psToUse = ps; if (this.nativeJdbcExtractor != null) { psToUse = this.nativeJdbcExtractor.getNativePreparedStatement(ps); } T result = action.doInPreparedStatement(psToUse); handleWarnings(ps); return result; } catch (SQLException ex) { // Release Connection early, to avoid potential connection pool deadlock // in the case when the exception translator hasn't been initialized yet. if (psc instanceof ParameterDisposer) { ((ParameterDisposer) psc).cleanupParameters(); } String sql = getSql(psc); psc = null; JdbcUtils.closeStatement(ps); ps = null; DataSourceUtils.releaseConnection(con, getDataSource()); con = null; throw getExceptionTranslator().translate("PreparedStatementCallback", sql, ex); } finally { if (psc instanceof ParameterDisposer) { ((ParameterDisposer) psc).cleanupParameters(); } JdbcUtils.closeStatement(ps); DataSourceUtils.releaseConnection(con, getDataSource()); } }
咱們跟進execute方法中的Connection con = DataSourceUtils.getConnection(getDataSource()); 去看看到底鏈接是怎麼獲取的,跟進發現connection獲取的操做交給了 doGetConnection(dataSource),繼續跟進
public static Connection doGetConnection(DataSource dataSource) throws SQLException
/** * Actually obtain a JDBC Connection from the given DataSource. * Same as {@link #getConnection}, but throwing the original SQLException. * <p>Is aware of a corresponding Connection bound to the current thread, for example * when using {@link DataSourceTransactionManager}. Will bind a Connection to the thread * if transaction synchronization is active (e.g. if in a JTA transaction). * <p>Directly accessed by {@link TransactionAwareDataSourceProxy}. * @param dataSource the DataSource to obtain Connections from * @return a JDBC Connection from the given DataSource * @throws SQLException if thrown by JDBC methods * @see #doReleaseConnection */ public static Connection doGetConnection(DataSource dataSource) throws SQLException { Assert.notNull(dataSource, "No DataSource specified");
// 當前線程若存在connection,那麼直接返回線程中的connection ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource); if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) { conHolder.requested(); if (!conHolder.hasConnection()) { logger.debug("Fetching resumed JDBC Connection from DataSource"); conHolder.setConnection(dataSource.getConnection()); } return conHolder.getConnection(); } // Else we either got no holder or an empty thread-bound holder here. logger.debug("Fetching JDBC Connection from DataSource");
// 從鏈接池中獲取connection Connection con = dataSource.getConnection(); if (TransactionSynchronizationManager.isSynchronizationActive()) { logger.debug("Registering transaction synchronization for JDBC Connection"); // Use same Connection for further JDBC actions within the transaction. // Thread-bound object will get removed by synchronization at transaction completion. ConnectionHolder holderToUse = conHolder; if (holderToUse == null) { holderToUse = new ConnectionHolder(con); } else { holderToUse.setConnection(con); } holderToUse.requested(); TransactionSynchronizationManager.registerSynchronization( new ConnectionSynchronization(holderToUse, dataSource)); holderToUse.setSynchronizedWithTransaction(true); if (holderToUse != conHolder) { TransactionSynchronizationManager.bindResource(dataSource, holderToUse); } } return con; }
咱們發現,若是當前線程存在connection,說明當前業務須要事務,那麼就將當前線程中的connection返回回去,不然就從鏈接池中獲取connection並返回回去;到這裏,相信你們就明白了。
總結
篇幅不小,一不當心可能就跟丟了,我這裏再總結一下,回顧下事務源碼實現的整個流程
一、事務標籤的解析,註冊事務相關的類到容器中,代理類以及事務功能的三個bean:BeanFactoryTransactionAttributeSourceAdvisor、AnnotationTransactionAttributeSource、TransactionInterceptor
二、找出指定bean對應的加強器,具體一點,就是找到DaoImpl的加強器BeanFactoryTransactionAttributeSourceAdvisor,也就是說BeanFactoryTransactionAttributeSourceAdvisor適用於DaoImpl
三、建立代理,將加強方法與目標方法集合,實現加強效果,實際上就是將TransactionInterceptor與DaoImpl結合,對DaoImpl的目標方法(@Transactional標註的,固然不止這一種方式)進行around加強,這裏有一個很重要的操做:將connection綁定到當前線程
四、jdbc操做,先判斷當前線程是否有connection,有則直接返回,那麼就保證了目標方法中的全部數據庫操做用的是同一個connection,不然則會每次數據庫操做都從鏈接池中獲取connection
一旦事務建立成功,那麼咱們獲取的是目標bean的代理,而不是目標bean對應的類生成的bean,代理在目標bean的基礎上進行了around加強;
事務也是aop實現的一種具體表現,若是你們對aop有所理解的話,那麼事務應該很好理解,若是你們對aop沒有了解,那麼我建議你們如今就去了解(最好將aop的源碼讀一遍),再回過頭來看這篇博客,那麼就很好理解了;
可能有人會有這樣的疑問:你說了這麼多,也就只是保證全部數據庫操做用的是同一個connection,這可不能算事務完成了,事務的內容可不止這些,包括各類事務的提交、回滾等等;事務的提交、回滾等,spring管理起來了,無需咱們管理,這也是spring事務帶給咱們的便利;另外,本博客旨在說明spring一個事務中的jdbc connection是同一個,至於其餘的就須要你們本身去閱讀源碼了
後話及疑問解答
博客中提到了3個問題:一、有事務與沒事務,connection的獲取方式是同樣的嗎,二、事務自定義標籤解析,爲何是從AnnotationDrivenBeanDefinitionParser的parse方法開始,三、spring在實例化bean的時候,爲何會調用postProcessAfterInitialization方法
關於問題1:其實這個問題應該是這樣的:有事務與沒事務,jdbc操做中connection獲取的方式是同樣的嗎,我想這個你們應該已經有答案了,兩種情形共用的是一套相同的獲取connection的代碼,就是上文中提到的doGetConnection方法,那麼獲取方式確定相同啦
關於問題2:spring配置文件中,有一個配置<tx:annotation-driven />,此配置是事務的開關;你們能夠打斷點去跟下標籤的解析,包括默認標籤、自定義標籤,找到以下方法,跟進parseCustomElement方法就能找到答案了
/** * Parse the elements at the root level in the document: * "import", "alias", "bean". * @param root the DOM root element of the document */ protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); // 默認標籤解析 } else { delegate.parseCustomElement(ele); // 自定義標籤解析 } } } } else { delegate.parseCustomElement(root); } }
關於問題3,這個也得須要你們去跟源代碼,當你找到以下源代碼的時候,你就會找到答案了
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) { result = beanProcessor.postProcessAfterInitialization(result, beanName); if (result == null) { return result; } } return result; }
本博客篇幅不小,沒讀過spring源碼的人確定是看不懂的,因此須要你們去看spring的源代碼;建議你們看源代碼的時候最後配合斷點調試,來跟蹤代碼,若是直接跟源代碼的話,很容易跟丟;另外,若是你們以爲看全英文的比較吃力,那麼就配合着中文書籍來看,<<spring源碼深度解析>>這本書我的以爲,只要你能耐心看的話,是一本不錯的書!
參考:
<<spring源碼深度解析>>