动态代理:没有具体类型(只有Object类型)的代理类,相较于静态代理而言,好处是大大的减少了代理类的数量,作用是一样的。(静态代理可看我上一篇)

下面演示基于jdk的动态代理:因为用到了jdk,所以先不得不介绍写下用到的jdk方法(我也想纯手写动态代理,可我不会呀、没有办法我就是这么强大,啦啦啦啦啦啦啦~~~~)。

名词:

处理程序:处理程序是指和代理类相关联的一个对象,它和代理类是一对一的关系(下面会解释先记住这个名词)。

invoke方法:记住此方法是属于处理程序的和代理类没有半点关系,但代理类需要用它去调用委托类的方法。

一:InvocationHandler

java.lang.reflect

Interface InvocationHandler

官方解释:InvocationHandler是由代理实例的调用处理程序实现的接口 。

每个代理实例都有一个关联的调用处理程序。 当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke方法。

个人解释(如有错误解释欢迎及时探讨):

  1.代理类必须要实现InvocationHandler接口,这样代理类就会拥有一个关联的调用处理程序 (我们就理解成:这个关联调用处理程序也是一个对象,代理类依附于他,这个关联调用处理程序就是我们代理类的代言人,一个代理类拥有一个调用处理程序)。

  2.这个处理程序有一个invoke方法,当我们的代理对象需要调用委托方的方法时,就将需要调用委托方方法的这个操作交给处理程序的invoke方法去做 (此时这个处理程序的invoke方法更像我们代理类的一个代理)。

  大意:代理类调用委托方方法的这个操作是由InvocationHandler的invoke方法去完成的,不再相静态代理那样直接去调用委托方的方法了。

 InvocationHandler的invoke方法解释

  既然代理类都将调用委托方方法的这个重任都交到nvocationHandler的invoke方法上来了,那么我们肯定要知道这个invoke方法是怎么工作的,否则它不调用我们怎么办 (虽然这个我们管不到,”但盖楼的不是我们,因为我们要买、所以肯定也要看呀,吐糟一下“)

官方解释:

Object invoke(Object proxy,
              方法 method,
              Object[] args)
       throws Throwable
处理代理实例上的方法调用并返回结果。 当在与之关联的代理实例上调用方法时,将在调用处理程序中调用此方法。
参数
proxy - 调用该方法的代理实例
method -所述方法对应于调用代理实例上的接口方法的实例。 方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。
args -包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。 原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integerjava.lang.Boolean 。
结果
从代理实例上的方法调用返回的值。 如果接口方法的声明返回类型是原始类型,则此方法返回的值必须是对应的基本包装类的实例; 否则,它必须是可声明返回类型的类型。 如果此方法返回的值是null和接口方法的返回类型是基本类型,那么NullPointerException将由代理实例的方法调用抛出。 如上所述,如果此方法返回的值,否则不会与接口方法的声明的返回类型兼容,一个ClassCastException将代理实例的方法调用将抛出。
异常
Throwable - 从代理实例上的方法调用抛出的异常。 异常类型必须可以分配给接口方法的throws子句中声明的任何异常类型java.lang.RuntimeException检查的异常类型java.lang.RuntimeExceptionjava.lang.Error 。 如果检查的异常是由这种方法是不分配给任何的中声明的异常类型throws接口方法的子句,则一个UndeclaredThrowableException包含有由该方法抛出的异常将通过在方法调用抛出代理实例。

个人理解:

  1.处理代理实例上的方法调用并返回结果(好像没啥好解释的):就是代理实类用他的方法要调用委托方的方法,而这个invoke方法就是处理代理实例方法的方法。

  2.当在与之关联的代理实例上调用方法时,将在调用处理程序中调用此方法。:看明白了没,意思是:当在与这个invoke方法关联的代理实例上调用方法时(就是代理类要调用委托方方法时),这个时候会在处理程序中调用invoke方法。

  参数:

    1.proxy:代理对象

    2.method:委托类的委托方法

    3.args:代理对象的参数,如果无参则传null.

二:Proxy类:我们将会用此类的newProxyInstance方法去创建动态代理对象。

官方解释:

Proxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。

动态代理-LMLPHP 

loader - 类加载器来定义代理类

interfaces - 代理类实现的接口列表

h - 调度方法调用的调用处理函数   

 proxy类的newProxyInstance方法是创建一个动态代理对象:

参数loader:类加载器,作用:当虚拟机运行的时候需要字节码文件,这个类就是将编译器编译好的字节码class文件加载到虚拟机中去的。就是要把我们希望生成的代理类的接口加载器传进去,至于这个加载器是怎么运作的(我又不知道了,还好我们这一块儿只关心这个加载器的作用)。

参数interfaces:代理类的超类:接口

参数h:就是一个处理程序的接口,是要被代理类实现的哪个,上面有讲。

好了,上代码。

场景一:房屋出租:

1.出租接口:

package mr.li.inface;
/**
 * 场景一:租房接口(此接口必须有,需要委托方和代理方都实现只不过动态代理不用去实现,但用它生成代理模型)
 * @author yanLong.Li
 * @date 2019年3月16日 下午3:09:28
 */
public interface Host {

    /** 房屋出租  */
    void houseRental();
}

2.委托类:房东

package mr.li.host;

import mr.li.inface.Host;
/**
 * 委托类:房东
 * @author yanLong.Li
 * @date 2019年3月14日 下午5:05:14
 */
public class Landlord implements Host{

    @Override
    public void houseRental() {
        System.out.println("委托方(具体业务):出租一号大院,二号大院");
    }
}

3.生成代理类的方式:场景1和场景2公用的:

package mr.li.factory;

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

/**
 * 处理程序
 * @author yanLong.Li
 * @date 2019年3月14日 下午5:03:36
 */
public class MyInvocationHandler implements InvocationHandler{

    private Object target;

    public MyInvocationHandler() {}

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

   //参数:args是委托方的方法中的参数,如果委托方方法有参数在测试类中调用时就会拿到,这儿就会有。 @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
     if(args != null){
      System.out.println("args[0]是"+args[0])
      } System.out.println(
"代理方:让客户挑选产品"); Object result = method.invoke(target, args); System.out.println("代理方:中间抽成"); System.out.println("代理方:完成交易"); return result; } }

4.场景2:购买糖果接口

package mr.li.inface;
/**
 * 场景二:出售糖果接口(此接口必须有,需要委托方和代理方都实现只不过动态代理不用去实现,但用它生成代理模型)
 * @author yanLong.Li
 * @date 2019年3月16日 下午3:43:04
 */
public interface Candy {

    /** 出售糖果  */
    void sellingCandy();
}

5.委托类:糖果工厂

package mr.li.host;

import mr.li.inface.Candy;
/**
 * 委托类:百货商场
 * @author yanLong.Li
 * @date 2019年3月16日 下午3:49:38
 */
public class CandyFactory implements Candy{

    @Override
    public void sellingCandy() {
        System.out.println("出售:喜之郎果冻,大白兔奶糖,水果糖....");
    }
}

6.测试:

package mr.li.test;

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

import mr.li.factory.MyInvocationHandler;
import mr.li.host.CandyFactory;
import mr.li.host.Landlord;
import mr.li.inface.Candy;
import mr.li.inface.Host;
/**
 * 客户
 * @author yanLong.Li
 * @date 2019年3月14日 下午5:09:03
 */
public class Client {

    public static void main(String[] args) {
        System.out.println("场景一:我要租房子");
        Host host = new Landlord();
        InvocationHandler invocationHandler = new MyInvocationHandler(host);
        //动态生成动态代理
        Host hoshProxy = (Host)Proxy.newProxyInstance(host.getClass().getClassLoader(), host.getClass().getInterfaces(), invocationHandler);
        hoshProxy.houseRental();
        System.out.println("------------------------------------------------------------------");
        System.out.println("场景二:我要购买糖果");
        Candy candy = new CandyFactory();
        InvocationHandler invocationHandler2 = new MyInvocationHandler(candy);
        Candy candyProxy = (Candy)Proxy.newProxyInstance(candy.getClass().getClassLoader(), candy.getClass().getInterfaces(), invocationHandler2);
        candyProxy.sellingCandy();
    }
}

测试结果:

场景一:我要租房子
代理方:让客户挑选产品
委托方(具体业务):出租一号大院,二号大院
代理方:中间抽成
代理方:完成交易
------------------------------------------------------------------
场景二:我要购买糖果
代理方:让客户挑选产品
出售:喜之郎果冻,大白兔奶糖,水果糖....
代理方:中间抽成
代理方:完成交易

03-17 02:29