概述
在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
对于J2EE领域的开发,通常都需要引入关系型数据库用来存储关系型数据。作为框架,Spring当然要提供通用的数据库操作、数据库事务处理的功能,从而减少业务方重复造轮子,重复封装这些功能,向上屏蔽底层操作不同数据库、管理数据库事务的细节。试想,如果Spring不提供这些功能,每次开发新业务,都需要引入一大堆重复的获取数据库连接,开启数据库事务,关闭数据库事务,回滚数据库事务的代码,将是一件十分不优雅的事情。
在本文中,我们暂时不考虑分布式事务。分布式事务相关问题,我们会在后续的文章中加以叙述。
对于非分布式事务,Spring中最为常用的事务管理器为org.springframework.jdbc.datasource.DataSourceTransactionManager。Spring基于DataSourceTransactionManager提供了两种方式来支持数据库事务处理,编程式和声明式。
编程式即为,在业务代码中,通过编程的方式,使用DataSourceTransactionManager的setDataSource(设置数据源,关系型数据库实例),doBegin(开始事务),doCommit(提交事务),doRollback(回滚事务),完成一次数据库操作。这种方式的缺点是明显的,就是对于通用的数据库操作(操作行为大致相同,只是数据不同,可抽象成为统一的行为),代码侵入太强,不够优雅。
声明式即为,在操作数据库的bean的函数上,使用@Transactional标注,声明该方法内部将进行数据库事务处理。借助于AOP,容器在加载该bean的时候,对其进行增强,在标注了@Transactional的方法执行前和执行后动态织入数据库事务处理的相关代码,也就是借助Spring容器将编程式事务处理的代码动态织入进去,无需在业务代码中体现,对于不同的配置项(比如回滚条件等)配置在@Transactional上。
在日常实践中,我们通常使用声明式事务处理的方式,因为它将业务代码与通用数据库处理代码分离开来,够优雅,够灵活。
在介绍Spring声明式事务处理的原理之前,我们先来看一下对事务处理器,如何进行配置。
结合配置文件以及Spring框架浅析 -- IoC容器与Bean的生命周期中关于获取BeanFactory、自定义命名空间解析以及Bean的生命周期的相关介绍,可以梳理出Spring声明式事务处理的执行过程,从而了解到如何对使用了@Transactional注解的方法进行增强,动态织入事务处理相关的逻辑。
步骤一
Spring容器启动的时候,按照配置文件中的配置,向BeanFactory中注册事务处理器DataSourceTransactionManager,将数据源xxxDataSource设置到其dataSource属性。
步骤二
当解析到
步骤三
在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 findEligibleAdvisors(Class> beanClass, String beanName) {
// 获取候选Advisors,由于上文提到的BeanFactoryTransactionAttributeSourceAdvisor就是Advisor,所以会被加入
List candidateAdvisors = findCandidateAdvisors();
// 获取符合条件的Advisors
List 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 findAdvisorsThatCanApply(List candidateAdvisors, Class> clazz) {
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
List eligibleAdvisors = new LinkedList();
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> classes = new LinkedHashSet>(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中,自然也不会被事务所支持。