动态代理:没有具体类型(只有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.Integer
或java.lang.Boolean
。- 结果
- 从代理实例上的方法调用返回的值。 如果接口方法的声明返回类型是原始类型,则此方法返回的值必须是对应的基本包装类的实例; 否则,它必须是可声明返回类型的类型。 如果此方法返回的值是
null
和接口方法的返回类型是基本类型,那么NullPointerException
将由代理实例的方法调用抛出。 如上所述,如果此方法返回的值,否则不会与接口方法的声明的返回类型兼容,一个ClassCastException
将代理实例的方法调用将抛出。 - 异常
Throwable
- 从代理实例上的方法调用抛出的异常。 异常类型必须可以分配给接口方法的throws
子句中声明的任何异常类型java.lang.RuntimeException
检查的异常类型java.lang.RuntimeException
或java.lang.Error
。 如果检查的异常是由这种方法是不分配给任何的中声明的异常类型throws
接口方法的子句,则一个UndeclaredThrowableException
包含有由该方法抛出的异常将通过在方法调用抛出代理实例。
个人理解:
1.处理代理实例上的方法调用并返回结果(好像没啥好解释的):就是代理实类用他的方法要调用委托方的方法,而这个invoke方法就是处理代理实例方法的方法。
2.当在与之关联的代理实例上调用方法时,将在调用处理程序中调用此方法。:看明白了没,意思是:当在与这个invoke方法关联的代理实例上调用方法时(就是代理类要调用委托方方法时),这个时候会在处理程序中调用invoke方法。
参数:
1.proxy:代理对象
2.method:委托类的委托方法
3.args:代理对象的参数,如果无参则传null.
二:Proxy类:我们将会用此类的newProxyInstance方法去创建动态代理对象。
官方解释:
- java.lang.Object
- java.lang.reflect.Proxy
Proxy
提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。
loader
- 类加载器来定义代理类
interfaces
- 代理类实现的接口列表
h
- 调度方法调用的调用处理函数
proxy类的newProxyInstance方法是创建一个动态代理对象:
参数
:类加载器,作用:当虚拟机运行的时候需要字节码文件,这个类就是将编译器编译好的字节码class文件加载到虚拟机中去的。就是要把我们希望生成的代理类的接口加载器传进去,至于这个加载器是怎么运作的(我又不知道了,还好我们这一块儿只关心这个加载器的作用)。loader
参数
:代理类的超类:接口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(); } }
测试结果:
场景一:我要租房子
代理方:让客户挑选产品
委托方(具体业务):出租一号大院,二号大院
代理方:中间抽成
代理方:完成交易
------------------------------------------------------------------
场景二:我要购买糖果
代理方:让客户挑选产品
出售:喜之郎果冻,大白兔奶糖,水果糖....
代理方:中间抽成
代理方:完成交易