最全介绍的代理模式---第二节-JDK 动态代理
Posted AnWen~
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最全介绍的代理模式---第二节-JDK 动态代理相关的知识,希望对你有一定的参考价值。
五、动态代理
优势:在静态代理中目标类很多时候,可以使用动态代理,避免静态代理的缺点:
动态代理中目标类即使有很多:
1)代理类数量可以很少
2)当你修改了接口中的方法时,不会影响代理类
1、介绍
在程序执行过程中,由JVM根据反射机制,创建代理类对象,并动态的指定要代理的目标类。换句话说,动态代理是一种创建Java对象的能力,让你不需要定义代理类的 .java源文件(如:Taobao类),就能创建代理类对象。
其实,就是jdk运行期间,动态创建class字节码并加载到JVM中
。
动态:在程序执行时,调用 JDK 提供的方法才能创建代理类的对象
【注】:
1)动态代理:使用反射机制,在程序执行中,创建代理类对象;代理的目标类是活动的,可设置的。如,设置不同的目标类,给不同的目标随时创建代理。
2)静态代理:代理类时手工实现的Java文件;代理的目标对象是规定的。
在Java中,要想创建对象:
1)创建类文件,JVM把文件编译为class
2)使用构造方法,创建类的对象
2、实现方法
(1)JDK动态代理:使用Java反射包中的类和接口实现动态代理的功能。
反射包:Java.lang.reflect,主要用到:InvocationHandler、Method、Proxy
(2)Cglib动态代理:Cglib是第三方的工具库,创建代理对象。
原理:继承。Cglib通过继承目标类,创建它的子类,在子类中重写父类中同名的方法,实现功能的修改。
因为Cglib是继承、重写方法,所以要求目标类不能是final的,方法也不能是final的。Cglib的要求目标类比较宽松,只要能继承就可以了。Cglib在很多框架中使用,比如,Mybatis、spring框架中都有使用。
Cglib(Code Generation Library)是一个开源项目,是一个强大的、高性能的、高质量的Code生产类库,它可以在运行期扩展Java类与实现Java接口,广泛被许多的AOP的框架中使用,如,Spring AOP。
使用 JDK 的 Proxy 实现代理,要求目标类与代理类实现相同的接口,若目标类不存在接口,则无法使用该方法实现。
对于无接口的类,要为其创建动态代理,就要使用Cglib来实现。Cglib 代理的生成原理是生成目标类的子类,而子类是增强的,这个子类对象就是代理对象。所以,使用 Cglib 生成动态代理,要求目标类必须能够被继承,即不能是final类。
3、JDK动态代理
反射包:Java.lang.reflect,主要用到:InvocationHandler、Method、Proxy
(1)InvocationHandler 接口----调用处理器【实际上表示你这代理到底要干什么!!】
事件处理,执行目标对象的方法时,会触发事件处理器方法,会把当前执行的目标对象方法作为参数传入。
接口中就一个方法 invoke(),表示代理对象要执行的功能代码,换句话说,代理类要实现的功能(下面列举的两种功能)放在invoke() 中
- 代理类完成的功能:
调用目标方法,执行目标方法的功能;
功能增强,在目标方法调用时,增强功能。
- 代码:
public interface InvocationHandler
// proxy JDK创建的代理对象,无需赋值,不需要你管理,没什么用
// method 目标对象中的方法,JDK提供Method对象,就是要调用目标对象的某个方法的Method对象
// args 调用目标对象某个方法时接受的参数,JDK提供的,不需要认为参与
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
- 怎么用:
创建类,实现InvocationHandler接口
重写invoke(),吧原来静态代理中的代理类要完成的功能写在这里
(2)Method 类------表示方法的,确切地说是目标类中的方法
作用:通过Method可以执行某个方法类的方法:Method.invoke()
【注】这里的invoke与上面提到的invoke 完全不一样,只是名字碰巧一样。
- 上面提及的invoke:接口中的方法;
- 这里提及的invoke:类中的方法。
在动态代理中的使用方法:method.invoke(目标对象,方法的参数args);
【注】可以执行任一对象的方法,而不需要知道这个方法的名称
说明:method.invoke() 就是用来执行目标方法的,等同于静态代理中的
float price = factory.sell(amount);
(3)Proxy 类:核心的对象,创建代理对象。之前创建对象都是new类的构造方法()。现在我们是使用proxy类的方法,代替new的使用。
-
方法:静态方法
newProxyInstance()
-
作用是:创建代理对象,等同于静态代理中的
TaoBao taoBao=new TaoBao()
-
代码:
public class Proxy implements java.io.Serializable
@CallerSensitive
// 这里会用到反射机制
// ClassLoader loader 当前目标对象使用的类加载器,获取加载器的方固定:target.getClass().getClassLoader()
// Class<?>[] interfaces 目标对象要实现的接口,用泛型确认类型:target.getClass().getInterfaces()。实际上,这个参数给代理对象提供了一组接口,这个代理对象会实现这些接口。
// InvocationHandler h 我们自己写的,代理类要完成的功能
// 返回对象 ---> 代理对象
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
....此处省略
(4)实现动态代理的步骤:
创建接口,定义目标类要完成的功能;
创建目标类实现接口;
创建InvocationHandler接口的实现类,在invoke方法中完成代理类的功能:
- 调用目标方法
- 增强功能
使用Proxy类的静态方法newProxyInstance(),创建代理对象,并把返回值转为接口类型。
(5)UML类图:
(6)代码:
***UsbSell接口
// 表示功能的厂家,或者商家要完成的功能
public interface UsbSell
// 定义方法
// amount: 表示一次购买的数量,暂时不用
// 返回表示一个U盘的价格
float sell(int amount);
// 可以定义多个其他的方法
***UsbKingFactory类
// 目标类:金士顿厂家,不接用户的单独购买
public class UsbKingFactory implements UsbSell
@Override
public float sell(int amount)
// 一个128G的U盘85元
// 后期可以根据amount,可以实现不同的价格,比如1000个,单价85元,
System.out.println("目标类中,执行sell目标方法");
return 85.0f;
***MySellHandler类
// 必须实现InvocationHandler,完成代理类需要完成的功能(1、待用目标方法;2、功能增强)
public class MySellHandler implements InvocationHandler
private Object target = null;
// 动态代理:目标对象是活动的,不是固定的,需要传入进来
// 传入是谁,就给谁创建代理对象
public MySellHandler(Object target)
this.target = target;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
// 向厂家发送订单,告诉厂家,我买了U盘,厂家发货
//float price = factory.sell(amount);//厂家的价格
Object res = method.invoke(target,args);
// 商家 需要加价,也就是dialing要增加价格
// 这里相对于目标对象,就增强了功能
// 代理类 在完成目标类方法调用后,增强了功能
//price += 25;
if(res!=null)
Float price = (Float) res;//强转
price += 25;
res = price;
// 在目标类的方法调用后。你做的其他功能,都是增强功能,如发放优惠券
System.out.println("淘宝商家,给你返回一个优惠券,或者红包");
return res;
***MainShop(客户端)
public class MainShop
public static void main(String[] args)
//1.创建对象,使用Proxy
//2.创建目标对象
UsbSell factory = new UsbKingFactory();
//3.创建Invocationhandler对象
InvocationHandler myHandle = new MySellHandler(factory);
//4.创建代理对象
UsbSell proxy = (UsbSell) Proxy.newProxyInstance
(factory.getClass().getClassLoader(), // 通过反射得到目标对象的类加载器
factory.getClass().getInterfaces(), // 通过反射得到目标对象的接口
myHandle); // 执行功能的Handle
// 这里 proxy.getclass().getName() = com.sun.proxy.$Proxy0 ,是可以转为目标对象所实现的所有接口
System.out.println(proxy.getClass().getName());
// factory.getClass().getInterfaces() 这个参数就说明了 目标对象 UsbKingFactory 有哪些,在创建代理对象的时候,会让代理对象自动实现这些接口,同时这个代理对象本身也实现Proxy
// 只是这里进行了强转,转为 目标对象 UsbKingFactory 实现的接口 UsbSell
//通过代理执行方法
float price = proxy.sell(1);
// 在执行这个 代理对象 执行 目标方法的时候,实际上用的是 myHandle 中的 invoke()。
// sell 方法 赋给 myHandle 中的 invoke 的 参数 method,参数 是 1
System.out.println("通过动态代理对象,调用方法:" +price);
(7)JDK动态代理执行流程
最后,要知道动态代理是做什么的?
可以在不改变原来目标方法功能的前提下,可以在代理中增强自己的功能代码
比如,在项目开发中,有一个功能是其他人(其他部门,其他小组的人)写好的,就可以使用动态代理,在 别人的基础上在自己的代理中增强功能。
以上是关于最全介绍的代理模式---第二节-JDK 动态代理的主要内容,如果未能解决你的问题,请参考以下文章
设计模式 结构型模式 -- 代理模式(动态代理(JDK动态代理(JDK动态代理要求必须定义接口,对接口进行代理。)动态代理原理(使用arthas-boot.jar查看代理类的结构)动态代理的作用)(代