动态代理与静态代理

代理是基本的设计模式之一,使用代理对象代替真实对象的方法调用,可以扩展真实对象某些方法的功能。

在 java 中又分为静态代理和动态代理, 先看静态代理:

静态代理

public class DynamicDemo {

    public static void main(String[] args) {
        /**
         * 静态代理示例
         */
        System.out.println("=======静态代理========");

        // user 真实对象
        Save user = new UserDAO();
        // userTransaction 是代理对象
        Save userTransaction = new UserTransaction(user);
        // 使用代理对象执行方法
        userTransaction.save("dc1");
    }

}

interface Save {
    /**
     * 保存
     */
    void save(String name);

    /**
     * 删除
     * @param name
     */
    void delete(String name);
}

class UserDAO implements Save {

    public void save(String name) {
        System.out.println("真实对象执行 save 方法");
    }

    public void delete(String name) {
        System.out.println("真实对象执行 delete 方法");
    }
}

/**
 * 手动编写代理类
 */
class UserTransaction implements Save {

    /**
     * 真实对象
     */
    private Save realObject;

    UserTransaction(Save save) {
        this.realObject = save;
    }

    /**
     * 在真实对象前后做一些操作
     */
    public void save(String name) {
        System.out.println("静态代理: 在真实对象做事之前,代理要做的事情");
        // 真实对象开始执行save方法
        realObject.save(name);
        System.out.println("静态代理: 在真实对象做事之后,代理要做的事情");

    }

    public void delete(String name) {

        /**
         * 什么都不做,直接执行真实对象的 delete 方法。
         * 这里就体现了静态代理方式的弊端,哪怕我只想代理一个方法,也需要实现所有方法。
         * 只用真实对象代理。有很多无意义的代码
         */
        realObject.save(name);
    }
}

通过以上代码可以知道java中的静态代理有几个关键的地方:

  1. 代理对象和真实对象实现了相同的接口,或者继承同一个类。换句话说就是可以向上转型为相同的对象
  2. 真实对象要作为代理对象中一个属性。方便真实对象调用方法

有时我们只想扩展原对象的某个方法,给这个方法添加一些功能。但是我们仍要编写一个代理类,实现真实类中的所有方法。导致很多冗余代码, java 中的动态代理可以解决这个问题

动态代理

先看代码:

public class DynamicDemo {

    public static void main(String[] args) {
        /**
         * 动态代理示例
         */
        System.out.println("======动态代理======");

        // 动态生成一个代理对象
        Save saveProxy = (Save) Proxy.newProxyInstance(
                // 真实对象的类加载器
                user.getClass().getClassLoader(),
                // 要代理的接口,数组
                new Class[]{ Save.class},
                // 将 user 真实对象传入代理处理类
                new DynamicHandler(user)
        );
        // 代理对象执行方法,内部会调用真实对象执行对应方法
        saveProxy.save("dc1");
    }

}

interface Save {
    /**
     * 保存
     */
    void save(String name);

    /**
     * 删除
     * @param name
     */
    void delete(String name);
}

class UserDAO implements Save {

    public void save(String name) {
        System.out.println("真实对象执行 save 方法");
    }

    public void delete(String name) {
        System.out.println("真实对象执行 delete 方法");
    }
}

/**
 * 代理对象要执行的方法
 */
class DynamicHandler implements InvocationHandler {

    private Object realObject;
    public DynamicHandler(Object real) {
        this.realObject = real;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理对象类:" + proxy.getClass());
        System.out.println("要代理的方法是: " + method.getName() );
        System.out.println("参数是:");
        for (Object arg : args) {
            System.out.printf(arg + " ");
        }
        System.out.println("");
        if(method.getName().equals("delete")){
            System.out.println("执行了 delete 方法");
        }

        /**
         * 这里的操作体现了动态代理的优点: 在需要代理的方法前后编写功能
         */
        if(method.getName().equals("save")){
            System.out.println("执行了 save 方法");
            System.out.println("真实对象执行save方法前");
        }
        // 真实对象执行方法
        method.invoke(this.realObject,args);

        if(method.getName().equals("save")){
            System.out.println("真实对象执行save方法后");
        }
        return null;
    }
}

从上面代码中可以发现,实现动态代理的关键点:

  1. InvocationHandler 的实现类,invoke 方法中编写你要扩展的功能
  2. InvocationHandler 的实现类中要有一个属性保存真实对象。也就是例中的 realObject, 以便真实对象调用方法,如果不需要真实对象调用方法,那么也就不需要这个真实对象了。
  3. 使用 Proxy.newProxyInstance 方法创建代理对象。

动态代理实际上是JVM在运行期动态创建class字节码并加载的过程,它并没有什么黑魔法。它创建的字节码就是我们在静态代理中编写的代理类。只不过不用我们手动编写了而已。