最近重温《Head First 设计模式》,里面讲到代理模式时涉及到Java动态代理。
动态代理是Spring Boot AOP面向切面编程的基础。所以打算给自己做个笔记。
示例演示
先编写一个动态代理的Java工程,直观认识如何使用动态代理。
1、定义了一个Subject类型的接口,为其声明了两个方法。
package application;
public interface Subject {
public void rent();
public void hello(String str);
}
2、定义RealSubject类,来实现这个接口,表示我们的真实对象。
package application;
public class RealSubject implements Subject{
@Override
public void rent() {
System.out.println("I want to rent my house");
}
@Override
public void hello(String str) {
System.out.println("hello: " + str);
}
}
3、定义MyInvocationHandler类,必须要实现 InvocationHandler 这个接口,作为一个帮助proxy的类。
package application;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler{
//要代理的真实对象
private Object subject;
public MyInvocationHandler(Object subject){
this.subject = subject;
}
//当代理的方法被调用时,代理会把这个调用转发给InvocationHandler。
//不管代理被调用的事何种方法,处理器被调用的一定是invoke方法。
@Override
public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable {
//在调用真实对象方法前我们可以添加一些自己的操作
System.out.println("before rent house");
System.out.println("Method:" + arg1);
//通过Method类调用真实对象的方法
arg1.invoke(subject, arg2);
//在调用真实对象方法后我们也可以添加一些自己的操作
System.out.println("after rent house");
return null;
}
}
4、编写main函数,实现如何使用动态代理。
package application;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args){
//我们要代理的真实对象
Subject realSubject = new RealSubject();
//InvocationHandler不是proxy,只是一个帮助proxy的类。proxy会把调用转发给它处理。
InvocationHandler handler = new MyInvocationHandler(realSubject);
/*
* 通过Proxy的newProxyInstance方法来创建我们的动态代理对象。
* 第一个参数 handler.getClass().getClassLoader(),传入被代理对象的类载入器。
* 第二个参数realSubject.getClass().getInterfaces(),传入代理需要实现的接口
* 第三个参数handler,传入调用处理器。
*/
Subject subject = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), realSubject
.getClass().getInterfaces(), handler);
System.out.println(realSubject.getClass().getName());
subject.rent();
subject.hello("world");
}
}
运行结果:
原理分析
原理分析之前,我们先看下动态代理的类图。
InvocationHandler
因为Java已经为我们创建 了Proxy类,所以需要有方法来告诉Proxy类你要做什么。由于Proxy类不是我们直接实现的,所以代码不能放在Proxy类中。那么放在哪里?放在InvocationHandler中,其工作是响应代理的任何调用。InvocationHandler可以想象成代理收到方法调用后,请求做实际工作的对象。
在Java的API帮助文档里,InvocationHandler是这样描述的:
InvocationHandler是被代理对象的invocation handler实现的接口,每个代理对象都有一个关联的invocation handler。当代理对象的方法被调用时,这个方法的调用就会被编码并转发给它的 invocation handler的invoke方法。
编写代码的时候,我们会发现InvocationHandler接口只有一个方法:invoke。
public Object invoke(Object arg0, Method arg1, Object[] arg2)
其中:
- arg0表示动态代理对象;
- arg1表示被调用的某个方法的Method对象;
- arg2表示被调用的某个方法的接受参数;
补充:
Method 类是反射最基本类之一,获取一个类对象,然后调用对象里的方法。其中invoke方法如下 :
Object invoke(Object obj, Object... args)
obj表示被调用的对象RealSubject,args表示接受参数,即上面的arg2。
Proxy
Proxy提供了静态方法创建动态类和实例。用的最多是newProxyInstance方法
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
loader表示被代理对象的类加载器,interfaces表示代理需要实现的接口。h表示调用处理器。
总结:
动态代理之所以称为动态,是因为运行时才将它的类创建出来。代码开始执行时,可还没有proxy类,动态代理类是根据传入的接口集创建的。调用动态代理类的方法,实际是InvocationHandler来处理的。