嘚不嘚
好久没有更新blog了,理论只有应用起来才能有更加深刻的体会。虽然同类的博客到处都是,但是每个人对同一个技术的理解是不一样的,每个人注重的细节也不太一样,不管有没有人看,写博客只是作为个人的一个习惯和自我的学习的总结过程。
概述
- 作用:在不变原有功能的基础上增加新的功能。
- 应用场景:AOP,日志的输出,事务。
- 技术前提:为了更好的理解动态代理,要熟悉java反射。
应用介绍
- 为了方便理解代理声明几个变量:
- 代理类入口:实现invocationHandler的类就是代理类的入口,其实仅仅是代理类的,而不是真正的代理类。他的作用:创建真正的代理类,执行被代理类的要执行的方法和调用功能增强的方法。
- 被代理类:需要被代理的接口。
- 代理类:真实的代理类,是在代码中无法直接看到的,对被代理类方法的调用是从代理类调用开始的。
- 代理实现(代码我是用我参考的博主的):
- 业务定义接口:也就是被代理类实现的接口
public interface ICook {
void dealWithFood();
void cook();
}
- 被代理类:实现业务定义接口,同时该类是需要功能增强的类
public class CookManager implements ICook {
@Override
public void dealWithFood() {
System.out.println("food had been dealed with");
}
@Override
public void cook() {
System.out.println("cook food");
}
}
- 代理类入口:该类的作用是创建真实的代理类,同时完成功能的增强。
public class DynamicProxyHandler implements InvocationHandler{
Object realCookManager;
DynamicProxyHandler(ICook realCookManager){
this.realCookManager = realCookManager;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("invoke start");
System.out.println(method.getName());
method.invoke(realCookManager,args);
System.out.println("invoke end");
return null;
}
}
- 测试类:
public class Main {
public static void main(String[] args){
CookManager cookManager = new CookManager();
DynamicProxyHandler dynamicProxyHandler = new DynamicProxyHandler(cookManager);
ICook iCook =(ICook)Proxy.newProxyInstance(dynamicProxyHandler.getClass().getClassLoader(),cookManager.getClass().getInterfaces(), dynamicProxyHandler);
//打印一下代理类的类名
System.out.println(iCook.getClass().getName());
iCook.dealWithFoot();
iCook.cook();
}
}
这里的代码我是使用其他博主的代码,因为我每太搞明白,真实代理类是怎么搞来的,下面贴出我的代理类入口类代码:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JavaProxy implements InvocationHandler {
/**
* 被代理类的对象
*/
private Object proxyObj;
/**
* Proxy 动态代理类动态创建代理对象
* newProxyInstance() ->创建代理对象
*
* 仅仅提供一个代理类的入口,创建一个继承Proxy,实现被代理的类的所有接口,
* 代理类会实现接口中定义的所有方法,
* @param proxyObj 被代理类的对象
* @param <T>
* @return
*/
public <T> T getProxyObj(Object proxyObj) {
this.proxyObj = proxyObj;
/**
* 被代理类的类加载器,被代理类实现的接口,实现invocationHandler的代理对象
*/
Object o = Proxy.newProxyInstance(proxyObj.getClass().getClassLoader(), proxyObj.getClass().getInterfaces(), this);
return (T) o;
}
/**
* @param proxy 代理类的真实对象,
* @param method 调用真实对象的某个方法的method对象
* @param args 调用方法的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before,,,,,");
System.out.println("proxy method......");
Method[] methods = proxy.getClass().getDeclaredMethods();
// Lists.newArrayList(methods).forEach(mm -> System.out.println(mm.getName()));
System.out.println();
System.out.println("被代理类proxy="+proxyObj.getClass().getName());
System.out.println("代理类proxy="+proxy.getClass().getName());
System.out.println("代理类->father="+proxy.getClass().getSuperclass().getName());
System.out.println("代理类->father interface=");
Class [] classes = proxy.getClass().getInterfaces();
for (int i = 0; i < classes.length; i++) {
System.out.println(classes[i].getName());
}
System.out.println("method=" + method);
Object o = method.invoke(proxyObj, args);
System.out.println((String)o);
//Object o1 = method.invoke(proxy, args);
System.out.println("after,,,,,");
return null;
}
}
- 我的入口代理类说明:参数比较多刚开始看的我也很迷惑,理解参数有助于理解代理类是如何实现的和使用的。先提及一下反射,反射获取某个类型(也就是类)的方法、属性或者完成对某个类型方法的调用是通过对该类型的字节码文件操作进行调用的。java动态代理生成了真实代理类的.class文件,通过对.class文件的操作完成对真实代理类方法的调用。
扩:.java->.class,.class文件是字节码文件,是JVM可以识别的文件,该文件内容不是JVM的执行指令,JVM执行的指令是将.class文件解释之后的指令。
-
getProxyObj方法:生成代理类真实对象的方法。该方法的参数proxyObj类型Object ,该类是真实的需要被代理的类,注意这个不是接口类,而是真实的对象类,该对象的真实类型决定了调用那个类的方法(多态)。例如:A接口是业务接口类,B、C是A接口的实现类,代理类中传入的真实类型是B的对象还是C的对象决定了调用的方法。
-
Proxy.newProxyInstance:生成代理类的方法。该方法的参数
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
。参数依次是类加载器,被代理类实现的接口,代理类入口。注意这个代理类的入口对象,最后能调用
JavaProxy.invoke()
方法就是该参数决定的。 -
invoke:在该方法中完成了对代理类功能的增强。该方法的参数:
(Object proxy, Method method, Object[] args)
参数依次是代理类的真实对象,被调用的方法和调用方法的参数。 -
遇到的问题:起初我是这么写的
method.invoke(proxy, args)
方法一直就一执行下去,根本停不下来,这样换一下好了method.invoke(proxyObj, args)
,它俩之间的区别是一个用真实的代理类proxy
对象,另一个使用被代理类的对象proxyObj
。出现死循环的主要原因,真实的代理类自己对自己的方法进行调用当然会出现死循环。想知道invoke中 的各个对象的含义,可以通过反射获取其名称。
原理介绍
- 直接看博主生成的真实的代理代码:就是这段代码我不知道怎么搞出来的。。。
public final class $Proxy0 extends Proxy implements ICook {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m4;
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})).booleanValue();
} 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 void cook() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void dealWithFoot() throws {
try {
super.h.invoke(this, m4, (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)).intValue();
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("com.company.ICook").getMethod("cook", new Class[0]);
m4 = Class.forName("com.company.ICook").getMethod("dealWithFoot", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
如果对这个类怎么产生的感兴趣自己研究下哈,看了这个类我还是比较困惑,就算生成了这个真实的代理类,那么究竟是如何完成对这个DynamicProxyHandler.invoke()
方法进行调用的呢?看两个地方:public $Proxy0(InvocationHandler var1) throws { super(var1); }
super.h.invoke(this, m4, (Object[])null);
。首先DynamicProxyHandler
这个类实现了InvocationHandler这个接口,通过构造方法完成了对象的初始化,然后看``super.h.invoke`,如果h的真实对象是DynamicProxyHandler对象的话,那么一切就解决了。这两个super指的对象都是在Proxy代理对象中的,java动态代理其实就是通过反射对java的真实代理类进行调用,关键在于生成了一个新的.class文件。
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h){
//所有被实现的业务接口
final Class<?>[] intfs = interfaces.clone();
//寻找或生成指定的代理类
Class<?> cl = getProxyClass0(loader, intfs);
//通过反射类中的Constructor获取其所有构造方法
final Constructor<?> cons = cl.getConstructor(constructorParams);
//通过Constructor返回代理类的实例
return cons.newInstance(new Object[]{h});
}
cl是生成的真实代理类,也就是$Proxy0
,通过cons.newInstance(new Object[]{h})
完成对类Proxy
中属性protected InvocationHandler h;
的赋值,真实代理类调用父类的构造方法是protected Proxy(InvocationHandler h) { Objects.requireNonNull(h); this.h = h; };
下一篇介绍CGLIB代理,如果有问题各位大神多多指教,说的比较乱,思路我也不太很清晰,有问题请大神不吝赐教。
参考地址:https://www.jianshu.com/p/23d3f1a2b3c7