使用注解和动态代理在Service层控制事务
使用注解和动态代理在Service层控制事务
数据库的事务控制可以从JavaWeb的Filter开始,到Struts2的interceptor,最后到Spring的AOP。按我个人的理解应该是粗粒度、中粒度和细粒度。在没有学习interceptor和AOP之前,我们自己完全可以使用现有技术做一个简单的AOP实现。
技术:
声明一个注解,以便于在拦截时判断是否要处理事务。
声明一个代理类。接收被代理对象。
需要说明的:如果使用JDK的动态代理,则注解必须要作用在接口上。如果使用cglib则可以没有接口类,当前注解也就作用在具体类的方法上了。
1、使用JDK的动态代理-需要接口
代码清单1-声明事务注解:
package cn.itcast.tx;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 事务注解
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value=ElementType.METHOD)
public @interface Tx {}
代码清单2-声明代理类:
package cn.itcast.tx;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 事务拦截模拟
*/
public class TxProxy implements InvocationHandler{
private Object obj;
private TxProxy(Object obj){
this.obj=obj;
}
//接收一个被代理的对象
public static Object newProxy(Object o){
Object proxy = Proxy.newProxyInstance(TxProxy.class.getClassLoader(),
o.getClass().getInterfaces(),
new TxProxy(o));
return proxy;
}
//使用泛型,接收被代理类的Class对象
public static <T>T newProxy(Class<T> cls){
Object o = null;
try{
o = cls.newInstance();
}catch(Exception e){
throw new RuntimeException(e.getMessage(),e);
}
Object proxy = Proxy.newProxyInstance(TxProxy.class.getClassLoader(),
o.getClass().getInterfaces(),
new TxProxy(o));
return (T)proxy;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object res = null;
if(method.isAnnotationPresent(Tx.class)){//判断是否添加了Tx事务注解
try{
System.err.println("开始拦截..从当前线程局部对象中获取一个连接");
res = method.invoke(this.obj, args);
System.err.println("拦截完成提交");
}catch(Exception e){
System.err.println("回滚");
throw new RuntimeException(e.getMessage(),e);
}finally{
System.err.println("结束。。。放回连接池");
}
}else{
res = method.invoke(this.obj, args);
}
return res;
}
}
测试代码清单1:
必须要先完成一个接口:
package cn.itcast.tx;
public interface IOne {
@Tx
void save();//添加需要处理事务的注解
void
}
实现接口类:
package cn.itcast.tx;
public class One implements IOne {
public void save() {
System.err.println("保存。。。");
}
public void del() {
System.err.println("删除。。。。");
}
}
测试:
@Test
public void t1(){
IOne one = TxProxy.newProxy(One.class);
one.save(); //此方法将会处理事务
one.del();
}
测试结果:可见,对于保存方法成功开始了事务:
开始拦截..从当前线程局部对象中获取一个连接
保存。。。
拦截完成提交
结束。。。放回连接池
删除。。。。
2、使用cglib的动态代理
不需要接口。但需要导入cglib的两个jar文件,分为是:
cglib.jar
asm.jar
使用cglib的动态代理,由于不需要接口,当然也可以有接口。所以此时,你的事务注解就必须要注解到实体类的方法上,而不是接口的方法上。
以下是cglib代理的工具类:
仍然使用上面那个Tx注解,所以Tx注解的代码略,直接上cglib的动态代理类:
package cn.itcast.cglib;
import java.lang.reflect.Method;
import cn.itcast.tx.Tx;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* 使用Cglib的代理
*/
public class ProxyUtils implements MethodInterceptor {
private Object src;
private ProxyUtils(Object src){
this.src=src;
}
public static <T>T newProxy(T t){
Enhancer en = new Enhancer();
en.setSuperclass(t.getClass());
en.setCallback(new ProxyUtils(t));
Object o = en.create();
return (T)o;
}
public Object intercept(Object proxy, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
Object o = null;
if(method.isAnnotationPresent(Tx.class)){
try{
System.err.println("开始一个事务。。。");
o = method.invoke(src, args);
System.err.println("提交一个事务");
}catch(Exception e){
System.err.println("回滚一个事务");
}finally{
System.err.println("放回连接池");
}
}else{
o = method.invoke(src,args);
}
return o;
}
}