事务处理配置的读入
上次说到AOP配置已经完成,下面我们来讲述一下关于具体的事务属性配置是如何读入的。书上所述为在TransactionProxyFactoryBean中,以TransactionAttributeSourceAdvisor的实现为入口,了解具体的事务属性配置是如何被读入的。
这个是我们已经见过的拦截器
private TransactionInterceptor transactionInterceptor;
这个是关于一个切面的配置
private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
@Override
protected TransactionAttributeSource getTransactionAttributeSource() {
return (transactionInterceptor != null ? transactionInterceptor.getTransactionAttributeSource() : null);
}
};
在声明式事务处理中,是通过对目标对象的方法调用进行拦截实现的。这和我们的AOP实现机制是非常相似的,通过对代理对象的方法执行,实现了对目标对象的方法增强。对于拦截的启动,首先要对方法调用是否需要拦截进行判断,判断的依据就是在TransactionProxyFactoryBean中为目标对象设置的事务属性。那么这个需要进行事务拦截的方法就由我们TransactionAttributeSourcePointcut完成并持有。
public boolean matches(Method method, Class<?> targetClass) {
if (targetClass != null && TransactionalProxy.class.isAssignableFrom(targetClass)) {
return false;
}
TransactionAttributeSource tas = getTransactionAttributeSource();
return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
}
同样在这个类中我们可以看到matches方法,具体的实现原理和之前的AOP中Pointcut是类似的。它首先会把事务方法的属性配置读取到TransactionAttributeSource中,有了这些事务的配置。我们在执行方法的时候就可以判断它是否需要加强和进行怎样的加强。这个方法里面需要调用TransactionAttributeSource 对象,这个对象用的是NameMatchTransactionAttributeSource对象,
通过这个方法我们可以看出,这个方法是在我们的TransactionProxyFactoryBean中配置TransactionInterceptor对象时调用的
public void setTransactionAttributes(Properties transactionAttributes) {
NameMatchTransactionAttributeSource tas = new NameMatchTransactionAttributeSource();
tas.setProperties(transactionAttributes);
this.transactionAttributeSource = tas;
}
那么就是在这个NameMatchTransactionAttributeSource中实现的事务处理属性的读入和匹配的。首先是对于事务方法名和配置属性的设置
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);
}
可以看到这里是通过一个map来持有相应的方法名和对应的事务属性。之前我们的matches方法中调用了getTransactionAttribute(Method, Class<?>)方法,那么我们来看一下这个方法是如何将相应的事务属性进行返回的。
首先是根据方法名从之前我们配置的map中进行查找,如果查找不到相应的name。那么就根据命名模式进行匹配,因为有的方法描述带有通配符*,所以需要用匹配通配符的方法来匹配。
String methodName = method.getName();
TransactionAttribute attr = this.nameMap.get(methodName);
if (attr == null) {
// Look for most specific name match.
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;
}
}
}
事务处理拦截器的实现
我们知道,我们的目标方法的增强是通过我们生成的代理对象的invoke方法实现的,在我们的TransactionInterceptor的Invoke方法中,首先需要得到事务处理配置,会取得配置的PlatformTransactionManager事务处理器。这个类我想大家应该比较熟悉,在我们学习Spring 事务的时候,就用到了这个类。
public Object invoke(final MethodInvocation invocation) throws Throwable {
// Work out the target class: may be {@code null}.
// The TransactionAttributeSource should be passed the target class
// as well as the method, which may be from an interface.
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// Adapt to TransactionAspectSupport's invokeWithinTransaction...
return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
@Override
public Object proceedWithInvocation() throws Throwable {
return invocation.proceed();
}
});
}
进入其中的invokeWithinTransaction方法
首先需要读取事务配置属性
final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
之后我们需要取得具体的事务处理器
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
创建事务,将事务的状态信息交由TransactionInfo对象来保存。
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
之后就需要沿着我们之前声明好的拦截器链进行,最终调用我们的方法。如果出现异常要对异常进行处理,最后把与线程绑定的
TransactionInfo 设置为oldTransactionInfo。最后通过事务处理器来对事务进行提交。这个方法的实现将会是我下一次博客的主要内容。
commitTransactionAfterReturning(txInfo);
书上所说还有一种事务的使用方式是通过回调来实现的。这里我就不再提及,因为它和我们接下来要说的方法关系不大。可以在原书中进行查询。