热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【设计模式】一文学透代理模式

文章目录1、前言1.1、定义1.2、代理模式的实现1.3、代理模式的应场景1.4、代理模式的分类:2、静态代理1.)首先新建一个买车的接口2.ÿ


文章目录

  • 1、前言
    • 1.1、定义
    • 1.2、代理模式的实现
    • 1.3、代理模式的应场景
    • 1.4、代理模式的分类:
    • 2、静态代理
      • 1.)首先新建一个买车的接口
      • 2.)声明一个要买车的客户,实现买车接口
      • 3.)声明一个买车代理汽车4S店,同样也实现买车接口,必须接受客户下单
      • 4.) 创建一个客户端,模拟一次买车
      • 5.)通过代理模式实现权限控制
    • 3、动态代理机制:
      • 3.1、 JDK代理——接口级别代理
      • 3.2、 CGLib代理——方法级别代理
      • 代理模式的好处:
  • 总结




1、前言

Spring 的AOP 面向切面编程,是通过动态代理实现的, 由两部分组成:(a) 如果有接口的话 通过 JDK 接口级别的代理 (b) 如果没有接口的话,通过方法级别的代理 CGLib代理实现。


1.1、定义

什么是代理模式?

      代理模式就是多一个代理类出来,代替原对象进行一些操作。

      代理模式的定义:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。举例说明,租房的中介、打官司的律师、旅行社,他们可以代替我们做一些事情,这就是代理 一个人或者一个机构代表另一个人或者另一个机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之前起到中介的作用。

      我们想对外开放某些功能,就可以将这些功能在代理类中被引用,如此一来,屏蔽了我们不想外露的功能,只将我们想开放的功能开放出来。亦即委托类中其实是可以有很多方法的,很多功能的,我们可以酌情对外开放,代理类犹如一道大门,将委托类与外部调用者隔绝开来,只将部分功能赋予这个大门,来代替委托类行使这个功能,哪怕最终还是要牵扯到自身(因为最终还是要调用委托类的对应方法实现)。


1.2、代理模式的实现

代理模式很简单,只要记住以下关键点,简单易实现:

(1)代理类与委托类实现同一接口

(2)在委托类中实现功能,在代理类的方法中中引用委托类的同名方法

(3)外部类调用委托类某个方法时,直接以接口指向代理类的实例,这正是代理的意义所在:屏蔽。


1.3、代理模式的应场景

如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法:


  1. 修改原有的方法来做到改进。但这样违反了“对扩展开放,对修改关闭”的原则。
  2. 采用一个代理类调用原有的方法,且对产生的结果进行控制。这就是代理模式。

      1)当我们想要隐藏某个类时,可以为其提供代理类

      2)当一个类需要对不同的调用者提供不同的调用权限时,可以使用代理类来实现(代理类不一定只有一个,我们可以建立多个代理类来实现,也可以在一个代理类中金进行权限判断来进行不同权限的功能调用)

      3)当我们要扩展某个类的某个功能时,可以使用代理模式,在代理类中进行简单扩展(只针对简单扩展,可在引用委托类的语句之前与之后进行)

      代理模式虽然实现了调用者与委托类之间的强耦合,但是却增加了代理类与委托类之间的强耦合(在代理类中显式调用委托类的方法),而且增加代理类之后明显会增加处理时间,拖慢处理时间。


1.4、代理模式的分类:

      静态代理:由程序员创建代理类或特定工具自动生成源代码再对其编译。再程序运行前代理类的.class文件就已经存在了。

      动态代理:在程序运行时用反射机制,动态创建代理类。动态代理有两种-JDK代理——接口级别代理CGLib代理——方法级别代理


2、静态代理

      所谓静态就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。

      通过上面的代理模式描述我们可以知道,其目的就是为了控制对象引用,生活场景中我们以买车为例,如果我们要买一辆轿车必须通过汽车4S店,汽车4s店就是充当代理角色,其目的就是控制买车客户的买车行为,必须通过汽车4S店才能从汽车厂商买一辆车。
在这里插入图片描述


1.)首先新建一个买车的接口

public interface IBuyCar {//买车void buyCar();
}

2.)声明一个要买车的客户,实现买车接口

public class Customer implements IBuyCar {private int cash;//购车款public int getCash() {return cash;}public void setCash(int cash) {this.cash = cash;}@Overridepublic void buyCar() {Log.e("buyCar", "买一辆车花费了-->" + cash + "元");}
}

3.)声明一个买车代理汽车4S店,同样也实现买车接口,必须接受客户下单

public class BuyCarProxy implements IBuyCar{private Customer customer;//接收买车客户public BuyCarProxy(Customer customer){this.customer=customer;//接收买车客户}@Overridepublic void buyCar() {//实现为客户买车customer.buyCar();}
}

4.) 创建一个客户端,模拟一次买车

Customer customer=new Customer();customer.setCash(120000);BuyCarProxy buyCarProxy=new BuyCarProxy(customer);buyCarProxy.buyCar();

在这里插入图片描述
代理类和委托类要实现相同的接口,因为代理真正调用的还是委托类的方法。


5.)通过代理模式实现权限控制

      通过上面的例子,我们可能有个疑问,难道就不能直接去厂家买车吗?当然可以,如果在使用场景中实现类能满足要求时,我们当然可以直接实现类,但当实现类不能满足要求,要扩展需求,根据开闭原则你又不能修改实现类代码,这时你就用代理类。比如购买一辆车我们要对客户进行一个购车款审核,如果符合条件就买车,不符合要求我们就告知客户购车款不足。

&#64;Overridepublic void buyCar() {//实现为客户买车int cash&#61;customer.getCash();if(cash<100000){Log.e("buyCar","你的钱不够买一辆车");return;}customer.buyCar();}

实现场景

Customer customer&#61;new Customer();
customer.setCash(120000);
BuyCarProxy buyCarProxy&#61;new BuyCarProxy(customer);
buyCarProxy.buyCar();Customer customer1 &#61;new Customer();
customer1.setCash(90000);
BuyCarProxy buyCarProxy1 &#61;new BuyCarProxy(customer1);
buyCarProxy1.buyCar();

在这里插入图片描述
静态代理优点&#xff1a;
客户端不必知道实现类&#xff08;委托类&#xff09;如何如何&#xff0c;只需要调用代理类即可。
缺点&#xff1a;


  • 代理类和委托类实现了相同的接口&#xff0c;代理类通过委托类实现了相同的方法。但这样出现了大量的代码重复。如果接口增加一个方法&#xff0c;除了所有实现类需要实现这个方法外&#xff0c;所有代理类也要实现这个方法。这显然增加了代码的复杂度。
  • 代理对象只服务于一种类型的对象&#xff0c;如果要服务多类型的对象&#xff0c;那就要对每种对象都进行代理。静态代理子啊程序规模稍大是就无法胜任了。

3、动态代理机制&#xff1a;

      以上讲的都是代理模式的静态实现&#xff0c;所谓静态代理就是自己要为要代理的类写一个代理类&#xff0c;或者用工具为其生成的代理类&#xff0c;总之&#xff0c;就是程序运行前就已经存在的编译好的代理类&#xff0c;这样有时候会觉得非常麻烦&#xff0c;也导致非常的不灵活&#xff0c;相比静态代理&#xff0c;动态代理具有更强的灵活性&#xff0c;因为它不用在我们设计实现的时候就指定某一个代理类来代理哪一个被代理对象&#xff0c;我们可以把这种指定延迟到程序运行时由JVM来实现。


3.1、 JDK代理——接口级别代理

package Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//Created by zhengbinMac on 2017/2/19.
public class DynamicProxy implements InvocationHandler{private Object target;public DynamicProxy(Object target) { this.target &#61; target; }&#64;SuppressWarnings("unchecked")public <T> T getProxy() {return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {sayBefore();Object result &#61; method.invoke(target, args);sayAfter();return result;}private void sayBefore() { System.out.println("before..."); }private void sayAfter() { System.out.println("after..."); }
}

JDK 提供的 Proxy 类的工厂方法 newProxyInstance 去动态地创建一个 Hello 接口的代理类。
Proxy.newProxyInstance&#xff1a;
参数&#xff1a;


  • loader - 定义代理类的类加载器
  • interfaces - 代理类要实现的接口列表
  • h - 指派方法调用的调用处理程序&#xff08;每个代理实例都具有一个关联的调用处理程序&#xff0c;调用代理实例的方法时&#xff0c;将对方法的调用指派到它的调用处理程序的 invoke 方法&#xff09;

返回&#xff1a;
一个带有代理类的指定调用处理程序的代理实例&#xff0c;它由指定类加载器定义&#xff0c;并实现指定的接口。

package Proxy;
//Created by zhengbinMac on 2017/2/19.
public class Test {public static void main(String[] args) {DynamicProxy dynamicProxy &#61; new DynamicProxy(new HelloImpl());Hello hello &#61; dynamicProxy.getProxy();hello.sayHello("JDK");}
}

再举例&#xff1a;还是接着上面的例子
1.&#xff09;首先我们要声明一个动态代理类&#xff0c;实现InvocationHandler接口

public class DynamicProxy implements InvocationHandler {// 被代理类的实例Object obj;// 将被代理者的实例传进动态代理类的构造函数中public DynamicProxy(Object obj) {this.obj &#61; obj;}/*** 覆盖InvocationHandler接口中的invoke()方法* 更重要的是&#xff0c;动态代理模式可以使得我们在不改变原来已有的代码结构* 的情况下&#xff0c;对原来的“真实方法”进行扩展、增强其功能&#xff0c;并且可以达到* 控制被代理对象的行为&#xff0c;下面的before、after就是我们可以进行特殊* 代码切入的扩展点了。*/public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {/** before &#xff1a;doSomething();*/Object result &#61; method.invoke(this.obj, args);/** after : doSomething();*/return result;}
}

2.&#xff09;具体实现

//我们要代理的真实对象Customer customer &#61; new Customer();//我们要代理哪个真实对象&#xff0c;就将该对象传进去&#xff0c;最后是通过该真实对象来调用其方法的InvocationHandler handler &#61; new DynamicProxy(customer);/** 通过Proxy的newProxyInstance方法来创建我们的代理对象&#xff0c;我们来看看其三个参数* 第一个参数 handler.getClass().getClassLoader() &#xff0c;我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象* 第二个参数customer.getClass().getInterfaces()&#xff0c;我们这里为代理对象提供的接口是真实对象所实行的接口&#xff0c;表示我要代理的是该真实对象&#xff0c;这样我就能调用这组接口中的方法了* 第三个参数handler&#xff0c; 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上*/IBuyCar buyCar &#61; (IBuyCar) Proxy.newProxyInstance(handler.getClass().getClassLoader(), customer.getClass().getInterfaces(), handler);buyCar.buyCar();

动态态代理相比静态代理&#xff0c;接口变了&#xff0c;动态代理类不需要改变&#xff0c;而静态代理类不仅需要改变实现类&#xff0c;代理类也需要修改。

但如果要代理一个没有接口的类&#xff0c;JDK 动态代理就用不上了&#xff0c;这就引出了 CGLib 代理。


3.2、 CGLib代理——方法级别代理

Spring、Hibernate 框架都是用了它&#xff0c;当然&#xff0c;spring两个都用到了 (a) 用到了JDK的 接口动态代理 (b) 也用到了 CGLib代理&#xff0c;它是一个在运行期间动态生成字节码的工具&#xff0c;也就是动态生成代理类。

package Proxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
//Created by zhengbinMac on 2017/2/19.
public class CGLibProxy implements MethodInterceptor {// 单例模式private static CGLibProxy instance &#61; new CGLibProxy();private CGLibProxy() {}public static CGLibProxy getInstance () { return instance; }public <T> T getProxy(Class<T> cls) {//设置要代理的类return (T) Enhancer.create(cls, this);}public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {sayBefore();Object result &#61; methodProxy.invokeSuper(obj, objects);sayAfter();return result;}private void sayBefore() { System.out.println("before..."); }private void sayAfter() { System.out.println("after..."); }
}

测试类

package Proxy;
//Created by zhengbinMac on 2017/2/19.
public class Test {public static void main(String[] args) {//CGlib要代理HelloImpl类Hello helloCGLib &#61; CGLibProxy.getInstance().getProxy(HelloImpl.class);helloCGLib.sayHello("CGLib");}
}

3.&#xff09;动态代理好处
使用Java动态代理机制的好处&#xff1a;

1、减少编程的工作量&#xff1a;假如需要实现多种代理处理逻辑&#xff0c;只要写多个代理处理器就可以了&#xff0c;无需每种方式都写一个代理类。

2、系统扩展性和维护性增强&#xff0c;程序修改起来也方便多了(一般只要改代理处理器类就行了)。


代理模式的好处&#xff1a;


  • 职责清晰真实的角色就是实现实际的业务逻辑&#xff0c;不用关心其他非本职责的事务&#xff0c;通过后期的代理完成一件完成事务&#xff0c;附带的结果就是编程简洁清晰。
  • 代理对象可以在客户端和目标对象之间起到中介的作用&#xff0c;这样起到了中介的作用和保护了目标对象的作用。
  • 高扩展性
    在这里插入图片描述

总结

Spring提供的实现动态代理有两种方式,一个是被代理对象需要实现JDK提供的动态代理接口。通过cglib的jar包实现动态代理,该方法只需要对目标对象继承即可。
spring支持两种方法,那么我们在使用spring进行动态代理时究竟使用的哪一种方法呢&#xff1f;spring优先支持实现接口的方式,如果没有接口则使用cglib方式。

原文地址&#xff1a;https://www.cnblogs.com/aspirant/p/7081738.html


推荐阅读
  • 初识java关于JDK、JRE、JVM 了解一下 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 纠正网上的错误:自定义一个类叫java.lang.System/String的方法
    本文纠正了网上关于自定义一个类叫java.lang.System/String的错误答案,并详细解释了为什么这种方法是错误的。作者指出,虽然双亲委托机制确实可以阻止自定义的System类被加载,但通过自定义一个特殊的类加载器,可以绕过双亲委托机制,达到自定义System类的目的。作者呼吁读者对网上的内容持怀疑态度,并带着问题来阅读文章。 ... [详细]
  • 开发笔记:Python之路第一篇:初识Python
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Python之路第一篇:初识Python相关的知识,希望对你有一定的参考价值。Python简介& ... [详细]
  • 精讲代理设计模式
    代理设计模式为其他对象提供一种代理以控制对这个对象的访问。代理模式实现原理代理模式主要包含三个角色,即抽象主题角色(Subject)、委托类角色(被代理角色ÿ ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文整理了Java面试中常见的问题及相关概念的解析,包括HashMap中为什么重写equals还要重写hashcode、map的分类和常见情况、final关键字的用法、Synchronized和lock的区别、volatile的介绍、Syncronized锁的作用、构造函数和构造函数重载的概念、方法覆盖和方法重载的区别、反射获取和设置对象私有字段的值的方法、通过反射创建对象的方式以及内部类的详解。 ... [详细]
  • 一次上线事故,30岁+的程序员踩坑经验之谈
    本文主要介绍了一位30岁+的程序员在一次上线事故中踩坑的经验之谈。文章提到了在双十一活动期间,作为一个在线医疗项目,他们进行了优惠折扣活动的升级改造。然而,在上线前的最后一天,由于大量数据请求,导致部分接口出现问题。作者通过部署两台opentsdb来解决问题,但读数据的opentsdb仍然经常假死。作者只能查询最近24小时的数据。这次事故给他带来了很多教训和经验。 ... [详细]
  • 生产环境下JVM调优参数的设置实例
     正文前先来一波福利推荐: 福利一:百万年薪架构师视频,该视频可以学到很多东西,是本人花钱买的VIP课程,学习消化了一年,为了支持一下女朋友公众号也方便大家学习,共享给大家。福利二 ... [详细]
  • Android系统启动过程分析一、Android平台架构首先贴一张Android系统架构图方便理解整个Android架构,这可以让我们从整体上对整个启动流程有个大概认知。可以看出整 ... [详细]
  • 多线程补充(一)JVM内存结构 VS Java内存模型 VS Java对象模型
    一:Java内存结构参考:https:www.zhihu.comquestion64586462answer576543433内存结构࿱ ... [详细]
  • Windows简单部署Exceptionless
    部署准备Elasticsearch、Exceptionless.API、Exceptionless.UI、URLRewrite、.NET运行时 1、安装ElasticSearch1 ... [详细]
  • java中的try catch_Java中的trycatchfinally异常处理
    Java中的try-catch-finally异常处理一、异常处理异常(Exception):是在运行发生的不正常情况。原始异常处理:if(条件){处理办法1处理办法 ... [详细]
author-avatar
萱恭俊逸明靖
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有