前言:本节内容是个人对JDK动态代理过程中实际代理逻辑以及invoke的第一个参数proxy的理解
动态代理
JDK动态代理的理解
参考链接:
1、Java中InvocationHandler接口中第一个参数proxy详解
2、java动态代理和com.sun.proxy.$Proxy0源码解析
注:分块较容易理解
Target接口
public interface Target {
public String execute();
Target work(String workName);
}
TargetImpl类实现了Target接口
public final class TargetImpl implements Target {
@Override
public String execute() {
System.out.println("TargetImpl execute");
return "execute";
}
@Override
public Target work(String workName) {
System.out.println("工作内容是"+workName);
return this; // 执行完再返回实例
}
}
DynamicProxyHandler 类实现了InvocationHandler 接口
注:这里面是真正的代理逻辑
public class DynamicProxyHandler implements InvocationHandler {
private Target target;
public DynamicProxyHandler(Target target){
this.target = target;
}
/**
* 反编译$Proxy0.class文件看源码:
* this就是创建的目标对象的代理类$Proxy0,它继承了Proxy,实现了目标对象的所有接口
* this.h就是下面的handler
* Proxy里面有这么一个变量InvocationHandler h,构造初始化时将handler赋给h
* m3其实就是通过反射拿到的代理类的对应的方法
* 源码执行后:this.h.invoke(this, m3, new Object[] { xxx }
* 就调用下面的invoke(Object proxy, Method method, Object[] args)
* 总结:
* jdk代理,实际上是根据目标对象的类加载器,目标对象的所有接口,InvocationHandler里面invoke添加的代理逻辑
* 重新生成了一个被代理类
* 补充:
* 下面的invoke方法里有个参数是proxy,这有什么用呢?
* 实际上,我们有时候并不是调用一次就结束了, 类似与链式执行方法
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("dynamic proxy begin...");
/* System.out.println(proxy.getClass().getName()); // com.sun.proxy.$Proxy0
System.out.println(target.getClass().getName()); // proxy.TargetImpl*/
if(method.getName().equals("work")){
method.invoke(target, args);
System.out.println("dynamic proxy end: current method work");
return proxy; // 多次调用
}
Object result = method.invoke(target, args); // 注意这里反射执行的对象一定是被代理对象
System.out.println("dynamic proxy end");
return result;
}
public static void main(String[] args) {
Target target = new TargetImpl();
DynamicProxyHandler handler = new DynamicProxyHandler(target);
// 被代理类的类加载器,被代理类的所有接口,InvocationHandler调用处理逻辑
// public final class $Proxy0 extends Proxy implements xxx 这个就是新生成的代理类
Target proxy = (Target) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), handler);
String result = proxy.execute();
System.out.println();
Target work = proxy.work("写代码").work("开会").work("下班"); // 链式执行
/*
dynamic proxy begin...
TargetImpl execute
dynamic proxy end
dynamic proxy begin...
工作内容是写代码
dynamic proxy end: current method work
dynamic proxy begin...
工作内容是开会
dynamic proxy end: current method work
dynamic proxy begin...
工作内容是下班
dynamic proxy end: current method work
*/
}
}
注:
需要根据反编译$Proxy0.class文件结合源码理解invoke执行过程
jdk动态代理本质
运行期间,实际上是根据目标对象的类加载器,目标对象的所有接口,InvocationHandler里面invoke添加的代理逻辑重新生成了一个被代理类,并且实例化得到代理对象
最终,代理对象调用方法,还是通过目标对象调用方法
从中可以看出jdk动态代理的缺陷,因为实现了目标对象的所有接口。
若目标对象未实现接口只是普通类,或要代理的方法不在接口中预先定义而是目标对象的原生方法,则jdk动态代理无法做到。
Cglib动态代理
Target 接口
public interface Target {
public String execute();
//Target work(String workName);
}
TargetDomain 实体类
public class TargetDomain {
public String execute(){
String msg = "--------test------------";
System.out.println(msg);
return msg;
}
}
TargetImpl 实现Target 接口
public class TargetImpl implements Target {
@Override
public String execute() {
System.out.println("TargetImpl execute");
return "execute";
}
public Target work(String workName) {
System.out.println("工作内容是"+workName);
return this; // 执行完再返回实例
}
}
关键在于:实现一个MethodInterceptor接口,重写intercept方法,添加代理逻辑
public class CglibMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib proxy begin...");
Object result = methodProxy.invokeSuper(obj, args);
System.out.println("cglib proxy end");
return result;
}
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(TargetImpl.class.getClassLoader());
// 设置需要代理的类
enhancer.setSuperclass(TargetImpl.class);
// 设置一个callback对原始类方法做增强
enhancer.setCallback(new CglibMethodInterceptor());
// 设置方法过滤器,分别执行callback
//enhancer.setCallbackFilter();
// 创建代理对象
//TargetDomain proxy = (TargetDomain) enhancer.create();
TargetImpl proxy = (TargetImpl) enhancer.create();
String result = proxy.execute();
System.out.println(result);
/*
cglib proxy begin...
--------test------------
cglib proxy end
*/
}
}
CglibProxyFactory 包装类,传入目标对象的类对象,生成代理对象
public class CglibProxyFactory {
public static Object getProxy(Class<?> clazz) {
// 创建动态代理增强类
Enhancer enhancer = new Enhancer();
// 设置类加载器
enhancer.setClassLoader(clazz.getClassLoader());
// 设置被代理类
enhancer.setSuperclass(clazz);
// 设置方法拦截器
enhancer.setCallback(new CglibMethodInterceptor());
// 创建代理类
return enhancer.create();
}
}
CglibProxy 测试类
public class CglibProxy {
public static void main(String[] args) {
TargetImpl proxy1 = (TargetImpl) CglibProxyFactory.getProxy(TargetImpl.class);
proxy1.execute(); // 接口
System.out.println();
proxy1.work("吃饭");
System.out.println();
TargetDomain proxy2 = (TargetDomain) CglibProxyFactory.getProxy(TargetDomain.class);
proxy2.execute();
}
/*
cglib proxy begin...
TargetImpl execute
cglib proxy end
cglib proxy begin...
工作内容是吃饭
cglib proxy end
cglib proxy begin...
--------test------------
cglib proxy end
*/
}
总结:
Cglib动态代理是通过继承目标类生成子类对象,即代理对象
可以看出:
1、可以代理未实现接口的目标对象,也可以代理实现接口的目标对象,拦截目标对象的原生方法或实现的接口中的方法
2、JDK 动态代理只能只能代理实现了接口的类,而 CGLIB 可以代理未实现任何接口的类
3、感觉CGLIB也能把JDK 动态代理做的事情做完
特别注意:
1、Cglib动态代理的目标对象不能是final修饰,因为需要继承生成子类
2、JVM 层面 :静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。而动态代理是在运行时动态生成类字节码,并加载到 JVM 中的。
3、动态代理更灵活