底层代码根据我自己不完全了解,应该是实现被代理类的接口,然后代理类调用代理接口的里方法时,会去调用InvocationHandler 里重写的的invoke实现的方法,invoke方法里包括了原本的要执行的方法和增强的逻辑代码。
定义一个父类接口
public interface Fu {
public String say();
}
定义一个子类实现父类
public class Zi implements Fu {
@Override
public String say() {
System.out.println("我是Zi类");
return "我是Zi类的say方法的返回值";
}
}
实现并测试动态代理类
public class ProxyText {
public static void main(String[] args) {
Zi z = new Zi();
Fu f = (Fu) Proxy.newProxyInstance(z.getClass().getClassLoader(), z.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理前。。。。");
if (method.getName() == "say") {
Object o = method.invoke(z);
System.out.println(o);
}
System.out.println("代理后。。。。");
return "代理类";
}
});
String s = f.say();
System.out.println(s);
}
}
输出:
代理前。。。。
我是Zi类
我是Zi类的say方法的返回值
代理后。。。。
代理类
使用注意:
1.一般情况下JDK的动态代理类只能代理有接口的类,想要代理没接口的必须使用cglib。
2.Fu f = (Fu) Proxy.newProxyInstance,要强制转换成被代理类的接口
先了解下一些要懂的知识:
1.通过实现 InvocationHandler 接口创建自己的调用处理器,当使用者调用了代理对象所代理的接口中的方法的时候,就会去执行InvocationHandler 里的内容
2.Proxy 里有个构造方法,传进来的是InvocationHandler 这个类
newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
第三个参数
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
$Proxy0代理类会继承这个类,所以Proxy(InvocationHandler h)也会被继承,h里面就有invoke方法,invoke放法里面有我们要增强的逻辑。
3.看看NewProxyInstance方法生成的$Proxy0代理类的源码,大致如下,照着别人改的,自己不会生成o(* ̄︶ ̄*)o
public final class $Proxy0 extends Proxy implements Fu {
private static Method m1;
private static Method m0;
private static Method m3;
private static Method m2;
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals",
new Class[] { Class.forName("java.lang.Object") });
m0 = Class.forName("java.lang.Object").getMethod("hashCode",
new Class[0]);
m3 = Class.forName("***.Zi").getMethod("say",
new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString",
new Class[0]);
} catch (NoSuchMethodException nosuchmethodexception) {
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
} catch (ClassNotFoundException classnotfoundexception) {
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
/*super(paramInvocationHandler),是调用父类Proxy的构造方法,就是前面那个我说的构造方法
Proxy(InvocationHandler h)*/
public $Proxy0(InvocationHandler invocationhandler) {
super(invocationhandler);
}
@Override
public final boolean equals(Object obj) {
try {
return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue();
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public final int hashCode() {
try {
return ((Integer) super.h.invoke(this, m0, null)).intValue();
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final void say() {
try {
super.h.invoke(this, m3, null); //就是这个地方 调用h.invoke()
return;
} catch (Error e) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public final String toString() {
try {
return (String) super.h.invoke(this, m2, null);
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
}
来看看这个源码,,里面主要有两个方法值得引起我们注意:
Ⅰ.
public $Proxy0(InvocationHandler invocationhandler) {
super(invocationhandler);
}
super(paramInvocationHandler),是调用父类Proxy的构造方法,就是前面那个我说的构造方法
Proxy(InvocationHandler h),这段就是执行了构造方法
ⅠⅠ.
public final void say() {
try {
super.h.invoke(this, m3, null); //就是这个地方 调用h.invoke()
return;
} catch (Error e) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
①:当调用f.say()方法时,其实调用的$Proxy0的say()方法,从而调用父类Proxy中传进来第三个参数(h)的的Invoke方法。
②:h.invoke的h就是我们继承父类那个传进来的h,而这个h的invoke方法就是调用newProxyInstance里第三个参数里面的invoke方法,m3就是这个方法。
③:
this.h:看Proxy的源码,发现h就是Proxy类中的InvocationHandler h,并且在指向了Zi实例
this:指此动态代理类$Proxy0本身
m3:看静态代码块,指的就是反射生成的Fu接口的say()方法的Method对象,我也不是很懂,暂时不理他
④:h.invoke(this, m3, null)方法并不是反射的invoke()方法,只是普通的调用h重写的invoke方法,调用时就将动态代理类$Proxy0本身、m3为Fu反射的Method
⑤然后在反射调用的代码前后,可以插入自定义的一些代码,就是增强方法了
该了解的讲完,大致的执行过程过一遍
调用f.say()
↓
首先生成一个缓存在java虚拟机中的$Proxy0代理类,里面有两个主要的东西
InvocationHandler h:h里有个invoke方法,就是我们重写的方法
say()方法:执行了h里的invoke方法,并把这个$Proxy0类,say()方法和方法参数传递过去
↓
开始执行$Proxy0.say()方法里的h.invoke()
↓
进入到public Object invoke(Object proxy, Method method, Object[] args)里
↓
System.out.println("代理前。。。。");
↓
method.invoke(z);
这个z就是被代理类,method就是接口的方法,所以整个就是调用Zi类里的say()方法
↓
System.out.println("代理后。。。。");
↓
return "代理类";
↓
f.say()接收这个"代理类",并打印输出
总结
这篇文章,不一定准确,我仅仅只是对源码和执行过程大致了解了下而已,写出的这个过程和解析,应该是会有点错误,不过整体还是对的,等我那天牛逼了都懂了,我就回来修改补充