使用注解和動態代理在Service層控制事務

使用注解和動態代理在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;

    }

}

 本文由用戶 wangjianme 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!