在介绍动态代理之前,有必要先聊聊静态代理。
静态代理优点在于,业务类只需关注业务本身,保证了业务类的重用性,这也是代理模式共有的优点;
其缺点是:
如果使用静态代理,那么真实角色(委托类)必须是实现已经存在的,并将其作为代理对象的内部成员。但实际开发中,一个真实角色必须对应一个代理角色,如果大量使用就会导致类的急剧增加;另外,如果实现并不知道真实角色(委托类),该如何使用代理呢。
这时就需要动态代理上场了。
动态代理类的源码是在程序运行期间有JVM根据反射等机制动态生成的,所以不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时确定的。
是java动态代理机制生成的所有动态代理类的父类,它提供了一系列方法来为一组接口动态地生成代理类及其对象。
// 方法 1: 该方法用于获取指定代理对象所关联的调用处理器
static InvocationHandler getInvocationHandler(Object proxy)
// 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象
static Class getProxyClass(ClassLoader loader, Class[] interfaces)
// 方法 3:该方法用于判断指定类对象是否是一个动态代理类
static boolean isProxyClass(Class cl)
// 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
这是调用处理器接口,它定义了一个invoke方法,用于处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。每次生成动态代理对象时都需要指定一个对应的调用处理器对象。
// 该方法负责集中处理动态代理类上的所有方法调用。第一个参数既是代理类实例,第二个参数是被调用的方法对象
// 第三个方法是调用参数。调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行
Object invoke(Object proxy, Method method, Object[] args)
类加载器,负责将类的字节码加载到JVM中并为其定义类对象。重点是其字节码是由JVM在运行时动态生成的而非与存在任何一个.class文件中。
// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
// 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用
InvocationHandler handler = new InvocationHandlerImpl(..);
// 通过 Proxy 为包括 Interface 接口在内的一组接口动态创建代理类的类对象
Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... });
// 通过反射从生成的类对象获得构造函数对象
Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });
// 通过构造函数对象创建动态代理类实例
Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });
Proxy类的静态方法newProxyInstance对上面后三步进行了封装,简化了动态代理对象获取的过程:
// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
InvocationHandler handler = new InvocationHandlerImpl(..);
// 通过 Proxy 直接创建动态代理类实例
Interface proxy = (Interface)Proxy.newProxyInstance( classLoader,
new Class[] { Interface.class }, handler );
public interface Person {
/**
* 表演
* @param payment 薪资待遇
*/
void play(int payment);
/**
* 吃饭
* @param food 食物名称
*/
void eat(String food);
}
public class Actor implements Person {
@Override
public void play(int payment) {
Log.e("jia", "eat: 开始表演 " + payment);
}
@Override
public void eat(String food) {
Log.e("jia", "eat: 开始吃饭 " + food);
}
}
public class BusinessInvocationHandler implements InvocationHandler {
private Actor actor;
public BusinessInvocationHandler(Actor actor) {
this.actor = actor;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
Log.e("jia", "invoke: 代理之前要做的事情");
Log.e("jia", "invoke: 方法名: " + method.toString());
if (args != null && args.length > 0){
if(args[0] instanceof String || args[0] instanceof Integer){
Log.e("jia", "invoke: 参数: " + args[0]);
}
}
result = method.invoke(actor, args);
Log.e("jia", "invoke: 代理之后要做的事情");
return result;
}
}
public class ProxyFactory {
public static Person getProxy() {
Actor actor = new Actor();
BusinessInvocationHandler handler = new BusinessInvocationHandler(actor);
Person personProxy = (Person) Proxy.newProxyInstance(actor.getClass().getClassLoader(), actor.getClass().getInterfaces(), handler);
return personProxy;
}
}
var proxy = ProxyFactory.getProxy()
proxy.eat("面包")
proxy.play(1200)
动态代理与静态代理相比较,最大的好处就是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样,在接口方法数量比较多的时候我们就可以灵活处理。