JDK动态代理VS CgLib

試著忘記壹切 提交于 2020-05-07 20:24:32

业务场景

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

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

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的区别。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!