软件开发的最终目的是以不变应万变,为此我们的前辈们总结了很多模式来应对各种需求,代理模式就是其中的一种。假如我们已经理解了静态代理,那动态代理就相对容易理解了,被代理的类是动态变化的,是一个泛型的 target; 那到底怎么实现的呢。
Java的动态代理,会用到InvocationHandler、Proxy 。代码设计的时候考虑到方便扩展和维护,都需要抽象出公共特性,例如共有的方法。 动态代理的出现是为了解决:
1.控制外部调用
2.增强某些方法的处理
3.需要在N多类的同一个方法执行前或者执行后做一些事情(AOP)
代码例子:
/**
* 公共接口,抽象出公共方法
*/
public interface Person {
public void giveTask();
}
/**
* 具体的人,例如学生,实现交作业的方法
*/
public class Student implements Person {
private String name;
public Student(String name) {
this.name = name;
}
public void task() {
System.out.println(name+"交作业");
}
}
/**
* 具体的人,老师改作业
*/
public class Tescher implements Person {
@Override
public void task() {
System.out.println("老师改作业");
}
}
如果不用代理,那直接就调用Student和Teacher类了,那我们现在想在所有的task方法执行前都干点事情,也许你说可以用抽象类,然后抽象类中实现方法进行处理,也不是不行,但是这样带来问题,我将来想扩展更多的类是不是需要改动父类或者我super调用不及时,等等这样的问题。 所以动态代理的出现就是为了不侵入的方式去增强原有的方法。
/**
* 我想在原有的老师或者学生做事情前,干点啥又不用去改动原来的方法
* 这就是一个Person代理方法处理,并不是代理类哦,代理是通过他动态创建的
* 在代理类里调用本类的invoke方法,从而调用目标类的方法
* @param <T> 泛型
*/
public class PersonInvocationHandler<T> implements InvocationHandler {
// 被代理的对象
T target;
public PersonInvocationHandler(T target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理执行" + method.getName() +"方法");
// 用反射执行被代理对象的方法
Object ret = method.invoke(target,args);
return ret;
}
}
具体调用:
public static void main(String[] args) {
// 学生
Person xiaoming = new Student("小明");
// 老师
Person laoshi = new Tescher();
// 构建代理方法处理者,就是通过他要在别人方法前后干点事情
PersonInvocationHandler invocationHandler1 = new PersonInvocationHandler(laoshi);
// 通过Proxy.newProxyInstance 生成一个新的代理类对象,这个方法硬生生的创造出了一个新的类出来$Proxy0
// 并且按照定义的接口,实现了接口中的方法,方法内部是调用 invocationHandler的invoke()
Person person1 = (Person)Proxy.newProxyInstance(Tescher.class.getClassLoader(),new Class<?>[]{Person.class},invocationHandler1);
// 表面上看是调用Person.task(), 实际上是调用新创建的代理类的task()方法,神不知鬼不觉。
person1.task();
}
运行结果如你猜想的一样:
代理执行task方法
老师改作业
动态代理之所以动态,就在于用泛型接收被代理对象和完全动态的创建了一个新的代理类出来。 怎么知道创建了新的类呢,在Proxy.newProxyInstance前设置一下参数,就会输出动态创建的类到项目目录下,$Proxy0.class这个就是刚刚创建被编译成字节码的文件。
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
利用IDEA反编译查看一下
/*
继承Proxy,方便调用invocationHandler,还记得Proxy.newProxyInstance传进去的,实现Person接口
*/
public final class $Proxy0 extends Proxy implements Person {
//以下是提取的 方法
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
// 其他部分省略了,这里是获取方法,利用反射找到被代理的方法
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.alienjun.dynamictest.Person").getMethod("task");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
// 根据接口实现task()方法,这里只是调用父类的invocationHandler.invoke(),具体执行task是在invocationHandler.invoke中执行的
public final void task() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
}
到这里就已经明白整个动态代理过程了,
动态代理
其中怎么创建的类,又是怎么被编译和加载调用的更多细节可以细看源码。
网友评论