JDK动态代理与开源CGlib实现动态代理

大家都清楚Spring的AOP方面工作是很优秀,但是其内在的基础的东西,还是有一大部分不太了解的,其AOP大量用了ThreadLocal,这一个在前面已做了介绍了,还有一个比较重要的怎样用动态代理组装成AOP.

说到动态代理,有两种情况,第一种是有接口的情况下,你可以选择为jdk自带的动态代理的方式来编写程序,但你想要为一个实在的类编写动态代理的方式的话,这时候就必须选择一些开源的lib包啦.spring和hibernate选择了同样的CGlib包,具体表现在:Hibernate主要是利用cglib生成pojo的子类并override get方法来实现lazy loading机制,Spring则是利用cglib来实现动态代理。

接下来我们就来看看动态代理这两个情况是怎样实现的吧.其实通过demo是比较容易理解一样东西的.所以打算写一个简单的例子来表达我的意思,大家都知道JavaEye社区可以发新帖子,可以修改自己的帖子,所以我们定义下面的一个接口.
Java代码
package lighter.<SPAN class=hilite1>java</SPAN>eye.com;

public interface <SPAN class=hilite1>Java</SPAN>EyeForum {
void postTopic(int topicId);

void editTopic(int topicId);
}

package lighter.iteye.com;

public interface JavaEyeForum {
void postTopic(int topicId);

void editTopic(int topicId);
}

当然,有接口啦,我们自然而然的为它写一个实现的类,作为演示并没有实质性的代码的:
Java代码
package lighter.<SPAN class=hilite1>java</SPAN>eye.com;

public class <SPAN class=hilite1>Java</SPAN>EyeForumImpl implements <SPAN class=hilite1>Java</SPAN>EyeForum {
public void postTopic(int topicId) {
System.out.println("发布帖子,帖子的ID号为:"+topicId);
}
public void editTopic(int topicId) {
System.out.println("编辑帖子,帖子的ID号为:"+topicId);
}
}

package lighter.iteye.com;

public class JavaEyeForumImpl implements JavaEyeForum {
public void postTopic(int topicId) {
System.out.println("发布帖子,帖子的ID号为:"+topicId);
}
public void editTopic(int topicId) {
System.out.println("编辑帖子,帖子的ID号为:"+topicId);
}
}
因为一般情况下,你发布帖子和编辑要处在事务范围之内(假设的),所以我们新写下面的一个功能类TransactionManager,想让在postTopic和editTopic方法前后分别调用下面的beginTransaction和endTransaction方法.
Java代码
package lighter.<SPAN class=hilite1>java</SPAN>eye.com;

public class TransactionManager {
public static void beginTransaction(String methodName){
System.out.println(methodName + "开始事务管理!");
}
public static void endTransaction(String methodName){
System.out.println(methodName + "事务管理结束!\n");
}
}

package lighter.iteye.com;

public class TransactionManager {
public static void beginTransaction(String methodName){
System.out.println(methodName + "开始事务管理!");
}
public static void endTransaction(String methodName){
System.out.println(methodName + "事务管理结束!\n");
}
}

剩下的问题就是,我们用方式把TransactionManager里面的两个方法织入到JavaEyeForumImpl类里面方法的合适的位置,很简单地,我们只需要写一个处理的Handler类,如下:
Java代码
package lighter.<SPAN class=hilite1>java</SPAN>eye.com;

import <SPAN class=hilite1>java</SPAN>.lang.reflect.InvocationHandler;
import <SPAN class=hilite1>java</SPAN>.lang.reflect.Method;

public class TransactionHandler implements InvocationHandler {
private Object target;
public TransactionHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
TransactionManager.beginTransaction(method.getName());
Object obj = method.invoke(target, args);
TransactionManager.endTransaction(method.getName());
return obj;
}
}

package lighter.iteye.com;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class TransactionHandler implements InvocationHandler {
private Object target;
public TransactionHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
TransactionManager.beginTransaction(method.getName());
Object obj = method.invoke(target, args);
TransactionManager.endTransaction(method.getName());
return obj;
}
}
在上面的类中的invoke方法中,"Object obj = method.invoke(target, args);"前后的语句指定调用前该前做,调用后该做什么.
接下来,就是写一个测试类啦
Java代码
package lighter.<SPAN class=hilite1>java</SPAN>eye.com;

import <SPAN class=hilite1>java</SPAN>.lang.reflect.Proxy;
public class TestDynamicProxy {
public static void main(String[] args) {
<SPAN class=hilite1>Java</SPAN>EyeForum target = new <SPAN class=hilite1>Java</SPAN>EyeForumImpl();
TransactionHandler handler = new TransactionHandler(target);
<SPAN class=hilite1>Java</SPAN>EyeForum proxy = (<SPAN class=hilite1>Java</SPAN>EyeForum) Proxy.newProxyInstance(target
.getClass().getClassLoader(),target.getClass().getInterfaces(), handler);
proxy.postTopic(100);
proxy.editTopic(999);
}
}

package lighter.iteye.com;

import java.lang.reflect.Proxy;
public class TestDynamicProxy {
public static void main(String[] args) {
JavaEyeForum target = new JavaEyeForumImpl();
TransactionHandler handler = new TransactionHandler(target);
JavaEyeForum proxy = (JavaEyeForum) Proxy.newProxyInstance(target
.getClass().getClassLoader(),target.getClass().getInterfaces(), handler);
proxy.postTopic(100);
proxy.editTopic(999);
}
}
测试类,请仔细看 Proxy.newProxyInstance这一个方法的第二个参数必须指定target.getClass().getInterfaces()这一个接口后,动态代理才能起效. 这是为什么说平时我们说jdk 中的动态代理有时候比较麻烦,那是还要指定特定的接口的原因.
测试代码运行结果如下:

引用
postTopic 开始事务管理!
发布帖子,帖子的ID号为:100
postTopic事务管理结束!

editTopic 开始事务管理!
编辑帖子,帖子的ID号为:999
editTopic事务管理结束!


接下来我们来看看怎样用CGLib来生成动态代理,首先把TestDynamicProxy.java和TransactionHandler.java两个类删除掉,免得影响视线嘛,呵呵;然后再新建一个CglibProxy代理类.
Java代码
package lighter.<SPAN class=hilite1>java</SPAN>eye.com;

import <SPAN class=hilite1>java</SPAN>.lang.reflect.Method;
import net.sf.<SPAN class=hilite4>cglib</SPAN>.proxy.Enhancer;
import net.sf.<SPAN class=hilite4>cglib</SPAN>.proxy.MethodInterceptor;
import net.sf.<SPAN class=hilite4>cglib</SPAN>.proxy.MethodProxy;
/**
* net.sf.<SPAN class=hilite4>cglib</SPAN>.proxy.Enhancer和MethodInterceptor在<SPAN class=hilite4>CGLib</SPAN>中负责完成代理对象创建和方法截获处理,
* 产生的是目标类的子类而不是通过接口来实现方法拦截的,Enhancer主要是用于构造<SPAN class=hilite2>动态代理</SPAN>子类来实现拦截,MethodInterceptor(扩展了
* Callback接口)主要用于实现around advice(AOP中的概念)
*/
public class <SPAN class=hilite4>Cglib</SPAN>Proxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz) {
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercept(Object obj, Method method, Object[] args,MethodProxy proxy) throws Throwable {
String methodName = obj.getClass().getName()+"."+method.getName();
TransactionManager.beginTransaction(methodName);
Object result = proxy.invokeSuper(obj, args);
TransactionManager.endTransaction(methodName);
return result;
}
}

package lighter.iteye.com;

import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* net.sf.cglib.proxy.Enhancer和MethodInterceptor在CGLib中负责完成代理对象创建和方法截获处理,
* 产生的是目标类的子类而不是通过接口来实现方法拦截的,Enhancer主要是用于构造动态代理子类来实现拦截,MethodInterceptor(扩展了
* Callback接口)主要用于实现around advice(AOP中的概念)
*/
public class CglibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz) {
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercept(Object obj, Method method, Object[] args,MethodProxy proxy) throws Throwable {
String methodName = obj.getClass().getName()+"."+method.getName();
TransactionManager.beginTransaction(methodName);
Object result = proxy.invokeSuper(obj, args);
TransactionManager.endTransaction(methodName);
return result;
}
}

然后,我们再写一个测试类如下:
Java代码
package lighter.<SPAN class=hilite1>java</SPAN>eye.com;

public class Test<SPAN class=hilite4>CGLib</SPAN>Proxy {
public static void main(String[] args) {
<SPAN class=hilite4>Cglib</SPAN>Proxy proxy = new <SPAN class=hilite4>Cglib</SPAN>Proxy();
<SPAN class=hilite1>Java</SPAN>EyeForum forum = (<SPAN class=hilite1>Java</SPAN>EyeForum)proxy.getProxy(<SPAN class=hilite1>Java</SPAN>EyeForumImpl.class);
forum.postTopic(999);
forum.editTopic(999);
}
}

package lighter.iteye.com;

public class TestCGLibProxy {
public static void main(String[] args) {
CglibProxy proxy = new CglibProxy();
JavaEyeForum forum = (JavaEyeForum)proxy.getProxy(JavaEyeForumImpl.class);
forum.postTopic(999);
forum.editTopic(999);
}
}
测试的结果如下:

引用
lighter.iteye.com.JavaEyeForumImpl$$EnhancerByCGLIB$$155ad1e9.postTopic 开始事务管理!
发布帖子,帖子的ID号为:999
lighter.iteye.com.JavaEyeForumImpl$$EnhancerByCGLIB$$155ad1e9.postTopic事务管理结束!

lighter.iteye.com.JavaEyeForumImpl$$EnhancerByCGLIB$$155ad1e9.editTopic 开始事务管理!
编辑帖子,帖子的ID号为:999
lighter.iteye.com.JavaEyeForumImpl$$EnhancerByCGLIB$$155ad1e9.editTopic事务管理结束!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值