动态代理与链式动态代理
链式代理就是将多个代理串链在一起,一个一个的去执行,执行的顺序取决于链上的先后顺序。JDK的动态代理只能代理实现了接口的类实例,而CGLib则可以代理普通的类实例。
执行链式代理的样子大概如下图所示:这很像Spring Aop的前后增强,实际上我也是基于这个想到,如果增强有多个,那么就是链式动态代理的样子了。好像Filter也是这样的,Node.js中如Express的中间件也是这样的。
/*
* a.before()
* b.before()
* 执行被代理的方法
* b.after()
*a.after()
*
*/
类图及测试结果
类图
TestMain是执行类。将被代理class和Proxy的链表list以参数传入到ProxyManager中,生成被代理的实例,执行被代理的实例的属性方法。
测试结果
dynamic.proxy.ProxyImplFirst@25c78000 ImplFirst before
dynamic.proxy.ProxyImplSecond@4ca49360 Impl Second before
TestMain$$EnhancerByCGLIB$$59702c51 ------------Hello--------
dynamic.proxy.ProxyImplSecond@4ca49360 Impl Second after
dynamic.proxy.ProxyImplFirst@25c78000 ImplFirst after
dynamic.proxy.ProxyImplFirst@25c78000 ImplFirst before
dynamic.proxy.ProxyImplSecond@4ca49360 Impl Second before
TestMain ------------Hello--------
dynamic.proxy.ProxyImplSecond@4ca49360 Impl Second after
dynamic.proxy.ProxyImplFirst@25c78000 ImplFirst after
第一个组输出为CGLIB的动态代理结果,第二组输出为JDK的动态代理结果。
主要代码
ProxyManager
CgProxyManager
/**
* 根据cls,和代理链生成Proxy代理对象
* cls不必含有接口,CGLib本身决定的。
*
* @param targetClass
* @param proxyList
* @return
*/
public <T> T createProxy(final Class<?> targetClass, final List<Proxy> proxyList) {
return (T) Enhancer.create(targetClass, new MethodInterceptor() {
@Override
public Object intercept(Object targetObject, Method targetMethod, Object[] methodParams, MethodProxy methodProxy) throws Throwable {
return new CgProxyChain(proxyList, targetClass, targetObject, targetMethod, methodProxy, methodParams).doProxyChain();
}
});
}
JdkProxyManager
/**
* 根据cls,和代理链生成Proxy代理对象
* cls必须含有接口,JDK要求的。
*
* @param cls
* @param proxyList
* @return
*/
@SuppressWarnings("unchecked")
public <T> T createProxy(final Class<?> cls, final List<Proxy> proxyList) {
return (T) java.lang.reflect.Proxy.newProxyInstance(cls.getClassLoader(),
cls.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return new JdkProxyChain(proxyList, cls, cls.newInstance(), method, args).doProxyChain();
}
});
}
可以看出在对被代理方法进行拦截以后都执行的是一个叫doProxyChain()的做链式代理的方法,可以猜想,这个方法肯定是做链式处理的,先处理第一个Proxy然后继续直到没有Proxy就执行被代理方法,然后返回被代理方法的返回值。那么是如何做到依次执行被代理方法的呢。
抽象类ProxyChain
public abstract class ProxyChain {
List<Proxy> proxyList; //代理链
Class<?> targetClass; //被代理类
Object targetObject; //被代理Object
Method targetMethod; //被代理方法
MethodProxy methodProxy; //被代理方法的Proxy,用于CGLib
Object[] methodParams; //方法参数
public int proxyIndex=0; //指向代理链的当前执行位置
/*用于CGLib*/
public ProxyChain(List<Proxy> proxyList, Class<?> targetClass, Object targetObject, Method targetMethod, MethodProxy methodProxy, Object[] methodParams) {
this.proxyList = proxyList;
this.targetClass = targetClass;
this.targetObject = targetObject;
this.targetMethod = targetMethod;
this.methodProxy = methodProxy;
this.methodParams = methodParams;
}
/*用于JDk*/
public ProxyChain(List<Proxy> proxyList, Class<?> targetClass, Object targetObject, Method targetMethod, Object[] methodParams) {
this.proxyList = proxyList;
this.targetClass = targetClass;
this.targetObject = targetObject;
this.targetMethod = targetMethod;
this.methodParams = methodParams;
}
...
/*执行代理机制不一样,子类实现。*/
public abstract Object doProxyChain() throws Throwable;
}
CgProxyChain
@Override
public Object doProxyChain() throws Throwable {
Object methodResult;
/*没有执行链式的最后 则依次执行链式代理*/
/*使用proxyIndex充当计数器,如果没到最后,则取出响应的Proxy对象*/
/*并调用doProxy方法,在Proxy接口的实现中提供相应的横切逻辑,并再次代用doProxyChain直到次数达到*/
/*最后,调用methodProxy的invokeSuper(),执行目标对象的业务逻辑*/
if (proxyIndex < proxyList.size()) {
methodResult = proxyList.get(proxyIndex++).doProxy(this);
} else {
methodResult = methodProxy.invokeSuper(targetObject, methodParams);
}
return methodResult;
}
JdkProxyChain
@Override
public Object doProxyChain() throws Exception {
Object methodResult;
/*没有执行链式的最后 则依次执行链式代理*/
/*使用proxyIndex充当计数器,如果没到最后,则取出响应的Proxy对象*/
/*并调用doProxy方法,在Proxy接口的实现中提供相应的横切逻辑,并再次代用doProxyChain直到次数达到*/
/*最后,调用methodProxy的invoke,执行目标对象的业务逻辑*/
if (proxyIndex < proxyList.size()) {
methodResult = proxyList.get(proxyIndex++).doProxy(this);
} else {
methodResult = targetMethod.invoke(targetObject, methodParams);
}
return methodResult;
}
由上述可以看出一个执行的是invokeSuper一个执行的invoke方法,实际上大体是一样的,只是在学习动态代理的时候就是如此,那就如此吧。果然,我刚才试了一下,有问题!!所以这个方法针对不同的动态代理得重写。
几个代理类
代理类是这样的:定义一个接口,实现类1实现接口,实现类2继承实现类1,并复写里面的方法,达到2个代理实现类的目的。
Proxy
public interface Proxy {
/**
* 执行链式代理
* @param proxyChain
* @throws Exception
*/
Object doProxy(ProxyChain proxyChain) throws Exception;
}
ProxyImplFirst
public class ProxyImplFirst implements Proxy {
/**
* 执行链式代理
*
* @param proxyChain
* @throws Exception
*/
@Override
public Object doProxy(ProxyChain proxyChain) throws Exception {
Object res = null;
Class<?> cls = proxyChain.getTargetClass();
Method method = proxyChain.getTargetMethod();
Object[] params = proxyChain.getMethodParams();
try {
if (intercept(cls, method, params)) {
before(proxyChain);
res = proxyChain.doProxyChain();
after(proxyChain);
} else {
res = proxyChain.doProxyChain();
}
} catch (Exception e) {
e.printStackTrace();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return res;
}
protected void before(ProxyChain proxyChain) {
System.out.println(this + " ImplFirst before");
}
protected void after(ProxyChain proxyChain) {
System.out.println(this + " ImplFirst after");
}
private boolean intercept(Class<?> cls, Method method, Object[] params) {
return true;
}
ProxyImplSecond
public class ProxyImplSecond extends ProxyImplFirst {
@Override
protected void before(ProxyChain proxyChain) {
System.out.println(this + " Impl Second before");
}
@Override
protected void after(ProxyChain proxyChain) {
System.out.println(this + " Impl Second after");
}
}
TestMain
Main实现了ProxyInterface接口,这个接口只有一个SayHello方法。
public class TestMain implements ProxyInterface {
public static void main(String[] args) {
List<Proxy> proxies = new ArrayList<>();
for (int i = 0; i < 2; i++) {
if (i % 2 == 0) {
Proxy proxy = new ProxyImplFirst();
proxies.add(proxy);
} else {
Proxy proxy = new ProxyImplSecond();
proxies.add(proxy);
}
}
TestMain instanceCG = new CgProxyManager().createProxy(TestMain.class, proxies);
ProxyInterface instanceJDK = new JdkProxyManager().createProxy(TestMain.class, proxies);
instanceCG.sayHello();
System.out.println(" ");
instanceJDK.sayHello();
}
@Override
public void sayHello() {
System.out.println(this.getClass().getSimpleName() + " ------------Hello-------- ");
}
}
注意Jdk的动态代理必须返回的接口ProxyInterface,否则会出现类型转换异常是,即将ProxyInterface instanceJDK = new JdkProxyManager().createProxy(TestMain.class, proxies);
改为TestMain instanceJDK = new JdkProxyManager().createProxy(TestMain.class, proxies);
会报错。