代理是一种软件设计模式,目的是通过不直接调用被代理对象而访问其方法,提高代码重用率。
一般用于框架本身处理多种业务逻辑时,通过代理机制方便业务方调用,也利于框架的拓展和解耦。在代码编译时就确定目标类是哪个,可用静态代理。动态代理是在代码运行时加载目标类。
静态代理步骤:
- 目标类和代理类都实现相同的业务接口;
- 代理类的构造方法中传入目标类的实例;
- 在代理类的接口实现中调用目标类实例的接口方法;
静态代理的缺点是不同的业务,需要实现多个目标实现类,代码冗余高。
动态代理原理:
动态代理中涉及到InvocationHandler接口和Proxy类。
Proxy类的newProxyInstance()方法负责代理类实例的创建。
public static Object newProxyInstance(
ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
- ClassLoader loader:指定一个动态加载代理类的类加载器。
- Class<?>[] interfaces:指明目标类实现的接口。
- InvocationHandler h:方法委托类。
Proxy类与普通类的唯一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中。
InvocationHandler的接口方法是invoke(),通过代理类调用目标类方法时,最终都会委托此方法执行。
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
- Object proxy:代理类实例
- Method method:调用的方法对象
- Object[] args:调用的参数
一般会在此方法前后增加一些操作,对某个业务流程补充完善子流程,即面向切面编程(AOP)。
面向切面编程(AOP):是在一系列纵向的控制流程中,把那些相同的子流程提取成一个横向的面。即通过预编译方式和运行期动态代理,实现在不修改源代码的情况下给程序统一添加功能的技术思想。
动态代理步骤:
- 创建目标业务接口和实现类实例;
- 通过Proxy类的newProxyInstance()方法获取代理类实例;
- 创建InvocationHandler方法委托类实例,实现代理类到目标类的方法分派,并支持在目标类业务前后增改操作;
目标类及其接口 示例如下:
/**
* 目标业务接口
*/
public interface IProxy {
void print();
}
/**
* 目标类——实现目标业务接口
*/
public class MyProxy implements IProxy {
@Override
public void print() {
Log.d("test","面向AOP编程——进行中");
}
}
方法委托类 示例如下:
/**
* 方法委托类——实现InvocationHandler接口
*/
public class MyInvocationHandler implements InvocationHandler {
private MyProxy myProxy;
/**
* 通过构造器注入被目标类实例
* @param myProxy
*/
public MyInvocationHandler(MyProxy myProxy) {
this.myProxy = myProxy;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Log.d("test", "面向AOP编程——准备中");//模拟前置操作
Object object = method.invoke(this.myProxy, args);//通过反射调用目标类方法
Log.d("test", "面向AOP编程——结束后");//模拟后置操作
return object;
}
}
外部框架调用目标类 示例如下:
private void testProxy() {
MyProxy myProxy = new MyProxy();
IProxy iProxy = (IProxy) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
myProxy.getClass().getInterfaces(),
new MyInvocationHandler(myProxy));
iProxy.print();
}
ClassLoader是类装载器,将类的字节码装载到 Java 虚拟机(JVM)中并为其定义类对象,然后该类才能被使用。 因此每次生成动态代理类对象时都需要指定一个类装载器对象。
打印log如下:
D/test: 面向AOP编程——准备中
D/test: 面向AOP编程——进行中
D/test: 面向AOP编程——结束后
网友评论