比如有个需求:在某个方法执行之前添加一条日志。这里我们并不像修改原有的代码。比如原来的类为class1,其中要添加日志的方法为doSomething,我们可以通过代理的方式来进行。其模型如下
public interface Class1Interface {
public void doSomething();
}
public class Class1 implements Class1Interface {
@Override
public void doSomething() {
Log.e("tag", "class1");
}
}
public class Class1Proxy implements Class1Interface {
Class1 clzz = new Class1();
@Override
public void doSomething() {
System.out.println("Begin log");
clzz.doSomething();
System.out.println("End log");
}
}
真正使用
Class1Proxy proxy = new Class1Proxy();
proxy.doSomething();
通过这种方式虽然可以达到代理的目的,但是如果还有类也要实现该方法的话,那么就需要很多个对应的Proxy类,这时候就需要动态代理来解决这个问题了。主要用的是Proxy类的newProxyInstance方法,声明如下
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
参数说明
- loader,设置为目标对象class1对应的ClassLoader
- interfaces,设置为目标对象class1所实现的接口类型,这里是Class1Interface
- h,是一个实现了InvocationHandler接口的类对象,我们通过它的构造函数把目标对象class1注入。
代码如下:
Class1 class1 = new Class1();
Class1Interface class1Proxy = (Class1Interface) Proxy.newProxyInstance(
class1.getClass().getClassLoader(),
new Class<?>[] { Class1Interface.class },
new InvocationHandlerForTest(class1));
class1Proxy.doSomething();
通过 Proxy.newProxylnstance 方法创建的对象,是一个实现了 Class1Interface 接口的对 象,也就是 classI Proxy。 执行class1Proxy的doSomething方法,其实是在执行InvocationHandlerForTest类 的 invoke方法。这个 invoke方法是代理模式的设计思想。 它有一个 method参数,执行 method 的 invoke方法,就是在执行 class1 的 doSomething方法。
public class InvocationHandlerForTest implements InvocationHandler {
private Object target;
public InvocationHandlerForTest(Object target) {
this.target = target;
}
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
Log.e("tag", "日志开始");
Object obj = method.invoke(target, objects);
Log.e("tag", "日志结束");
return obj;
}
}
Proxy.newProxyInstance方法可以“套”在任何一个接口类型的对象上,为这个对象增 加新功能,所以我们称之为“动态代理” 。 在插件化的领域, Proxy.newProxyInstance生成的对象,直接替换掉原来的对象,这个技术就是 Hook 技术 。
这里总结一下静态代理与动态代理的区别
- 静态代理:需要代理对象和目标对象实现一样的接口,静态代理在编译时就已经实现,编译完成后代理类是一个实际的class文件。可以在不修改目标对象的前提下扩展目标对象的功能,由于代理对象需要与目标对象实现一样的接口,所以会产生过多的代理类,一旦接口增减方法,目标对象与代理对象都要进行修改。
- 动态代理:动态代理对象不需要实现接口,但是要求目标对象必须实现接口,动态代理是在运行时动态生成的,即编译完成后没有实际的class文件,而是在运行时动态生成类字节码,并加载到JVM中