这里主要是对比jdk的动态代理和cglib的动态代理,然后再对jdk的代理模式进行原理分析。有不对的地方请指出,谢谢。
jdk实现动态代理:
jdk的动态代理主要是需要一个类(Proxy)和一个接口(InvocationHandler),Proxy主要是用来生成class文件并加载到内存(这个后面会详细讲到),InvocationHandler主要是调用代理方法。
以下是代码:
//目标接口
public interface TargetInterface {
void print();
}
//目标实现类
public class Target implements TargetInterface {
@Override
public void print() {
System.out.println("打印的主要内容");
}
}
//代理类
public class Agent implements InvocationHandler {
private TargetInterface target;
public Object getInstance(TargetInterface target) {
this.target = target;
Class extends TargetInterface> clazz = target.getClass();
System.out.println("clazz:"+clazz);
return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("proxy:"+proxy.getClass());
System.out.println("打印前...");
method.invoke(target,args);
System.out.println("打印后...");
return null;
}
}
//测试类
public class Test {
public static void main(String[] args) throws IOException {
TargetInterface target = (TargetInterface) new Agent().getInstance(new Target());
System.out.println("target:"+target.getClass());
target.print();
}
}
这个是个非常简单的测试,看到这里的调用不知道原理肯定还是会很懵逼的,所以下面对代码进行分析。
修改Test方法,这样是获取jdk动态代理生成的$Proxy0对象,具体的生成方法就不详细介绍了,其实就是四点,1、写一个$Proxy0.java的文件。2、调用jdk的方法对这个文件进行编译生成class文件。3、利用反射将这个class文件生成对象并加载。4、删除.java及.class文件。
//测试类
public class Test {
public static void main(String[] args) throws IOException {
TargetInterface target = (TargetInterface) new Agent().getInstance(new Target());
System.out.println("target:"+target.getClass());
target.print();
byte[] datas = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{TargetInterface.class});
FileOutputStream f=new FileOutputStream("$Proxy0.class");
f.write(datas);
f.flush();
f.close();
}
}
这样在项目的根目录下会生成一个$Proxy.class文件,打开这个文件
public final class $Proxy0 extends Proxy implements TargetInterface {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void print() throws {
try {
super.h.invoke(this, m3, (Object[])null);//重点
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("com.cf.design.proxy.jdk.TargetInterface").getMethod("print");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
这个类继承Proxy,并实现了目标接口类,重点处显示的是调用了父类的h.invoke(),也就是Proxy的h.invoke(),查看Proxy可以发现h就是InvocationHandler,所以就是为什么代理类Agent需要实现InvocationHandler了,其实调用的就是Agent的invoke()。
jdk动态代理机制只能代理实现接口的类,一般没有实现接口的类不能进行代理,cglib就是针对类来实现代理的,它的原理是对指定的目标类生成它的一个子类,并覆盖其中方法实现增强,但因为采用的事集成,所以不能对final修饰的类进行代理。
cglib代理和jdk代理的区别:
1、cglib主要是针对类来进行代理,而jdk代理必须要有接口。
2、cglib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用java反射效率更高。
需要导入两个包:cglib.jar、asm.jar
下面是测试代码:
//目标类
public class Target {
public void print(){
System.out.println("打印的内容");
}
}
//代理类
public class Agent implements MethodInterceptor {
public Object getInstance(){
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(Target.class);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("打印之前...");
methodProxy.invokeSuper(o,objects);
System.out.println("打印之后...");
return null;
}
}
//测试类
public class Test {
public static void main(String[] args){
Target target= (Target) new Agent().getInstance();
target.print();
}
}
刚开始看动态代理的时候觉得特别难以理解,通过对源码的分析后才发现原来是这么回事,也让自己收获了很多开发上的技巧,比如通过IO去写一个java文件,再编译成class文件,再使用反射成为对象,加载进内存,以前就从来没想过还可以这么干,所以技术还是需要灵活运用。