注:本文参考
http://www.oschina.net/code/snippet_188964_26555
http://my.oschina.net/jally/blog/180366
实现进行改进。
想在service层开事务,想到的是代理service的方法,在代理中开启事务,然后执行被代理方法,最后提交事务。
cglib的MethodInterceptor可以代理对象的所有方法,使用非常方便,所以这里使用cglib来代理service对象。
为了表明那个方法需要开启事务,这里新建一个注解MethodTx,用来表示要开启事务。
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface MethodTx {
public abstract String config() default (String) "main";
}
注解中的config用来指定使用的数据源,默认使用主数据源,在jfinal中主数据源默认命名为main。
下面是实现代理类:
public class TxProxy implements MethodInterceptor {
private static final Logger log = LoggerFactory.getLogger(TxProxy.class);
/**
* 事务包装
*
* @author shizc
*
*/
private class TxInvoke implements IAtom {
private Object target = null;
private MethodProxy proxy = null;
private Object[] args = null;
private Object result = null;
private String dsConfig = null;
@SuppressWarnings("unused")
private TxInvoke() {
}
public TxInvoke(Object target, MethodProxy proxy, Object[] args) {
super();
this.target = target;
this.proxy = proxy;
this.args = args;
}
public boolean run() throws SQLException {
boolean flag = false;
try {
result = proxy.invokeSuper(target, args);
flag = true;
} catch (Throwable e) {
log.error("调用事务失败", e);
}
return flag;
}
public Object getResult() {
return result;
}
}
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
Object result = null;
if (method.isAnnotationPresent(MethodTx.class)) {
MethodTx methodTx = method.getAnnotation(MethodTx.class);
String conf = methodTx.config();
// 包装成事务
TxInvoke invoke = new TxInvoke(obj, proxy, args);
log.debug("开始事务--------->{}", conf);
if (StringUtils.isNotBlank(conf) && !"main".equalsIgnoreCase(conf)) {
Db.use(conf).tx(invoke);
} else {
Db.tx(invoke);
}
log.debug("结束事务--------->{}", conf);
result = invoke.getResult();
} else {
// 没有事务,直接执行
result = proxy.invokeSuper(obj, args);
}
return result;
}
}
核心思路就是在实现intercept方法中,判断被代理的方法是否有MethodTx注解。如果有MethodTx注解,则选择对应的数据源开启事务执行。如果没有则执行原始方法。
最后,为了得到被代理对象,需要一个工厂类来生成被代理的代理对象:
/**
* service代理工厂
* @author shizc
*
*/
public class TxProxyFactory {
private static final Logger log = LoggerFactory.getLogger(TxProxyFactory.class);
/**
* 获取要代理的对象
*
* @param targetClass
* 被代理的对象
* @return
*/
@SuppressWarnings("unchecked")
public static <T> T newProxy(Class<T> targetClass) {
if (targetClass == null) {
return null;
}
Object proxy = null;
Enhancer en = new Enhancer();
en.setSuperclass(targetClass);
// 代理回调
en.setCallback(new TxProxy());
proxy = en.create();
log.debug("创建代理类:{}", targetClass.getName());
return (T) proxy;
}
}
使用方法:
public class TestService {
public static final TestService me = TxProxyFactory.newProxy(TestService.class);
}
关键是cglib的使用结合Db.Tx来生成代理类。其中切换数据源使用的也是Db.use方法。jfinal使用起来确实比较简单方便。最后感谢 @JFinal 开源这么优秀的框架 感谢@泡泡队长 @hyanqing 提供cglib实现事务代理类的思路
本人能力有限,有失误的地方请指正。
cglib实现jfinal service上添加事务 多数据源切换改进
原文:http://my.oschina.net/purely/blog/414483