ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
#### **参考文章** [Android设计模式源码解析之Proxy模式](http://blog.csdn.net/singwhatiwanna/article/details/44590179) [《JAVA与模式》之代理模式](http://www.cnblogs.com/java-my-life/archive/2012/04/23/2466712.html) [【设计模式】代理模式](http://www.cnblogs.com/chenpi/p/5190397.html) [Java之代理](https://www.kancloud.cn/alex_wsc/java/461708) [Java之美[从菜鸟到高手演变]之设计模式二](http://blog.csdn.net/zhangerqing/article/details/8239539) [《Android源码设计模式解析与实战》读书笔记(十八)](http://blog.csdn.net/qq_17766199/article/details/50492805) [CGLIB(Code Generation Library)详解](http://blog.csdn.net/danchu/article/details/70238002) [Java动态代理分析](http://blog.csdn.net/danchu/article/details/70146985) #### **代理模式** **基本概念**: 代理模式定义:为其他对象提供一种代理以控制对这个对象的访问,代理对象起到中介作用,可去掉功能服务或增加额外的服务。代理模式又称为委托模式。 **代理模式的使用场景**:当无法或不想直接访问某个对象或访问某个对象存在困难时可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,委托对象与代理对象需要实现相同的接口。(**静态代理模式**) UML类图 ![](https://box.kancloud.cn/1aa3333f0ebe993a2b25ec3d6c808191_747x341.png) 代码如下所示 **抽象主题类** ~~~ public abstract class Subject { /** *一个普通的业务方法 / public abstract void visit(); } ~~~ 真正主题类 ~~~ public class RealSubject extends Subject { @Override public void visit() { // RealSubject 中visit 的具体逻辑 System .out.println("Real subject!”) ; } } ~~~ 代理类 ~~~ public class ProxySubject extends Subject { private RealSubject mSubject;//持有真实主题的引用 public ProxySubject(RealSubject mSubject) { this. mSubject = mSubject; } @Override public void visit() { //通过真实主题引用的对象调用真实主题中的逻辑方法 mSubject.visit(); } } ~~~ 客户端类 ~~~ public class Client { public static void main(String() args) { //构造一个真实主题对象 RealSubject real= new RealSubject() ; //通过真实主题对象构造一个代理对象 ProxySubject proxy= new ProxySubject(real); // 调用代理的相关方法 proxy.visit(); } } ~~~ **角色介绍** * **Subject :抽象主题类**。 该类的主要职责是**声明**真实主题与代理的**共同接口方法**,该类**既可以是一个抽象类也可以是一个接口**。 * **RealSubject : 真实主题类**。 该类也称为**被委托类**或**被代理类**,该类**定义了代理所表示的真实对象,由其执行具体的业务逻辑方法**,而客户类则通过代理类间接地调用真实主题类中定义的方法。 * **ProxySubject:代理类**。 该类也称为**委托类**或**代理类**,该类**持有一个对真实主题类的引用,在其所实现的接口方法中调用真实主题类中相应的接口方法执行,以此起到代理的作用。** * **Client:客户类**,即**使用代理类的类型**。 #### **代理模式的简单实现** 例子:小民以前在公司上班时,就遇到过被老板拖欠工资甚至克扣工资的情况,这种情况下小民还是通过法律途径来解决问题, 一旦小民选择了走法律途径解决该纠纷,那么不可避免地就需要请一个律师来作为自己的诉讼代理人,我们将诉讼的流程抽象在一个接口类中。以小民诉讼的流程举例。那么需要代理律师代理,诉讼简单流程:提交申请–>进行举证–>开始辩护–>诉讼完成。 **诉讼接口类**: ~~~ public interface ILawsuit { /** * 提交申请 */ void submit(); /** * 进行举证 */ void burden(); /** * 开始辩护 */ void defend(); /** * 诉讼完成 */ void finish(); } ~~~ 4 个方法非常简单,都是诉讼的一般流程。 **具体诉讼人小民**: ~~~ public class XiaoMin implements ILawsuit{ @Override public void submit() { //小民申请仲裁 System.out.println("老板年底拖欠工资,特此申请仲裁!"); } @Override public void burden() { //小民提交证据 System.out.println("这是合同书和过去一年的银行工资流水!"); } @Override public void defend() { //铁证如山 System.out.println("证据确凿,不需要再说什么!"); } @Override public void finish() { //结果 System.out.println("诉讼成功,判决老板即日起七天内结算工资!"); } } ~~~ 该类实现!Lawsuit 并对其中4 个方法作出具体的实现逻辑,逻辑很简单, 都只是输出一段话而己, 当然, 小民自己是不会去打官司的,于是小民请个律师代替自己进行诉讼。 **代理律师**: ~~~ public class Lawyer implements ILawsuit{ private ILawsuit mLawsuit; //持有一个具体被代理者的引用 public Lawyer(ILawsuit lawsuit) { this.mLawsuit = lawsuit; } @Override public void submit() { mLawsuit.submit(); } @Override public void burden() { mLawsuit.burden(); } @Override public void defend() { mLawsuit.defend(); } @Override public void finish() { mLawsuit.finish(); } } ~~~ 律师类表示代理者律师,在该类里面会持有一个被代理者( 这里也就是上面的XiaoMin 类)的引用, 律师所执行的方法实质就是简单地调用被代理者中的方法,下面来看看客户类中具体的调用执行关系。 **开始仲裁**: ~~~ public class Client { public static void main(String[] args) { //构造出诉讼人小民 ILawsuit xiaomin = new XiaoMin(); //构造一个代理律师,并将小民传递进去 ILawsuit lawyer = new Lawyer(xiaomin); //律师提交申请 lawyer.submit(); //律师进行举证 lawyer.burden(); //律师代小民辩护 lawyer.defend(); //完成诉讼 lawyer.finish(); } } ~~~ 结果: ~~~ 老板年底拖欠工资,特此申请仲裁! 这是合同书和过去一年的银行工资流水! 证据确凿,不需要再说什么! 诉讼成功,判决老板即日起七天内结算工资! ~~~ 其实代理模式也很简单,其主要还是一种**委托机制**,**真实对象将方法的执行委托给代理对象,而且委托得干净利落毫不做作**,这也是为什么代理模式也称为委托模式的原因,相信大家不难理解。除此之外,大家其实可以继续发散思维,**其实我们的代理类完全可以代理多个被代理类**,就像上面的例子一样, 一个律师可以代理多个人打官司,这是没有任何问题的,而具体到底是代理的哪个人,这就要看代理类中所持有的实际对象类型,上述的例子中实际对象类型是XiaoMin,也就是代理的XiaoMin。 同样我们也可以代理其他人,只需要实现ILawsuit即可。上面的代理模式也叫**静态代理**,也就是**在代码运行前代理类的class文件就已经存在**。那么相反,当然也会有动态代理,而**动态代理则与静态代理相反**,**通过反射机制动态地生成代理者的对象,也就是说我们在code 阶段压根就不需要知道代理谁,代理谁我们将会在执行阶段决定**。而Java 也给我们提供了一个便捷的动态代理接口lnvocationHandler,实现该接口需要重写其调用方法invoke。 ~~~ public class DynamicProxy implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {--- -- return null; } } ~~~ 在这里,我们**主要通过invoke 方法来调用具体的被代理方法,也就是真实的方法**。动态代理可以使我们的代码逻辑更简沽, 不过在这之前我们得首先完善动态代理类。 下面用动态代理实现上述例子: Java提供了一个便捷的动态代理接口InvocationHandler,我们来实现它: ~~~ public class DynamicPorxy implements InvocationHandler{ private Object obj; //被代理类的引用 public DynamicPorxy(Object obj) { this.obj = obj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 调用被代理类对象的方法 Object result = method.invoke(obj, args); return result; } } ~~~ 如上代码所述,我们**声明一个Object 的引用**,**该引用将指向被代理类**,而我们**调用被代理类的具体方法则在invoke 方法中执行**, 是不是很简洁?也就是说我们原来**由代理类所做的工作现在由InvocationHandler 来处理, 不再需要关心到底代理谁**。 修改后的Client类: ~~~ public class Client { public static void main(String[] args) { //构造出诉讼人小民 ILawsuit xiaomin = new XiaoMin(); //1.静态代理 //构造一个代理律师,并将小民传递进去 //ILawsuit lawyer = new Lawyer(xiaomin); //-------------------------------------- //2.动态代理 //构造一个动态代理 DynamicPorxy proxy = new DynamicPorxy(xiaomin); //获取被代理类小民的ClassLoader ClassLoader loader = xiaomin.getClass().getClassLoader(); //动态构造一个代理者律师 ILawsuit lawyer = (ILawsuit) Proxy.newProxyInstance(loader, new Class[]{ ILawsuit.class }, proxy); //律师提交申请 lawyer.submit(); //律师进行举证 lawyer.burden(); //律师代小民辩护 lawyer.defend(); //完成诉讼 lawyer.finish(); } } ~~~ 结果不变,由此可以看出**动态代理通过一个代理类来处理N多个被代理类,其实质是对代理者与被代理者解耦,使两者直接没有直接的耦合关系**。相对而言**静态代理则只能为给定接口下的实现类做代理,如果接口不同那么就需要重新定义不同的代理类,较为复杂,但是静态代理更符合面向对象原则。具体使用哪种方式,根据个人喜好。** **静态代理和动态代理是从code 方面来区分代理模式的两种方式**。 **从适用范围区分不同类型的代理实现**: ![](https://box.kancloud.cn/66f418a6c3cf4774833b216414e92baf_1159x416.jpg) * **远程代理(Remote Proxy)**:为某个对象在不同的内存地址空间提供局部代理。使系统可以将Server 部分的实现隐藏,以便Client 可以不必考虑Server 的存在。 为不同地理的对象,提供局域网代表对象。 * **虚拟代理(Virtual Proxy)**:使用一个代理对象表示一个十分耗资源的对象并在真正需要时才创建。 根据需要将资源消耗很大的对象进行延迟,真正需要时才进行创建。 * **保护代理(Protection Proxy)**:使用代理控制对原始对象的访问。该类型的代理常被用于原始对象有不同访问权限的情况。 * **智能引用(Smart Reference)**:在访问原始对象时执行一些自己的附加操作并对指向原始对象的引用计数。 提供对目标对象额外的服务,**实际开发中智能引用代理应用最广泛**。 这里要注意的是, [静态和动态代理](https://www.kancloud.cn/alex_wsc/java/490242)都可以应用于上述4 种情形,两者是各自独立的变化。 **静态代理**:代理和被代理对象在代理之前是确定的,他们都实现相同的接口或者继承相同的抽象类。 ![](https://box.kancloud.cn/7551ba09506305f5a4c03cf4d6411fd4_507x368.jpg) ### **动态代理**: * **背景**: 要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情!写成百上千个代理类,会造成类爆炸,类膨胀。 #### **JDK动态代理** ![](https://box.kancloud.cn/52623d989e97bba73030769efbba58ec_798x285.jpg) Java动态代理位于**java.lang.reflect**包下,一般主要涉及到以下各类: * **①、Interface InvocationHandler** **Object invoke(Object proxy, Method method, Object[] args)**,在实际使用中,一般第一个参数是指代理类,method是被代理的方法,args是该方法的参数数组,没有参数可以置空或者去掉;这个抽象方法在代理类中动态实现。 * **② class Proxy**:该类即为动态代理类 **Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)**:返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用代理类的在接口中声明过的方法) 动态代理实现步骤: 1. 创建一个实现接口InvocationHandler的类,它必须实现invoke方法 2. 创建被代理的类以及接口 3. 调用Proxy的静态方法,创建一个代理类 **object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)** **JDK动态代理和[CGLIB](https://github.com/cglib/cglib)动态代理的区别**: * **JDK动态代理** * 一、只能代理实现了接口的类 * 二、没有实现接口的类不能实现JDK的动态代理 * **CGLIB动态代理** 1. 针对类来实现代理的 2. 对指定目标类产生一个子类,通过方法拦截技术拦截所有父类方法的调用 **CGLIB是使用继承的方式,所以不能对final修饰的类进行代理** #### **CGLIB(Code Generation Library)动态代理** CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。 关于CGLB的使用可以参考以下文章: [cglib动态代理介绍(一)](http://blog.csdn.net/xiaohai0504/article/details/6832990) [CGLib动态代理原理及实现](http://blog.csdn.net/yakoo5/article/details/9099133) [CGLIB介绍与原理 ](http://blog.csdn.net/zghwaicsdn/article/details/50957474) [系统架构设计——设计模式之代理模式(二)CGLIB动态代理实现](http://blog.csdn.net/qq_25689397/article/details/51427164) [动态代理proxy与CGLib的区别](http://blog.csdn.net/hintcnuie/article/details/10954631) [AOP的底层实现-CGLIB动态代理和JDK动态代理](http://blog.csdn.net/dreamrealised/article/details/12885739) 使用CGLIB需要导入cglb的jar包[cglib-nodep-3.2.5.jar](https://github.com/cglib/cglib/releases/tag/RELEASE_3_2_5),目前是3.2.5版本 示例: **Train.class** ~~~ package com.cglib.proxy; public class Train { public void move() { System.out.println("火车行驶中........."); } } ~~~ **CglibProxy.class** ~~~ package com.cglib.proxy; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class CglibProxy implements MethodInterceptor { private Enhancer enhancer = new Enhancer();//创建代理类的属性 /** * 得到代理类 * @param clazz * @return */ public Object getProxy(Class clazz) { // 设置于创建子类的类,就是说为哪个类来产生代理类 enhancer.setSuperclass(clazz); enhancer.setCallback(this); return enhancer.create(); } /** * 拦截所有目标类方法的调用 * obj:目标类的实例 * method:目标方法的反射对象 * args:方法的参数 * proxy:代理类的实例 */ @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("日志开始......"); // 代理类调用父类的方法 proxy.invokeSuper(obj, args); System.out.println("日志结束......."); return null; } } ~~~ **Client.class** ~~~ package com.cglib.proxy; public class Client { public static void main(String[] args) { CglibProxy proxy = new CglibProxy(); Train t = (Train) proxy.getProxy(Train.class); t.move(); } } ~~~ 输出结果: ~~~ 日志开始...... 火车行驶中......... 日志结束....... ~~~ **JDK动态代理实现的思路**: **实现功能:通过Proxy的newProxyInstance返回代理对象** * 1、声明一段代码(动态产生代理) * 2、编译源码(JDK Compiler API),产生新的类(代理类) * 3、 将这个类load到内存当中,产生一个新的对象(代理对象) * 4、return 代理对象 **代理模式实际应用**: 1、调用jar包或者库library中的某一个方法,不能修改源码,所以可以采用代理模式机制在方法前后添加一些逻辑:日志处理、时间、其它业务逻辑。这种模式也叫AOP(面向切面编程)