锁定老帖子 主题:浅析“代理模式”实现spring事务管理
精华帖 (0) :: 良好帖 (1) :: 新手帖 (0) :: 隐藏帖 (1)
|
|
---|---|
作者 | 正文 |
发表时间:2011-12-21
最后修改:2011-12-21
模拟用户调用: package bean.parterdesign.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; public class Client { /** * @param args */ public static void main(String[] args) { //RealSubject 是接口Subject 的实现类 RealSubject rs = new RealSubject(); // 在这里指定被代理类 InvocationHandler ds = new DynamicSubject(rs); // 初始化代理类 //实现类的相关信息 ClassLoader rsClassLoader = RealSubject.class.getClassLoader(); Class[] rsInterfaces = RealSubject.class.getInterfaces(); //生成RealSubject 的代理类, 并执行 Subject subject = (Subject) Proxy.newProxyInstance( rsClassLoader,rsInterfaces, ds); subject.request(); System.out.println("======动态代理类====" + subject.getClass().getName() ); /*// 以下是分解步骤 * Class c = Proxy.getProxyClass(cls.getClassLoader(),cls.getInterfaces()) ; * Constructor ct=c.getConstructor(new Class[]{InvocationHandler.class}); * Subject subject =(Subject) ct.newInstance(new Object[]{ds}); */ Class c = Proxy.getProxyClass(rsClassLoader,rsInterfaces) ; System.out.println( c.getName() ); System.out.println("===== 动态代理类实现的接口==" + c.getInterfaces()[0].getName() ); } } RealSubject 的代码: package bean.parterdesign.proxy; public class RealSubject implements Subject { public RealSubject() { } //提交数据更新操作 public void request() { System.out.println("UPDATE DATABASE From real subject."); } } DynamicSubject(InvocationHandler接口的实现类) 的代码: Proxy根据InvocationHandler接口的实现类中的内容生成最后供用户调用的Subject接口 //生成RealSubject 的代理类 Subject subject = (Subject) Proxy.newProxyInstance( rsClassLoader,rsInterfaces, ds); package bean.parterdesign.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class DynamicSubject implements InvocationHandler { private Object sub; // sub 必须是一个接口的实现类 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //为啥要设置自动提交为false ?很多数据库驱动是自动提交的 System.out.println("设置 Object sub 中的CONNECTION自动提交为false , connect before calling " + method); try { method.invoke(sub, args); } catch (Exception e) { System.out.println("发生异常时进行数据回滚 , " + method); } System.out.println("没有异常时,可以提交 after calling " + method); return null; } public DynamicSubject() { } public DynamicSubject(Object obj) { sub = obj; } } -------------------------------------------------------------------- DynamicSubject 中我们可以模拟数据库操作;实现事务管理; 初级开发者会犯一个错误:直接从SPRING中配置的DATASOURCE中取得CONNECT数据库连接,这样会导致事务失效; 因为你取得的连接的AUTOCOMMIT 是TRUE ;这样事务就没法交给SPRING进行管理啦。 用代理的作用其实就是控制CONNECT数据库连接,通过代理我们可以控制数据库操作的提交和回滚; 另外早期的SPRING事务管理必须是基于接口的,当现在改变啦!为啥必须基于接口?看看这段代码: Proxy根据InvocationHandler接口的实现类中的内容生成最后供用户调用的Subject接口 , newProxyInstance 中有个参数必须是接口的集合,这个就是原因 //生成RealSubject 的代理类 Subject subject = (Subject) Proxy.newProxyInstance( rsClassLoader,rsInterfaces, ds); DynamicSubject 中的invoke 会为Object proxy(被代理对象)每个方法都加上 System.out.println("设置 Object sub 中的CONNECTION自动提交为false , connect before calling " + method); 和 System.out.println("没有异常时,可以提交 after calling " + method); return null;; 当然你也可以根据method的方法名来进行判断是否需要加其他处理;这就是为啥spring 事务管理可以配置某些方法可以事务管理,也可以让一些方法不进行事务管理;不过现在的配置方法和早期的改变很大,但内部实现应该是一样的; 见代码 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //为啥要设置自动提交为false ?很多数据库驱动是自动提交的 System.out.println("设置 Object sub 中的CONNECTION自动提交为false , connect before calling " + method); try { method.invoke(sub, args); } catch (Exception e) { System.out.println("发生异常时进行数据回滚 , " + method); } System.out.println("没有异常时,可以提交 after calling " + method); return null; } 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2011-12-22
初级开发者会犯一个错误:直接从SPRING中配置的DATASOURCE中取得CONNECT数据库连接,这样会导致事务失效;
因为你取得的连接的AUTOCOMMIT 是TRUE ;这样事务就没法交给SPRING进行管理啦。 我觉得不一定只是这个原因吧 Spring使用TransactionSynchronizationManager使每个session和当前线程绑定起来。 这样当使用commit的时候肯定能找使用的那个session 我看了一下HibernateTransactionManager的commit只针对session做commit 而不是connection |
|
返回顶楼 | |
发表时间:2011-12-22
如果绕开这些框架,就可以看到事务的本质;
如果让你自己实现事务管理,你会怎么做?这个才是我写这个帖子的目的; |
|
返回顶楼 | |
发表时间:2011-12-22
json20080301 写道 如果绕开这些框架,就可以看到事务的本质;
如果让你自己实现事务管理,你会怎么做?这个才是我写这个帖子的目的; 恩 你说的对! |
|
返回顶楼 | |
发表时间:2011-12-22
xiaoZ5919 写道 初级开发者会犯一个错误:直接从SPRING中配置的DATASOURCE中取得CONNECT数据库连接,这样会导致事务失效;
因为你取得的连接的AUTOCOMMIT 是TRUE ;这样事务就没法交给SPRING进行管理啦。 我觉得不一定只是这个原因吧 Spring使用TransactionSynchronizationManager使每个session和当前线程绑定起来。 这样当使用commit的时候肯定能找使用的那个session 我看了一下HibernateTransactionManager的commit只针对session做commit 而不是connection 请忽略上述言论! session底层的commit还是调用JDBCTransction的commit |
|
返回顶楼 | |
发表时间:2011-12-22
呵呵!写这个帖子就是为了抛开框架,看到事物的本质;其实知道原理自己也是可以实现的,
|
|
返回顶楼 | |
发表时间:2011-12-22
是的,其实声明性事务管理大体实现思路应该是这样子,
不过根据楼主的例子,假如RealSubject没有接口呢,就不好用动态代理了,spring只能是用到了字节码增强来实现proxy,对吧 |
|
返回顶楼 | |
发表时间:2011-12-22
最后修改:2011-12-22
kakaluyi 写道 是的,其实声明性事务管理大体实现思路应该是这样子,
不过根据楼主的例子,假如RealSubject没有接口呢,就不好用动态代理了,spring只能是用到了字节码增强来实现proxy,对吧 如你所说,是的。后面SPRING 改进了配置,可以对没有实现接口的类进行事务管理; 相关资料百度一下,都可以找得到 <!-- 加入 aop annotation 注释 --> <aop:aspectj-autoproxy proxy-target-class="true" /> 这种方法很直接很暴力,直接修改你的字节码文件;其实这种修改字节码的方式也可以用于其他用途, 根据字符串动态生成JAVA代码,可以一定程度上减少硬编码,提高代码灵活性; |
|
返回顶楼 | |
发表时间:2011-12-22
kakaluyi 写道 是的,其实声明性事务管理大体实现思路应该是这样子,
不过根据楼主的例子,假如RealSubject没有接口呢,就不好用动态代理了,spring只能是用到了字节码增强来实现proxy,对吧 if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { 如在配置有optimize = ture 或者 proxy-target-class=true 则使用cglib创建代理 否则使用动态代理 |
|
返回顶楼 | |
发表时间:2011-12-22
顶,看源码的同学必须顶啊。
CGLIB很暴力,但是很高效,直接修改字节码 |
|
返回顶楼 | |