论坛首页 Java企业应用论坛

浅析“代理模式”实现spring事务管理

浏览 7579 次
精华帖 (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;
	}






  • 大小: 26.7 KB
   发表时间:2011-12-22  
初级开发者会犯一个错误:直接从SPRING中配置的DATASOURCE中取得CONNECT数据库连接,这样会导致事务失效;
因为你取得的连接的AUTOCOMMIT 是TRUE ;这样事务就没法交给SPRING进行管理啦。

我觉得不一定只是这个原因吧
Spring使用TransactionSynchronizationManager使每个session和当前线程绑定起来。
这样当使用commit的时候肯定能找使用的那个session
我看了一下HibernateTransactionManager的commit只针对session做commit 而不是connection
0 请登录后投票
   发表时间:2011-12-22  
如果绕开这些框架,就可以看到事务的本质;

如果让你自己实现事务管理,你会怎么做?这个才是我写这个帖子的目的;

0 请登录后投票
   发表时间:2011-12-22  
json20080301 写道
如果绕开这些框架,就可以看到事务的本质;

如果让你自己实现事务管理,你会怎么做?这个才是我写这个帖子的目的;



恩  你说的对!
0 请登录后投票
   发表时间:2011-12-22  
xiaoZ5919 写道
初级开发者会犯一个错误:直接从SPRING中配置的DATASOURCE中取得CONNECT数据库连接,这样会导致事务失效;
因为你取得的连接的AUTOCOMMIT 是TRUE ;这样事务就没法交给SPRING进行管理啦。

我觉得不一定只是这个原因吧
Spring使用TransactionSynchronizationManager使每个session和当前线程绑定起来。
这样当使用commit的时候肯定能找使用的那个session
我看了一下HibernateTransactionManager的commit只针对session做commit 而不是connection

请忽略上述言论!
session底层的commit还是调用JDBCTransction的commit
0 请登录后投票
   发表时间:2011-12-22  
呵呵!写这个帖子就是为了抛开框架,看到事物的本质;其实知道原理自己也是可以实现的,
0 请登录后投票
   发表时间:2011-12-22  
是的,其实声明性事务管理大体实现思路应该是这样子,
不过根据楼主的例子,假如RealSubject没有接口呢,就不好用动态代理了,spring只能是用到了字节码增强来实现proxy,对吧
0 请登录后投票
   发表时间:2011-12-22   最后修改:2011-12-22
kakaluyi 写道
是的,其实声明性事务管理大体实现思路应该是这样子,
不过根据楼主的例子,假如RealSubject没有接口呢,就不好用动态代理了,spring只能是用到了字节码增强来实现proxy,对吧



如你所说,是的。后面SPRING 改进了配置,可以对没有实现接口的类进行事务管理;

相关资料百度一下,都可以找得到

<!-- 加入 aop annotation 注释 -->
<aop:aspectj-autoproxy proxy-target-class="true" />

这种方法很直接很暴力,直接修改你的字节码文件;其实这种修改字节码的方式也可以用于其他用途,
根据字符串动态生成JAVA代码,可以一定程度上减少硬编码,提高代码灵活性;

0 请登录后投票
   发表时间:2011-12-22  
kakaluyi 写道
是的,其实声明性事务管理大体实现思路应该是这样子,
不过根据楼主的例子,假如RealSubject没有接口呢,就不好用动态代理了,spring只能是用到了字节码增强来实现proxy,对吧


if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {

如在配置有optimize = ture 或者  proxy-target-class=true 则使用cglib创建代理
否则使用动态代理
0 请登录后投票
   发表时间:2011-12-22  
顶,看源码的同学必须顶啊。

CGLIB很暴力,但是很高效,直接修改字节码
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics