1.jdk动态代理
jdk的动态代理就是根据代理类的接口替我们动态生成实现类,先看案例:
顶层接口
public interface IPerson {
String stduy(String name);
}
这是被代理的类,需要对study方法进行功能增强.
public class Tom implements IPerson {
@Override
public String stduy(String name) {
System.out.println(“执行原生方法”);
return Tom.class.getName()+name;
}
}
Proxy.newProxyInstance()需要传三个参数,一个是类加载器,一个是被代理类的实现接口,第三个是InvocationHandler(接口)的实现类对象,这里是使用的匿名内部类的方式来写的
public class JDKProxy {
//需要代理的对象。这里我们使用Iperson来接收Tom对象
private T target;
public void setTarget(T target) {
this.target = target;
}
public T getProxy(){
Object o = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
/**
*
* @param proxy 生产的代理对象
* @param method 执行的方法
* @param args 执行的方法的入参
* @return 返回方法执行的结果
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法执行前");
//执行原有的方法
Object invoke = method.invoke(target, args);
System.out.println("方法执行后");
return invoke;
}
});
return (T)o;
}
}
测试:
public class Test {
public static void main(String[] args) {
JDKProxy iPersonJDKProxy = new JDKProxy<>();
//需要被代理的对象
IPerson tom = new Tom();
iPersonJDKProxy.setTarget(tom);
//获取代理对象,此时这个对象就不再是上面的tom了
IPerson proxy = iPersonJDKProxy.getProxy();
proxy.stduy("java");
}
}
这时候产生了一个问题,这个代理类到底是啥。其实动态代理的原理很简单.jdk就是用字符串去拼接了一个类,我们正常写代理都是在idea里面直接写,但是动态代理写代码是类似于这样的:
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("public class Tom implements Iperson{")
//拼接方法和字段
stringBuffer.append("}") ;
然后把这个类生成到磁盘。再用classloader把这个生成的临时的class文件加载到jvm,之后这个文件就会被删除了。那有办法看到这个代理类到生成了个啥.
源码里面这一段就是就是生成jdk拼接好的类后的byte数组。我们只需要把他生成到我们想要的位置即可.这个方法参数1就是代理类 的名字,这个查看的方法就是dbug查看,参数二就是被代理类的接口,参数三不管.
来看具体的代码实现:
public class Test {
public static void main(String[] args) throws Exception{
JDKProxy iPersonJDKProxy = new JDKProxy<>();
//需要被代理的对象
IPerson tom = new Tom();
iPersonJDKProxy.setTarget(tom);
//获取代理对象,此时这个对象就不再是上面的tom了
IPerson proxy = iPersonJDKProxy.getProxy();
proxy.stduy("java");
//代理类的字节码数组
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
"$Proxy0", new Class[]{IPerson.class});
FileOutputStream fileOutputStream = new FileOutputStream(""d://$Proxy0.class"");
fileOutputStream.write(proxyClassFile);
fileOutputStream.flush();
fileOutputStream.close();
}
}
运行后:
这个类是字节码文件,咱也看不懂。直接把文件拖拽到idea.
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import jdkProxy.IPerson;
public final class $Proxy0 extends Proxy implements IPerson {
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 String stduy(String var1) throws {
try {
return (String)super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
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("jdkProxy.IPerson").getMethod("stduy", Class.forName("java.lang.String"));
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());
}
}
}
这个类就是字符串拼接好然后写道磁盘的一个类,这些都是jdk帮我们完成的.
看类名字:
public final class $Proxy0 extends Proxy implements IPerson
代理类实现了Iperson接口,还继承了Proxy这个类。
其中最重要的一个方法代理类会实现接口的方法:
public final String stduy(String var1) throws {
try {
return (String)super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
(String)super.h.invoke(this, m3, new Object[]{var1});
(String):就是我们study方法的返回值,被强转了.
super:就是父类,指的就是Proxy.
h:指的就是我们前面生成了InvocationHandler的内部类的对象.
invoke:就是我们生成了InvocationHandler的内部类后,重写的那个方法.
this:就是指代理类$Proxy0的对象
m3:就是接口的方法对象.在代理类的静态代码块中
m3 = Class.forName("jdkProxy.IPerson").getMethod("stduy", Class.forName("java.lang.String"));
new Object[]{var1}:就是方法的m3方法的入参.
到此神秘的面纱就解开了。下篇继续讲解,cglb生成的代理类长啥样