业务场景
描述:每个旅客乘坐火车时,在上车、下车之前需要检票。站在乘客的角度,这个过程可以看做一件事:检票坐车检票;也可以看做两件事:检票,坐车;站在检票员的角度,那就是一件事,检票。
如果我们用代码实现,可能是这样的过程:
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中具体的动态代理类是怎么产生
- 产生代理类$Proxy0类 执行了Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 将产生$Proxy0类,它继承Proxy对象,并根据第二个参数,实现了被代理类的所有接口,自然就可以生成接口要实现的所有方法了(这时候会重写hashcode,toString和equals三个方法),但是还没有具体的实现体;
- 将代理类$Proxy0类加载到JVM中 这时候是根据Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)它的第一个参数----就是被代理类的类加载器,把当前的代理类加载到JVM中
- 创建代理类$Proxy0类的对象 调用的$Proxy0类的$Proxy0(InvocationHandler)构造函数,生成$Proxy0类的对象 参数就是Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)它的第三个参数 这个参数就是我们自己实现的InvocationHandler对象,我们知道InvocationHandler对象中组合加入了代理类代理的接口类的实现类;所以,$Proxy0对象调用所有要实现的接口的方法,都会调用InvocationHandler对象的invoke()方法实现;
- 生成代理类的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的区别。
来源:oschina
链接:https://my.oschina.net/u/917742/blog/663999