您的位置:首页 > 编程语言 > Java开发

JDK动态代理VS CgLib

2016-04-21 18:09 966 查看

业务场景

描述:每个旅客乘坐火车时,在上车、下车之前需要检票。站在乘客的角度,这个过程可以看做一件事:检票坐车检票;也可以看做两件事:检票,坐车;站在检票员的角度,那就是一件事,检票。

如果我们用代码实现,可能是这样的过程:

checkTicket();
takingTrain();
checkTicket();

如果有一天,车站变成刷身份证进站了,我们就得回去改代码了... 下面我们将这个过程分开,用代理来解决坐车意外的问题: 业务逻辑:坐火车 额外需要:检票/刷卡

具体实现

  • JDK动态代理,主要类、接口:Proxy, InvocationHandle,JDK动态代理特点只能对接口进行代理

所以需要一个业务逻辑接口:TakingTrain;

package com.spring.mybean;
public interface TakingTrain {
public void takeTrain(String name);
}

实现类:TakingTrainImpl------>要被代理的目标类,实现TakingTrain接口

package com.spring.mybean.imp;
import com.spring.mybean.TakingTrain;
public class TakingTrainImpl implements TakingTrain {
public void takeTrain(String name) {
System.out.println("Hi "+name+ "Welcome to take the train");
}
}

增强的横切逻辑:CheckTicket------>增强类,实现InvocationHandle接口

package com.spring.myadvice;

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

public class CheckTicket implements InvocationHandler {
private Object target;
public CheckTicket(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("please show your tickes");
Object obj=method.invoke(target, args);
return obj;
}
}

调用:

public class TakingTainingTest {

@Test
public void takingTrain(){
TakingTrain target=new TakingTrainImpl();
CheckTicket ct=new CheckTicket(target);
TakingTrain proxy=(TakingTrain) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), ct);
proxy.takeTrain("zhangsan");
}

}

打印结果

please show your tickes
Hi zhangsan Welcome to take the train

JDK中具体的动态代理类是怎么产生

  1. 产生代理类$Proxy0类 执行了Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 将产生$Proxy0类,它继承Proxy对象,并根据第二个参数,实现了被代理类的所有接口,自然就可以生成接口要实现的所有方法了(这时候会重写hashcode,toString和equals三个方法),但是还没有具体的实现体;
  2. 将代理类$Proxy0类加载到JVM中 这时候是根据Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)它的第一个参数----就是被代理类的类加载器,把当前的代理类加载到JVM中
  3. 创建代理类$Proxy0类的对象 调用的$Proxy0类的$Proxy0(InvocationHandler)构造函数,生成$Proxy0类的对象 参数就是Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)它的第三个参数 这个参数就是我们自己实现的InvocationHandler对象,我们知道InvocationHandler对象中组合加入了代理类代理的接口类的实现类;所以,$Proxy0对象调用所有要实现的接口的方法,都会调用InvocationHandler对象的invoke()方法实现;
  4. 生成代理类的class byte动态代理生成的都是二进制class字节

CgLib实现

好了,我们知道JDK的动态代理只能对接口进行代理,如果没有接口怎么办呢? CgLib可以为一个类创建一个子类,在子类中采用方法拦截的技术拦截父类所有方法的调用,你可以在这里加入你的横切逻辑。 还是上面的例子,下面我们用Cglib来实现:

public class CglibProxy implements MethodInterceptor{
private Enhancer enhancer = new Enhancer();

public Object getProxy(Class clazz){
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}

@Override
public Object intercept(Object arg0, Method arg1, Object[] arg2,
MethodProxy proxy) throws Throwable {
System.out.println("please show your tickes");
return proxy.invokeSuper(arg0, arg2);
}

}

然后我们用这个CglibProxy代理类来获取TakingTrainImpl这个业务的实现类:

CglibProxy cglibProxy = new CglibProxy();
TakingTrainImpl takingTrain = (TakingTrainImpl) cglibProxy.getProxy(TakingTrainImpl.class);
takingTrain.takeTrain("zhangsan");

上面的方法,我们直接使用了TakingTrainImpl,没有在使用它的接口,这就是JDK动态代理和CgLib的区别。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  JDK 动态代理 CgLib