使用注解和动态代理在Service层控制事务

wangjianme 12年前

使用注解和动态代理在Service层控制事务

       数据库的事务控制可以从JavaWebFilter开始,到Struts2interceptor,最后到SpringAOP。按我个人的理解应该是粗粒度、中粒度和细粒度。在没有学习interceptorAOP之前,我们自己完全可以使用现有技术做一个简单的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 del();

}

实现接口类:

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;

    }

}