静态代理和动态代理

代理是一种软件设计模式,目的是通过不直接调用被代理对象而访问其方法,提高代码重用率。

一般用于框架本身处理多种业务逻辑时,通过代理机制方便业务方调用,也利于框架的拓展和解耦。在代码编译时就确定目标类是哪个,可用静态代理。动态代理是在代码运行时加载目标类。

静态代理步骤:

  1. 目标类和代理类都实现相同的业务接口;
  2. 代理类的构造方法中传入目标类的实例;
  3. 在代理类的接口实现中调用目标类实例的接口方法;

静态代理的缺点是不同的业务,需要实现多个目标实现类,代码冗余高。

动态代理原理:
动态代理中涉及到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):是在一系列纵向的控制流程中,把那些相同的子流程提取成一个横向的面。即通过预编译方式和运行期动态代理,实现在不修改源代码的情况下给程序统一添加功能的技术思想。

动态代理步骤:

  1. 创建目标业务接口和实现类实例;
  2. 通过Proxy类的newProxyInstance()方法获取代理类实例;
  3. 创建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编程——结束后

你可能感兴趣的:(静态代理和动态代理)