天天看点

Spring框架浅析 -- 数据库事务处理什么是数据库事务为什么Spring需要对数据库事务处理进行支持Spring提供了哪些方式支持数据库事务Spring声明式事务处理的原理声明式事务处理使用须知

概述

在Spring框架浅析 -- 概述中,我们介绍过,Spring对于数据库操作及事务处理的支持,是Spring功能中较为重要的一环。那么数据库事务是什么?为什么要Spring需要对数据库操作及事务处理进行支持?Spring都提供了哪些方式对数据库事务处理进行支持?这些方式中又有哪种比较适合当前工程开发的实践?在本文中,我们将对这些问题一一展开阐述。

什么是数据库事务

相信这个问题,具备J2EE开发经验的同学都不陌生。详情可见:https://zh.wikipedia.org/zh-hans/%E6%95%B0%E6%8D%AE%E5%BA%93%E4%BA%8B%E5%8A%A1

为什么Spring需要对数据库事务处理进行支持

对于J2EE领域的开发,通常都需要引入关系型数据库用来存储关系型数据。作为框架,Spring当然要提供通用的数据库操作、数据库事务处理的功能,从而减少业务方重复造轮子,重复封装这些功能,向上屏蔽底层操作不同数据库、管理数据库事务的细节。试想,如果Spring不提供这些功能,每次开发新业务,都需要引入一大堆重复的获取数据库连接,开启数据库事务,关闭数据库事务,回滚数据库事务的代码,将是一件十分不优雅的事情。

Spring提供了哪些方式支持数据库事务

在本文中,我们暂时不考虑分布式事务。分布式事务相关问题,我们会在后续的文章中加以叙述。

对于非分布式事务,Spring中最为常用的事务管理器为org.springframework.jdbc.datasource.DataSourceTransactionManager。Spring基于DataSourceTransactionManager提供了两种方式来支持数据库事务处理,编程式和声明式。

编程式即为,在业务代码中,通过编程的方式,使用DataSourceTransactionManager的setDataSource(设置数据源,关系型数据库实例),doBegin(开始事务),doCommit(提交事务),doRollback(回滚事务),完成一次数据库操作。这种方式的缺点是明显的,就是对于通用的数据库操作(操作行为大致相同,只是数据不同,可抽象成为统一的行为),代码侵入太强,不够优雅。

声明式即为,在操作数据库的bean的函数上,使用@Transactional标注,声明该方法内部将进行数据库事务处理。借助于AOP,容器在加载该bean的时候,对其进行增强,在标注了@Transactional的方法执行前和执行后动态织入数据库事务处理的相关代码,也就是借助Spring容器将编程式事务处理的代码动态织入进去,无需在业务代码中体现,对于不同的配置项(比如回滚条件等)配置在@Transactional上。

在日常实践中,我们通常使用声明式事务处理的方式,因为它将业务代码与通用数据库处理代码分离开来,够优雅,够灵活。

Spring声明式事务处理的原理

在介绍Spring声明式事务处理的原理之前,我们先来看一下对事务处理器,如何进行配置。

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="xxxDataSource">
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
           

结合配置文件以及Spring框架浅析 -- IoC容器与Bean的生命周期中关于获取BeanFactory、自定义命名空间解析以及Bean的生命周期的相关介绍,可以梳理出Spring声明式事务处理的执行过程,从而了解到如何对使用了@Transactional注解的方法进行增强,动态织入事务处理相关的逻辑。

步骤一

Spring容器启动的时候,按照配置文件中的配置,向BeanFactory中注册事务处理器DataSourceTransactionManager,将数据源xxxDataSource设置到其dataSource属性。

步骤二

当解析到<tx:annotation-driven transaction-manager="transactionManager"/>时,由于这是一个自定义namespace,所以使用tx命名空间的namespaceHandler-TxNamespaceHandler进行解析(namespace到namespaceHandler的映射关系配置在spring-tx jar包下的META-INF目录下)。

步骤三

在TxNamespaceHanlder中,根据xml配置文件中配置的transaction-manager属性,获取到其对应的BeanDefinitionParser-AnnotationDrivenBeanDefinitionParser,然后调用其parse方法,在其调用链中,向容器中以"org.springframework.aop.config.internalAutoProxyCreator"为beanName注册InfrastructureAdvisorAutoProxyCreator,这是一个实现了InstantiationAwareBeanPostProcessor接口的BeanPostProcessor,所以其会在Bean的实例化、初始化过程中对Bean进行加工修饰。此外,还注册了AnnotationTransactionAttributeSource、TransactionInterceptor、BeanFactoryTransactionAttributeSourceAdvisor,这三个组件也会在下边的步骤中起作用。

步骤四

在Bean初始化之后,获取注册的BeanPostProcessor集合(这里边包含了InfrastructureAdvisorAutoProxyCreator),调用其postProcessAfterInitialization方法,获取Bean的proxy,在代理中,针对标注了@Transactional的方法,进行增强,将事务处理相关逻辑动态织入。

我们一步一步地分析一下这个调用链。

InfrastructureAdvisorAutoProxyCreator的postProcessAfterInitialization方法,实质上调用的是其父类AbstractAutoProxyCreator。

@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (!this.earlyProxyReferences.contains(cacheKey)) {
                                // 主入口在这里
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}
           

从主入口进入

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		if (beanName != null && this.targetSourcedBeans.contains(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.
        // 获取Interceptors
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		if (specificInterceptors != DO_NOT_PROXY) {
			this.advisedBeans.put(cacheKey, Boolean.TRUE);
			// 创建proxy
			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;
	}
           

从以上代码看出,主要步骤有两个,一个是获取Interceptors,一个是基于Interceptors对当前处理的bean生成proxy。

说到proxy,其实了解一下代理模式,会对为什么要生成proxy更加明晰。代理模式相关介绍参见:https://zh.wikipedia.org/wiki/%E4%BB%A3%E7%90%86%E6%A8%A1%E5%BC%8F

简单说就是,为被代理对象创建代理,代理对外部屏蔽了被代理对象的内部逻辑。当外部按照协议试图调用被代理对象的方法时,实际上是落在了代理上,由代理加入增强逻辑,然后再调用被代理对象,将结果封装,最后返回给外部调用方。由于调用过程中经过了代理,所以代理当然可以加入相应的增强逻辑,比如计数、记录调用参数和返回结果、事务处理。

上边wikipedia中的附图能够清晰地说明这些点。

我们依然回到代码中,获取Interceptors的过程如下:

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
		// 获取候选Advisors,由于上文提到的BeanFactoryTransactionAttributeSourceAdvisor就是Advisor,所以会被加入
		List<Advisor> candidateAdvisors = findCandidateAdvisors();
		// 获取符合条件的Advisors
		List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
		extendAdvisors(eligibleAdvisors);
		if (!eligibleAdvisors.isEmpty()) {
			eligibleAdvisors = sortAdvisors(eligibleAdvisors);
		}
		return eligibleAdvisors;
	}
           

我们先进入findCandidateAdvisors方法,最为重要的代码如下,本质上是获取到类型为Advisor的类。我们回到步骤三,可以看到BeanFactoryTransactionAttributeSourceAdvisor其实就是一个Advisor,因此也会被加入到Advisor候选集中。

advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Advisor.class, true, false);
           

再回到findAdvisorsThatCanApply方法

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;
			}
			// 对于BeanFactoryTransactionAttributeSourceAdvisor,它是PointcutAdvisor,所以走这个分支
			if (canApply(candidate, clazz, hasIntroductions)) {
				eligibleAdvisors.add(candidate);
			}
		}
		return eligibleAdvisors;
	}
           

再进入到canApply

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

注意其中的pca.getPointcut,这个返回值如下所示:

private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
		@Override
		protected TransactionAttributeSource getTransactionAttributeSource() {
			return transactionAttributeSource;
		}
	};
           

接下来看canApply方法

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

		// 调用StaticMethodMatcherPointcut(TransactionAttributeSourcePointcut的父类)的方法,返回值为this
		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();
			// 遍历bean中的方法,关注if判断的后一个判断表达式
			for (Method method : methods) {
				if ((introductionAwareMethodMatcher != null &&
						introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
						methodMatcher.matches(method, targetClass)) {
					return true;
				}
			}
		}

		return false;
	}
           

关注其中的methodMatcher.matches方法

public boolean matches(Method method, Class<?> targetClass) {
		if (TransactionalProxy.class.isAssignableFrom(targetClass)) {
			return false;
		}
		TransactionAttributeSource tas = getTransactionAttributeSource();
		return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
	}
           

tas实际上就是步骤三中设置的AnnotationTransactionAttributeSource,调用其getTransactionAttribute方法其实是调用其父类的同名方法,其中核心在于

protected 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.
		// 类中的方法标注了@Transactional为第一优先级
		TransactionAttribute txAtt = findTransactionAttribute(specificMethod);
		if (txAtt != null) {
			return txAtt;
		}

		// 类上标注了@Transactional为第二优先级
		// 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;
	}
           

我们仅以方法上标注@Transactional为例,分析一下是如何对标注进行解析的

protected TransactionAttribute determineTransactionAttribute(AnnotatedElement ae) {
		if (ae.getAnnotations().length > 0) {
			for (TransactionAnnotationParser annotationParser : this.annotationParsers) {
				// 实现TransactionAnnotationParser接口的共有三个
				// 1. Ejb3TransactionAnnotationParser
				// 2. JtaTransactionAnnotationParser
				// 3. SpringTransactionAnnotationParser
				// 我们以3为例进行分析
				TransactionAttribute attr = annotationParser.parseTransactionAnnotation(ae);
				if (attr != null) {
					return attr;
				}
			}
		}
		return null;
	}
           

以SpringTransactionAnnotationParser为例,终于看到了我们熟悉的标注@Transactional

public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
		AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ae, Transactional.class);
		if (attributes != null) {
			return parseTransactionAnnotation(attributes);
		}
		else {
			return null;
		}
	}
           

SpringTransactionAnnotationParser识别@Transactional标注,并解析其中的属性,属性列表详见:https://www.ibm.com/developerworks/cn/java/j-master-spring-transactional-use/index.html

至此,我们找到了@Transactional标注的方法,且识别了标注中的属性,下一步就是创建proxy,注入事务处理逻辑了

步骤五

上一步中获取到interceptor之后,这一步借助于AbstractAutoProxyCreator的createProxy方法开始创建代理。

我们注意到createProxy中的最后一句:

return proxyFactory.getProxy(getProxyClassLoader());

再进一步:

public Object getProxy(ClassLoader classLoader) {
		return createAopProxy().getProxy(classLoader);
	}
           

先看createAopProxy方法

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
			Class<?> targetClass = config.getTargetClass();
			if (targetClass == null) {
				throw new AopConfigException("TargetSource cannot determine target class: " +
						"Either an interface or a target is required for proxy creation.");
			}
			// 目标类如果是接口实现类,使用JDK创建动态代理
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
				return new JdkDynamicAopProxy(config);
			}
			// 否则,使用cglib创建代理
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			return new JdkDynamicAopProxy(config);
		}
	}
           

JDK动态代理和CgLib代理的区别与联系详见:http://www.cnblogs.com/binyue/p/4519652.html

我们以JDK动态代理为例,JDK动态代理的原理可参考这篇文章:http://www.importnew.com/23168.html。

简答说,proxy由JDK在运行时动态生成,然后装载到JVM中,就像手写的class一样对外提供服务,只不过其每一个代理方法,都需要调用invocationHander.invoke,在该方法就可以加入需要增强的逻辑。

看一下JdkDynamicAopProxy的getProxy方法

public Object getProxy(ClassLoader classLoader) {
		if (logger.isDebugEnabled()) {
			logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
		}
		Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
		findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
		return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
	}
           

可以看到最后一句,直接使用JDK中提供的方式,创建代理,并返回,至此完成了代理的创建。

不难看出,JdkDynamicAopProxy实现了重要的InvocationHandler方法,其invoke方法中比较重要的部分如下

// Check whether we have any advice. If we don't, we can fallback on direct
			// reflective invocation of the target, and avoid creating a MethodInvocation.
			if (chain.isEmpty()) {
				// We can skip creating a MethodInvocation: just invoke the target directly
				// Note that the final invoker must be an InvokerInterceptor so we know it does
				// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
				Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
			}
			else {
				// We need to create a method invocation...
				invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
				// Proceed to the joinpoint through the interceptor chain.
				retVal = invocation.proceed();
			}
           

简答说就是同一个类的方法可能被多个interceptor按链式进行代理,假设interceptor链为空,那么就调用其方法;

否则,就依次调用其interceptor链的代理方法。

在这里,我们仅关注事务处理相关的interceptor,即TransactionInterceptor(步骤三中注入的),其invoke方法内部调用了invokeWithinTransaction。在该方法内部,就可以看到其基于TransactionManager进行事务开启、遇异常回滚、提交等操作。

这样,当外部访问该bean时,实际上访问的是其已注入Spring事务处理功能的proxy,自然会得到事务处理的支持,如开启事务,提交事务,事务回滚等。

声明式事务处理使用须知

关于这部分,其实网上有着大量的介绍,如https://www.ibm.com/developerworks/cn/java/j-master-spring-transactional-use/index.html。但其实你只需要关注一点即可:

声明式事务处理是基于动态proxy实现的。

所以,如果你标注了@Transactional的方法,无法体现在proxy中(无论是自调用还是非public方法),都不会体现在其proxy中,自然也不会被事务所支持。