代理模式 -- 动态代理(JDK)

March . 04 . 2019
  • 动态代理的好处

 上一篇简单说了说静态代理, 代理模式的优点不言而喻, 不但对真实对象的保护帮了大忙, 还间接地解决了类与类之间的高耦合, 但是静态代理还是有它的缺陷, 仔细看过上一篇的同学应该发现了, 我们的代理类只能代理实现了同一接口的类, 就好比一匹骏马被永远的拴在了树上, 失去了它本该有的价值。 为了解决这一缺陷, 动态代理模式应运而生。

  • 目前常用的动态代理技术

在java中有多种动态代理技术, 比如JDK, CGLIB, Javassist, ASM, 其中最常用的有两种, 一种是JDK动态代理, 这是JDK自带的功能; 另一种是CGLIB, 这是第三方提供的一个技术。目前Spring常用JDK 和 CGLIB, 而Mybatis 还使用了Javassist, 但无论是那种动态代理, 理念都很相似, 都是利用反射等机制来实现。

  • 代码实现

包结构如下图

1.JPG

Negotiation.java

package proxy.dynamic;

// 谈判接口
public interface Negotiation {
    // 谈工资
    void money();
}


Programmer.java

package proxy.dynamic;

// 码农
public class Programmer implements Negotiation {
    @Override
    public void money() {
        System.out.println("No, I need 15000$");      // 码农需要 15000$
    }
}


Client.java

package proxy.dynamic;

// 客户
public class Client implements Negotiation {

    @Override
    public void money() {
        System.out.println("I will pay you 10000$");
    }
}


Agent.java

package proxy.dynamic;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Agent implements InvocationHandler {

    private Object target = null;          // 真实对象

    /**
     * 建立真实对象与代理对象之间的关系
     * @param target 真实对象
     * @return 代理对象
     */
    public Object bind(Object target)
    {
        this.target = target;

        // newProxyInstance() 包含三个参数: 1.类加载器 2.动态代理对象的继承接口 3.实现方法逻辑的代理类
        return Proxy.newProxyInstance(this.target.getClass().getClassLoader(),
                                      this.target.getClass().getInterfaces(),
                                      this);
    }

    /**
     *
     * @param proxy 代理对象
     * @param method 当前调度的方法
     * @param args 当前方法的参数
     * @return 代理结果返回
     * @throws Throwable 异常
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("\n进入代理逻辑..");
        System.out.println("在调度代理对象之前的服务...");
        Object result = method.invoke(this.target, args);    // 相当于调用 money() 方法
        System.out.println("在调度代理对象之后的服务...");
        return result;
    }
}


Test.java

package proxy.dynamic;

public class Test {
    public static void main(String[] args)
    {
        Agent agent = new Agent();                   // 中间商
        Client client = new Client();                // 客户
        Programmer programmer = new Programmer();    // 码农


        Negotiation clientAgent = (Negotiation) agent.bind(client);    // 注意此时客户已经被代理
        clientAgent.money();

        Negotiation programmerAgent = (Negotiation) agent.bind(programmer); // 注意此时码农已经被代理
        programmerAgent.money();
    }
}

首先通过bind方法绑定了代理关系, 然后在代理对象调度 mony方法时进入了代理逻辑.

  • 结果

2.JPG

 88~