You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
source-code-hunter/docs/Spring/SpringTransaction/Spring声明式事务处理.md

18 KiB

1 设计原理与基本过程

在使用Spring声明式事务处理的时候一种常用的方法是结合IoC容器和Spring已有的TransactionProxyFactoryBean对事务管理进行配置比如可以在这个TransactionProxyFactoryBean中为事务方法配置传播行为、并发事务隔离级别等事务处理属性从而对声明式事务的处理提供指导。具体来说在对声明式事务处理的原理分析中声明式事务处理的实现大致可以分为以下几个部分:

  • 读取和处理在IoC容器中配置的事务处理属性并转化为Spring事务处理需要的内部数据结构这里涉及的类是TransactionAttributeSourceAdvisor从名字可以看出它是一个AOP通知器Spring使用这个通知器来完成对事务处理属性值的处理。处理的结果是在IoC容器中配置的事务处理属性信息会被读入并转化成TransactionAttribute表示的数据对象这个数据对象是Spring对事物处理属性值的数据抽象对这些属性的处理是和TransactionProxyFactoryBean拦截下来的事务方法的处理结合起来的。
  • Spring事务处理模块实现统一的事务处理过程。这个通用的事务处理过程包含处理事务配置属性以及与线程绑定完成事务处理的过程Spring通过TransactionInfo和TransactionStatus这两个数据对象在事务处理过程中记录和传递相关执行场景。
  • 底层的事务处理实现。对于底层的事务操作Spring委托给具体的事务处理器来完成这些具体的事务处理器就是在IoC容器中配置声明式事务处理时配置的PlatformTransactionManager的具体实现比如DataSourceTransactionManager和HibernateTransactionManager等。

2 实现分析

2.1 事务处理拦截器的配置

和前面的思路一样从声明式事务处理的基本用法入手来了解它的基本实现原理。在使用声明式事务处理的时候需要在IoC容器中配置TransactionProxyFactoryBean见名知义这是一个FactoryBean有一个getObject()方法。在IoC容器进行注入的时候会创建TransactionInterceptor对象而这个对象会创建一个TransactionAttributePointcut为读取TransactionAttribute做准备。在容器初始化的过程中由于实现了InitializingBean接口因此AbstractSingletonProxyFactoryBean会实现afterPropertiesSet()方法正是在这个方法实例化了一个ProxyFactory建立起Spring AOP的应用在这里会为这个ProxyFactory设置通知、目标对象并最终返回Proxy代理对象。在Proxy代理对象建立起来以后在调用其代理方法的时候会调用相应的TransactionInterceptor拦截器在这个调用中会根据TransactionAttribute配置的事务属性进行配置从而为事务处理做好准备。

从TransactionProxyFactoryBean入手通过代码实现来了解Spring是如何通过AOP功能来完成事务管理配置的Spring为声明式事务处理的实现所做的一些准备工作包括为AOP配置基础设施这些基础设施包括设置拦截器TransactionInterceptor、通知器DefaultPointcutAdvisor或TransactionAttributeSourceAdvisor。同时在TransactionProxyFactoryBean的实现中 还可以看到注人进来的PlatformTransactionManager和事务处理属性TransactionAttribute等。

/**
 * 代理工厂bean用于简化声明式事务处理,这是标准AOP的一个方便的替代方案
 * 使用单独的TransactionInterceptor定义。
 */
@SuppressWarnings("serial")
public class TransactionProxyFactoryBean extends AbstractSingletonProxyFactoryBean
		implements BeanFactoryAware {

	/** 事务拦截器通过AOP来发挥作用spring在此拦截器中封装了 事务处理实现 */
	private final TransactionInterceptor transactionInterceptor = new TransactionInterceptor();

	/** 切面 */
	private Pointcut pointcut;


	/**
	 * 通过依赖注入的事务属性以properties的形式出现
	 * 把从beandefinition中读到的事务管理的属性信息注入到transactionInterceptor
	 */
	public void setTransactionManager(PlatformTransactionManager transactionManager) {
		this.transactionInterceptor.setTransactionManager(transactionManager);
	}

	/**
	 * 创建AOP对事务处理的advisor
	 * 本方法在IoC容器完成Bean的依赖注入时通过initializeBean方法被调用
	 */
	@Override
	protected Object createMainInterceptor() {
		this.transactionInterceptor.afterPropertiesSet();
		if (this.pointcut != null) {
			// 如果自己定义了切面,就使用默认的通知器,并为其配置事务处理拦截器
			return new DefaultPointcutAdvisor(this.pointcut, this.transactionInterceptor);
		}
		else {
			// 如果没定义则使用spring默认的切面使用TransactionAttributeSourceAdvisor
			// 作为通知器,并配置拦截器
			return new TransactionAttributeSourceAdvisor(this.transactionInterceptor);
		}
	}
	
	public void setTransactionAttributes(Properties transactionAttributes) {
		this.transactionInterceptor.setTransactionAttributes(transactionAttributes);
	}

	public void setTransactionAttributeSource(TransactionAttributeSource transactionAttributeSource) {
		this.transactionInterceptor.setTransactionAttributeSource(transactionAttributeSource);
	}

	public void setPointcut(Pointcut pointcut) {
		this.pointcut = pointcut;
	}

	public void setBeanFactory(BeanFactory beanFactory) {
		this.transactionInterceptor.setBeanFactory(beanFactory);
	}
}

以上代码完成了AOP配置对于用户来说一个值得关心的问题是Spring的TransactionInterceptor配置是在什么时候被启动并成为Advisor通知器的一部分的呢从对createMainInterceptor()方法的调用分析中可以看到这个createMainInterceptor()方法在IoC容器完成Bean的依赖注入时通过initializeBean()方法被调用,具体的调用过程如下图所示。

avatar

在TransactionProxyFactoryBean的父类AbstractSingletonProxyFactoryBean中的afterPropertiesSet()方法是Spring事务处理完成AOP配置的地方在建立TransactionProxyFactoryBean的事务处理拦截器的时候首先需要对ProxyFactoryBean的目标Bean设置进行检查如果这个目标Bean的设置是正确的就会创建一个ProxyFactory对象从而实现AOP的使用。在afterPropertiesSet()的方法实现中可以看到为ProxyFactory生成代理对象、配置通知器、设置代理接口方法等。

public abstract class AbstractSingletonProxyFactoryBean extends ProxyConfig
		implements FactoryBean<Object>, BeanClassLoaderAware, InitializingBean {

	private Object target;

	private Class<?>[] proxyInterfaces;

	private Object[] preInterceptors;

	private Object[] postInterceptors;

	/** Default is global AdvisorAdapterRegistry */
	private AdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.getInstance();

	private transient ClassLoader proxyClassLoader;

	private Object proxy;

	/**
	 * 处理完成AOP配置创建ProxyFactory对象为其生成代理对象
	 * 配置通知器、设置代理接口方法
	 */
	public void afterPropertiesSet() {
		
		// 校验target目标对象
		if (this.target == null) {
			throw new IllegalArgumentException("Property 'target' is required");
		}
		if (this.target instanceof String) {
			throw new IllegalArgumentException("'target' needs to be a bean reference, not a bean name as value");
		}
		if (this.proxyClassLoader == null) {
			this.proxyClassLoader = ClassUtils.getDefaultClassLoader();
		}

		// 使用ProxyFactory完成AOP的基本功能ProxyFactory提供proxy对象
		// 并将TransactionIntercepter设为target方法调用的拦截器
		ProxyFactory proxyFactory = new ProxyFactory();

		if (this.preInterceptors != null) {
			for (Object interceptor : this.preInterceptors) {
				proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(interceptor));
			}
		}

		// 加入Advisor通知器可以加入两种通知器分别是
		// DefaultPointcutAdvisor、TransactionAttributeSourceAdvisor
		// 这里通过调用TransactionProxyFactoryBean实例的createMainInterceptor()方法
		// 来生成需要的Advisors。在ProxyFactory的基类AdvisedSupport中维护了一个持有Advisor
		// 的链表LinkedList<Advisor>,通过对这个链表中的元素执行增、删、改等操作,用来管理
		// 配置给ProxyFactory的通知器
		proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(createMainInterceptor()));

		if (this.postInterceptors != null) {
			for (Object interceptor : this.postInterceptors) {
				proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(interceptor));
			}
		}

		proxyFactory.copyFrom(this);

		// 这里创建AOP的目标源与在其它地方使用ProxyFactory没什么差别
		TargetSource targetSource = createTargetSource(this.target);
		proxyFactory.setTargetSource(targetSource);

		if (this.proxyInterfaces != null) {
			proxyFactory.setInterfaces(this.proxyInterfaces);
		}
		else if (!isProxyTargetClass()) {
			// 需要根据AOP基础设施来确定使用哪个接口作为代理
			proxyFactory.setInterfaces(
					ClassUtils.getAllInterfacesForClass(targetSource.getTargetClass(), this.proxyClassLoader));
		}

		// 设置代理对象
		this.proxy = proxyFactory.getProxy(this.proxyClassLoader);
	}
}

DefaultAopProxyFactory创建AOP Proxy的过程在前面分析AOP的实现原理时已分析过这里就不再重复了。可以看到通过以上的一系列步骤Spring为实现事务处理而设计的拦截器TransctionInterceptor已经设置到ProxyFactory生成的AOP代理对象中去了这里的TransactionInterceptor是作为AOP Advice的拦截器来实现它的功能的。在IoC容器中配置其他与事务处理有关的属性比如比较熟悉的transactionManager和事务处理的属性也同样会被设置到已经定义好的TransactionInterceptor中去。这些属性配置在TransactionInterceptor对事务方法进行拦截时会起作用。在AOP配置完成以后可以看到在Spring声明式事务处理实现中的一些重要的类已经悄然登场比如TransactionAttributeSourceAdvisor和TransactionInterceptor正是这些类通过AOP封装了Spring对事务处理的基本实现。

2.2 事务处理配置的读入

在AOP配置完成的基础上以TransactionAttributeSourceAdvisor的实现为入 口,了解具体的事务属性配置是如何读入的。

public class TransactionAttributeSourceAdvisor extends AbstractPointcutAdvisor {

	/**
	 * 与其它Advisor一样同样需要定义AOP中用到的Interceptor和Pointcut
	 */
	private TransactionInterceptor transactionInterceptor;

	/**
	 * 对于切面pointcut这里使用了一个匿名内部类
	 */
	private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
		/**
		 * 通过transactionInterceptor来得到事务的配置属性在对Proxy的方法进行匹配调用时
		 * 会使用到这些配置属性
		 */
		@Override
		protected TransactionAttributeSource getTransactionAttributeSource() {
			return (transactionInterceptor != null ? transactionInterceptor.getTransactionAttributeSource() : null);
		}
	};
}

在声明式事务处理中通过对目标对象的方法调用进行拦截来实现事务处理的织入这个拦截通过AOP发挥作用。在AOP中对于拦截的启动首先需要对方法调用是否需要拦截进行判断而判断的依据是那些在TransactionProxyFactoryBean中为目标对象设置的事务属性。也就是说需要判断当前的目标方法调用是不是一个配置好的并且需要进行事务处理的方法调用。具体来说这个匹配判断在TransactionAttributeSourcePointcut的matches()方法中完成,该方法实现 首先把事务方法的属性配置读取到TransactionAttributeSource对象中有了这些事务处理的配置以后根据当前方法调用的Method对象和目标对象对是否需要启动事务处理拦截器进行判断。

abstract class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {

	public boolean matches(Method method, Class targetClass) {
		TransactionAttributeSource tas = getTransactionAttributeSource();
		return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
	}
}

在Pointcut的matches()判断过程中会用到TransactionAttributeSource对象这个TransactionAttributeSource对象是在对TransactionInterceptor进行依赖注入时就配置好的。它的设置是在TransactionInterceptor的基类TransactionAspectSupport中完成的配置的是一个NameMatchTransactionAttributeSource对象。

public abstract class TransactionAspectSupport implements BeanFactoryAware, InitializingBean {
	/**
	 * 设置事务属性以方法名为key事务属性描述符为value
	 * 例如key = "myMethod", value = "PROPAGATION_REQUIRED,readOnly"
	 */
	public void setTransactionAttributes(Properties transactionAttributes) {
		// 可以看到这是一个NameMatchTransactionAttributeSource的实现
		NameMatchTransactionAttributeSource tas = new NameMatchTransactionAttributeSource();
		tas.setProperties(transactionAttributes);
		this.transactionAttributeSource = tas;
	}
}

在以上的代码实现中可以看到NameMatchTransactionAttributeSource作为TransactionAttributeSource的具体实现是实际完成事务处理属性读入和匹配的地方。在对事务属性TransactionAttributes进行设置时会从事务处理属性配置中读取事务方法名和配置属性在得到配置的事务方法名和属性以后会把它们作为键值对加入到一个nameMap中。

在应用调用目标方法的时候因为这个目标方法已经被TransactionProxyFactoryBean代理所以TransactionProxyFactoryBean需要判断这个调用方法是否是事务方法。这个判断的实现是通过在NameMatchTransactionAttributeSource中能否为这个调用方法返回事务属性来完成的。具体的实现过程是这样的首先以调用方法名为索引在nameMap中查找相应的事务处理属性值如果能够找到那么就说明该调用方法和事务方法是直接对应的如果找不到那么就会遍历整个nameMap对保存在nameMap中的每一个方法名使用PatternMatchUtils的simpleMatch()方法进行命名模式上的匹配。这里使用PatternMatchUtils进行匹配的原因是在设置事务方法的时候可以不需要为事务方法设置一个完整的方法名而可以通过设置方法名的命名模式来完成比如可以通过对通配符*的使用等。所以如果直接通过方法名没能够匹配上而通过方法名的命名模式能够匹配上这个方法也是需要进行事务处理的相对应地它所配置的事务处理属性也会从nameMap中取出来从而触发事务处理拦截器的拦截。

public class NameMatchTransactionAttributeSource implements TransactionAttributeSource, Serializable {

	/** key是方法名value是事务属性 */
	private Map<String, TransactionAttribute> nameMap = new HashMap<String, TransactionAttribute>();

	/**
	 * 将给定属性transactionAttributes解析为名称/属性的map对象。以 方法名称 为键,
	 * 字符串属性定义 为值可通过TransactionAttributeEditor解析为TransactionAttribute实例。
	 */
	public void setProperties(Properties transactionAttributes) {
		TransactionAttributeEditor tae = new TransactionAttributeEditor();
		Enumeration propNames = transactionAttributes.propertyNames();
		while (propNames.hasMoreElements()) {
			String methodName = (String) propNames.nextElement();
			String value = transactionAttributes.getProperty(methodName);
			tae.setAsText(value);
			TransactionAttribute attr = (TransactionAttribute) tae.getValue();
			addTransactionalMethod(methodName, attr);
		}
	}

	/**
	 * 为事务方法添加属性
	 */
	public void addTransactionalMethod(String methodName, TransactionAttribute attr) {
		if (logger.isDebugEnabled()) {
			logger.debug("Adding transactional method [" + methodName + "] with attribute [" + attr + "]");
		}
		this.nameMap.put(methodName, attr);
	}
	
	/**
	 * 对调用的方法进行判断,判断它是否是事务方法,如果是事务方法,则取出相应的事务配置属性
	 */
	public TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass) {
		// 直接通过 方法名 匹配
		String methodName = method.getName();
		TransactionAttribute attr = this.nameMap.get(methodName);

		if (attr == null) {
			// 查找最具体的名称匹配
			String bestNameMatch = null;
			for (String mappedName : this.nameMap.keySet()) {
				if (isMatch(methodName, mappedName) &&
						(bestNameMatch == null || bestNameMatch.length() <= mappedName.length())) {
					attr = this.nameMap.get(mappedName);
					bestNameMatch = mappedName;
				}
			}
		}

		return attr;
	}

	/**
	 * 如果给定的方法名与映射的名称匹配,则返回
	 */
	protected boolean isMatch(String methodName, String mappedName) {
		return PatternMatchUtils.simpleMatch(mappedName, methodName);
	}

通过以上过程可以得到与目标对象调用方法相关的TransactionAttribute对象在这个对象中封装了事务处理的配置。具体来说在前面的匹配过程中如果匹配返回的结果是null那么说明当前的调用方法不是一个事务方法不需要纳入Spring统一的事务管理中因为它并没有配置在TransactionProxyFactoryBean的事务处理设置中。如果返回的TransactionAttribute对象不是null,那么这个返回的TransactionAttribute对象就已经包含了对事务方法的配置信息对应这个事务方法的具体事务配置也已经读入到TransactionAttribute对象中了为TransactionInterceptor做好了对调用的目标方法添加事务处理的准备。

2.3 事务处理拦截器的设计与实现