`
michael.softtech
  • 浏览: 208575 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

spring源码分析之——spring 事务管理实现方式

阅读更多

注意:这里只是分析spring事务的实现方式。也就是spring的事务管理是怎么发生作用的,而不分析具体的实现细节(细节将在下一篇里面分析).

 

紧接着上一篇提到的,Spring是通过NameSpaceHandler来解析配置文件中的标签的。下面就已事务的配置为例,讲述一下

事务配置的标签的解析过程,从来理解事物是如何通过aop产生作用的。

 

	<!-- 以AspectJ方式 定义 AOP -->
	<aop:config proxy-target-class="true">
		<aop:advisor pointcut="execution(* commo.base.BaseManager.*(..))" advice-ref="txAdvice"/>
		<aop:advisor pointcut="execution(* com.*..*.service.*Manager.*(..))" advice-ref="txAdvice"/>
	</aop:config>

	<!-- 基本事务定义,使用transactionManager作事务管理,默认get* find*方法的事务为readonly,其余方法按默认设置.
			 默认的设置请参考Spring文档事务一章. -->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="find*" read-only="true"/>
			<tx:method name="get*" read-only="true"/>
			<tx:method name="query*" read-only="true"/>
			<tx:method name="*" read-only="false"/>
		</tx:attributes>
	</tx:advice>

 以上的配置相信很多人已经很熟悉了,在此不赘述。而是具体分析一下原理。

 先来分析<tx:advice>...</tx:advice>。

tx是TransactionNameSpace。对应的是handler是TxNamespaceHandler.

这个类一个init方法:

 

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

这个方法是在DefaultNamespaceHandlerResolver的resolve中调用的。在为对应的标签寻找namespacehandler的时候,调用这个resolve方法。resolve方法先寻找namespaceUri对应的namespacehandler,如果找到了就先调用Init方法。

 

    OK.我们的<tx:advice>对应的解析器也注册了,那就是上面代码里面的

new TxAdviceBeanDefinitionParser()

    那么,这个解析器是什么时候调用的哪?

    上一篇提到了,对应标签解析时会先选择namespacehandler,然后调用其parse方法。

    TxNamespaceHandler的parse方法在其父类NamespaceHandlerSupport中,代码如下:

 

public BeanDefinition parse(Element element, ParserContext parserContext) {
		return findParserForElement(element, parserContext).parse(element, parserContext);
	}

   这下明白了吧?<tx:advice>在解析出来的Document里面是一个Element,而这个Element的parse就是上面注册了的

TxAdviceBeanDefinitionParser

  现在这个parser的parse方法在NamespaceHandlerSupport的parse方法中被调用了,下面我们来看看这个

  TxAdviceBeanDefinitionParser的parse方法吧,这个方法在TxAdviceBeanDefinitionParser的祖父类AbstractBeanDefinitionParser中:

 

public final BeanDefinition parse(Element element, ParserContext parserContext) {
		AbstractBeanDefinition definition = parseInternal(element, parserContext);
		if (definition != null && !parserContext.isNested()) {
			try {
				String id = resolveId(element, definition, parserContext);
				if (!StringUtils.hasText(id)) {
					parserContext.getReaderContext().error(
							"Id is required for element '" + parserContext.getDelegate().getLocalName(element)
									+ "' when used as a top-level tag", element);
				}
				String[] aliases = new String[0];
				String name = element.getAttribute(NAME_ATTRIBUTE);
				if (StringUtils.hasLength(name)) {
					aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
				}
				BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
				registerBeanDefinition(holder, parserContext.getRegistry());
				if (shouldFireEvents()) {
					BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);
					postProcessComponentDefinition(componentDefinition);
					parserContext.registerComponent(componentDefinition);
				}
			}
			catch (BeanDefinitionStoreException ex) {
				parserContext.getReaderContext().error(ex.getMessage(), element);
				return null;
			}
		}
		return definition;
	}
 

   注意其中这样一行:

 

AbstractBeanDefinition definition = parseInternal(element, parserContext);

    这个parseInternal是在TxAdviceBeanDefinitionParser的父类AbstractSingleBeanDefinitionParser中实现的,代码如下:

 

@Override
	protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
		BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
		String parentName = getParentName(element);
		if (parentName != null) {
			builder.getRawBeanDefinition().setParentName(parentName);
		}
		Class beanClass = getBeanClass(element);
		if (beanClass != null) {
			builder.getRawBeanDefinition().setBeanClass(beanClass);
		}
		else {
			String beanClassName = getBeanClassName(element);
			if (beanClassName != null) {
				builder.getRawBeanDefinition().setBeanClassName(beanClassName);
			}
		}
		builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
		if (parserContext.isNested()) {
			// Inner bean definition must receive same scope as containing bean.
			builder.setScope(parserContext.getContainingBeanDefinition().getScope());
		}
		if (parserContext.isDefaultLazyInit()) {
			// Default-lazy-init applies to custom bean definitions as well.
			builder.setLazyInit(true);
		}
		doParse(element, parserContext, builder);
		return builder.getBeanDefinition();
	}

    其中有一行:

 

Class beanClass = getBeanClass(element);

   getBeanClass是在TxAdviceBeanDefinitionParser中实现的,很简单:

 

@Override
	protected Class getBeanClass(Element element) {
		return TransactionInterceptor.class;
	}

  至此,这个标签解析的流程已经基本清晰了。那就是:解析除了一个以TransactionInerceptor为classname的beandefinition并且注册这个bean。剩下来要看的,就是这个TranscationInterceptor到底是什么?

 看看这个类的接口定义,就明白了:

 

public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable

  这根本就是一个spring AOP的advice嘛!现在明白为什么事务的配置能通过aop产生作用了吧?

  下面具体看看这个advice的advice:

 

public Object invoke(final MethodInvocation invocation) throws Throwable {
		// Work out the target class: may be <code>null</code>.
		// The TransactionAttributeSource should be passed the target class
		// as well as the method, which may be from an interface.
		Class targetClass = (invocation.getThis() != null ? invocation.getThis().getClass() : null);

		// If the transaction attribute is null, the method is non-transactional.
		final TransactionAttribute txAttr =
				getTransactionAttributeSource().getTransactionAttribute(invocation.getMethod(), targetClass);
		final PlatformTransactionManager tm = determineTransactionManager(txAttr);
		final String joinpointIdentification = methodIdentification(invocation.getMethod());

		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.proceed();
			}
			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.proceed();
								}
								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();
			}
		}
	}
 

   哦,原来就是在这里控制了method invocation(spring aop是基于method的)!根据我们的配置,来决定

  是不是对method使用事务。

 

   至此,spring的事务管理已经基本清晰了。就是解析出一个advice bean(of class : TransactionInterceptor)来,

   然后在aop中配置pointcut和这个advice,就能产生作用了!

 

   当然,这里没有分析事物控制的细节以及spring aop的原理,这些在接下来的章节里面会慢慢补全~

分享到:
评论
1 楼 weipeng1986 2013-09-13  
很不错,说的很好。能不能说说你是怎么找的,或者说是怎么把这些知识点串起来的,授人予鱼不如授人予鱼

相关推荐

    五、Spring源码分析——Spring Aop

    《Spring AOP 源码分析》 在深入探讨Spring AOP之前,我们先要理解AOP(面向切面编程)的基本概念。AOP是一种编程范式,它将关注点分离,使得我们可以将横切关注点(如日志、事务管理、安全检查等)与业务逻辑解耦...

    四、Spring源码分析——动态代理

    在Spring框架中,动态代理是实现AOP(面向切面编程)的核心技术之一。它允许我们在不修改原有代码的情况下,为方法添加额外的功能,如事务管理、日志记录等。本篇文章将深入探讨Spring中的动态代理机制,以及它是...

    Spring5 源码分析(第 2 版) .zip

    总的来说,《Spring5 源码分析(第 2 版)》这本书不仅涵盖了Spring5的主要特性和组件,还深入探讨了其实现原理,是Java开发者深入理解Spring框架、提升技术水平的宝贵资源。通过对书中源码的阅读和理解,读者可以更好...

    Spring源码解析.zip

    本压缩包“Spring源码解析”提供了对Spring框架核心组件——IOC(Inversion of Control,控制反转)、AOP(Aspect Oriented Programming,面向切面编程)以及Transaction(事务管理)的源码分析,帮助开发者更全面地...

    Spring5 源码分析(第 2 版)-某Tom老师

    文档将详细介绍Spring5中AspectJ的使用,包括切点定义、通知类型和AOP代理的实现方式。 接着,Spring5强化了对Web开发的支持,特别是Spring MVC。Spring MVC是构建RESTful Web服务的重要工具,它通过Model-View-...

    Spring注解驱动开发第35讲——声明式事务原理的源码分析

    Spring注解驱动开发第35讲——声明式事务原理的源码分析

    spring 源码中文注释

    在源码分析的过程中,读者会深入理解Spring的内部工作机制,例如如何解析配置、如何创建bean实例、如何实现AOP代理等。这将有助于开发者编写更高效、更健壮的代码,也能为参与Spring的扩展或定制打下坚实基础。 总...

    spring高级源码分析

    《Spring高级源码分析》是针对Java开发人员深入理解Spring框架的一份宝贵资源。Spring作为Java企业级应用的基石,其强大的功能和灵活性源于其深厚的设计理念和精巧的源码实现。本分析将深入探讨Spring的核心机制,...

    ProSpring——Spring专业开发指南

    书中可能包含如何阅读和分析Spring源码的方法和技巧。 10. **工具的使用**: 除了理论知识,本书可能还会介绍一些辅助开发的工具,如IDE集成、构建工具Maven或Gradle,以及调试和测试工具的使用方法。 在提供的...

    spring源码UML图

    《Spring框架源码分析——基于UML图的解读》 在深入探讨Spring框架源码之前,我们首先要理解什么是UML(统一建模语言)。UML是一种标准的图形化建模语言,用于软件设计和系统分析,它通过图表来表示系统的结构、...

    spring源码解析和mybatis学习

    6. **Spring源码分析**:通过阅读源码,理解Spring框架的工作原理,增强对框架的深入理解。 接下来,我们转向MyBatis,这是一个轻量级的持久层框架,它提供了灵活的SQL映射机制,使得数据库操作变得简单。"MyBatis3...

    Spring有关资料 源码

    4. **IoC容器**:IoC(Inversion of Control,控制反转)是DI的一种实现方式,Spring通过IoC容器管理对象,控制权由容器转移到了框架,开发者只需定义对象及其依赖关系,剩下的交给Spring处理。 5. **AOP代理**:...

    spring-jdbc.rar源码 学习分析用

    Spring JDBC的事务管理也是其关键特性之一。在源码中,我们可以看到TransactionManager接口及其实现,如DataSourceTransactionManager,它是Spring对数据库事务进行控制的核心。当开启事务时,Spring会通过保存点或...

    spring源码可导入eclipse版本 第五部分

    在本资源中,我们提供了可以直接导入Eclipse的Spring源码的第五部分,版本为4.3.18。这一版本的Spring包含了众多改进和修复,对于理解Spring的工作原理以及进行自定义开发非常有帮助。 首先,让我们深入了解一下...

    spring源码, 可以很好的学习spring源码, 对spring更深入理解

    这些切面可以在不修改原有代码的情况下被插入到系统中,如日志、事务管理等。AOP通过代理模式实现,Spring支持JDK动态代理和CGLIB字节码生成两种方式。 再来,Spring MVC是Spring用于构建Web应用的模块,它提供了...

    Spring学习笔记+学习源码.zip

    实践是检验真理的唯一标准,通过分析和运行源码,你将能更好地掌握Spring框架的精髓。 总之,这份资料对于Spring初学者或希望深入理解Spring的开发者来说是一份宝贵的资源。通过系统学习并结合实践,你将能够熟练地...

    会员管理系统jar源码下载(struts+hibernate+spring).zip

    这是一个基于Java技术栈的会员管理系统源码,使用了经典的SSH框架——Struts、Hibernate和Spring。这个系统的主要目的是实现对会员信息的有效管理和操作,通过这三个框架的集成,实现了业务层、持久层和表现层的解耦...

    ASP.NET源码——达达ASP.NET企业信息管理系统(Spring.NET修改版).zip

    【ASP.NET源码——达达ASP.NET企业信息管理系统(Spring.NET修改版)】 ASP.NET是由微软公司开发的一种Web应用程序框架,用于构建动态、数据驱动的Web应用。本系统是基于ASP.NET进行开发,专为企业信息管理设计,同时...

    java毕业设计&课设-spring源码深度解析+注解开发全套视频教程(视频+源码).doc

    ### Java毕业设计&课设——Spring源码深度解析+注解开发全套视频教程知识点概览 #### 一、Spring框架简介 Spring框架是由Rod Johnson创建的一款开源轻量级Java EE应用框架,旨在简化企业级应用的开发过程。Spring...

    Spring源码分析

    ### Spring源码分析 #### Spring框架概述 Spring框架是一个开源框架,它于2003年由Rod Johnson创建,旨在简化企业级应用的开发过程。Spring框架最初的理念来源于Rod Johnson在其著作《Expert One-On-One J2EE ...

Global site tag (gtag.js) - Google Analytics