使用注解,代理完成事务控制

文章来源:点击前往链接地址 这是我的培训老师的文章分享,挺不错。自己想写一套框架,可以用用。

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

 

 

      数据库的事务控制可以从JavaWeb的Filter开始,到Struts2的interceptor,最后到Spring的AOP。按我个人的理解应该是粗粒度、中粒度和细粒度。在没有学习interceptor和AOP之前,我们自己完全可以使用现有技术做一个简单的AOP实现。

 

技术:

声明一个注解,以便于在拦截时判断是否要处理事务。

声明一个代理类。接收被代理对象。

   需要说明的:如果使用JDK的动态代理,则注解必须要作用在接口上。如果使用cglib则可以没有接口类,当前注解也就作用在具体类的方法上了。

 

1、使用JDK的动态代理-需要接口

 

代码清单1-声明事务注解:

package cn.wangjian.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.wangjian.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.wangjian.tx;

public interface IOne {

@Tx

void save();//添加需要处理事务的注解

void del();

}

实现接口类:

package cn.wangjian.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.wangjian.cglib;

import java.lang.reflect.Method;

import cn.wangjian.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;

}

}

 

小结:

   1:声明事务注解,用于在反射中读取它。

   2:使用ThreadLocal管理当前线程的事务。

   3:使用动态代理/cglib的代理实现方法拦截并管理事务。

 

 

 


 

 

 

微官网:http://www.oraclewdp.com

实训咨询:010-52292270  

咨询QQ:2845789564

posted @ 2016-07-06 08:53  小小子~墨  阅读(213)  评论(0编辑  收藏  举报