Java设计模式之代理模式

本文由老王出租房子引出——代理设计模式,将从最简单的静态代理实现开始,后延伸使用jdk实现动态代理,最后扩展到Cglib实现动态代理。为了更深入理解代理模式,我们会对实际应用中的典型案例进行介绍,包括在Spring和Mybatis中的应用。

读者可以拉取完整代码到本地进行学习,实现代码均测试通过后上传到码云

一、引出问题

上篇文章老王和小王组装电脑虽然完美结束了,但是老王和小王的争吵却并没有结束。老王决定将小王扫地出门,并把小王住的房子出租,租金用来弥补游戏本的花销。

老王花费很大的功夫,搞清楚了各种租房平台的规则并发布了房源信息,接着邻居提醒他:房子租出去并不代表躺着收租金了,有一天租客提出一些额外的要求,在合同允许的范围内,你也要尽量满足他们(为了便于理解,现实当然不存在啦),房子租出去后物业有问题你还要和物业协调。

老王开始思考,如果我直租给租客,会面临两个问题:

①我需要了解租房的全过程,我自己的事和租房的事严重的耦合了。

②租客提出的一些要求我不得不介入到其中,我不得不改变我自己的行程安排。

我应该想到一种办法,

第一点,将业务和功能解耦,业务层专注业务,比如网络接口请求,业务层只需要知道该调哪个接口请求方法,而不需要知道这个接口请求是如何发起网络请求的。

第二点,创建一个切面,在这个切面中增加一些通用的附加操作,比如注解解析,日志上报等,避免这些通用操作在每个接口方法都要写一遍。

老王灵感一闪:我可以给我的房子找一个一个代理,以控制对这个房子的管理。即通过代理管理房子.这样做的好处是:可以在目标实现的基础上,增强额外的功能操作,即扩展目标的功能。

这实际上就是静态代理。

二、静态代理

代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。

也即在静态代理中应该有三个角色:

①代理对象,消费端通过它来访问实际的对象(中介)

②实际被代理的对象(老王房子)

③一组可以被代理的行为的集合,通常是一个接口(老王和中介之间的约定事件)

老王与中介的约定接口:

/**
 * 代理行为的集合(接口)
 * @author tcy
 * @Date 02-08-2022
 */
public interface HostAgreement {

    // 房子出租
    void rent();
}

实际对象类(老王):

/**
 * 目标对象
 * @author tcy
 * @Date 02-08-2022
 */
public class Host implements HostAgreement {

    /**
     * 目标对象的原始方法
     */
    @Override
    public void rent() {
        System.out.println(" 这个房子要出租...");
    }
}

代理类(中介):

/**
 * 实际对象的代理
 * @author tcy
 * @Date 02-08-2022
 */
public class HostProxy implements HostAgreement {

    // 目标对象,通过接口来聚合
    private HostAgreement target;

    //构造器
    public HostProxy(HostAgreement target) {
        this.target = target;
    }

    @Override
    public void rent() {
        System.out.println("房子出租前,装修一下....");
        target.rent();
        System.out.println("房子出租后,与物业协调....");//方法
    }

}

客户类:

/**
 * @author tcy
 * @Date 02-08-2022
 */
public class Client {

    public static void main(String[] args) {
        //创建目标对象(被代理对象)
        Host hostTarget = new Host();
        //创建代理对象, 同时将被代理对象传递给代理对象
        HostProxy hostProxy = new HostProxy(hostTarget);
        //通过代理对象,调用到被代理对象的方法
        hostProxy.rent();
    }

}

这样就很好的解决了老王想到的问题,老王心满意足的看着自己的成果。

但中介看着老王的方案开始小声的嘀咕了,我一个人管那么多的房子,每一个房东都让我实现一个代理类,那我就会有很多的代理类,这是个问题呀!还有就是,有一天协议变动了,我们俩都要做许多工作。

最好是不要让我实现我们之间的协议(接口)了。

老王开始改造他的方案了。

三、动态代理Jdk

老王突然想到,使用jdk的动态代理可以很好的解决这个问题。

Jdk代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象动态代理,也叫做:JDK代理、接口代理。

我们对代码进行改造。

目标对象和目标对象的协议保持不变,我们需要修改也就是中介(代理类)的代码。

代理类:

/**
 * 代理类
 * @author tcy
 * @Date 02-08-2022
 */
public class HostProxy {

    //维护一个目标对象 , Object
    private Object target;

    //构造器 , 对target 进行初始化
    public HostProxy(Object target) {

        this.target = target;
    }

    //给目标对象 生成一个代理对象
    public Object getProxyInstance() {

        //说明
      /*
       *  //1. ClassLoader loader : 指定当前目标对象使用的类加载器, 获取加载器的方法固定
            //2. Class<?>[] interfaces: 目标对象实现的接口类型,使用泛型方法确认类型
            //3. InvocationHandler h : 事情处理,执行目标对象的方法时,会触发事情处理器方法, 会把当前执行的目标对象方法作为参数传入
       */
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {

                    /**
                     * 该方法会调用目标对象的方法
                     * @param proxy
                     * @param method
                     * @param args
                     * @return
                     * @throws Throwable
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("房子出租前,装修一下....");
                        //反射机制调用目标对象的方法
                        Object returnVal = method.invoke(target, args);
                        System.out.println("房子出租后,与物业协调....");
                        return returnVal;
                    }
                });
    }

}

客户端:

/**
 * @author tcy
 * @Date 02-08-2022
 */
public class Client {

    public static void main(String[] args) {

        //创建目标对象
        HostAgreement hostAgreement = new Host();
        //给目标对象,创建代理对象, 可以转成 ITeacherDao
        HostAgreement hostProxy = (HostAgreement)new HostProxy(hostAgreement).getProxyInstance();
        // proxyInstance=class com.sun.proxy.$Proxy0 内存中动态生成了代理对象
        //通过代理对象,调用目标对象的方法
        hostProxy.rent();
    }

}

这样就很好的解决了中介实现协议(接口)的问题,无论房子怎么变化,中介都能很完美的实现代理。

中介想让老王给他讲讲,Proxy.newProxyInstance()怎么就能完美的解决这个问题了。

老王撸起袖子开始给他讲实现原理。

在我们用Proxy.newProxyInstance实现动态代理的时候,有三个参数,第一个便是classloader。

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)

在我们的代码中classloader就是目标对象的类加载器。

第二个参数是 目标对象实现的接口类型,使用泛型方法确认类型,我们使用java的反射target.getClass().getInterfaces()获取到了接口类型。

第三个参数是实现InvocationHandler接口,并实现它唯一的方法invoke(),invoke其实就会执行我们的目标方法,我们就可以在invoke前后去做一些事。比如,房子出租前,装修一下或者房子出租后,与物业协调。

中介听完心满意足的离开了,老王总觉得哪里不对,中介都从协议中抽出来了,那我为什么还要被协议约束着呢?我何不也从协议(接口)中抽离出来。

我们查阅书籍觉得Cglib或许能帮到他。

四、动态代理Cglib

1、概念及实现

CGLIB是一个强大的、高性能的代码生成库。采用非常底层的字节码技术,对指定目标类生成一个子类,并对子类进行增强,其被广泛应用于AOP框架(Spring、dynaop)中,用以提供方法拦截操作。

CGLIB代理主要通过对字节码的操作,为对象引入间接级别,以控制对象的访问。我们知道Java中有一个动态代理也是做这个事情的,那我们为什么不直接使用Java动态代理,而要使用CGLIB呢?答案是CGLIB相比于JDK动态代理更加强大,JDK动态代理虽然简单易用,但是其有一个致命缺陷是,只能对接口进行代理。如果要代理的类为一个普通类、没有接口,那么Java动态代理就没法使用了。

老王觉得这些概念都不说人话,不如老王直接着手改造项目。

CGLIB是一个第三方的类库,首先需要引入依赖。

<dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>2.2.2</version>
</dependency>

现在只需要两个角色即可,代理类和目标类。

目标类:

/**
 * @author tcy
 * @Date 02-08-2022
 */
public class Host {
    /**
     * 租房方法
     */
    public void rent() {

        System.out.println("这个房子要出租...");

    }
}

代理类:

/**
 * 代理类
 * @author tcy
 * @Date 02-08-2022
 */
public class HostProxy implements MethodInterceptor {

    //维护一个目标对象
    private Object target;

    //构造器,传入一个被代理的对象
    public HostProxy(Object target) {
        this.target = target;
    }

    //返回一个代理对象:  是 target 对象的代理对象
    public Object getProxyInstance() {
        //1. 创建一个工具类
        Enhancer enhancer = new Enhancer();
        //2. 设置父类
        enhancer.setSuperclass(target.getClass());
        //3. 设置回调函数
        enhancer.setCallback(this);
        //4. 创建子类对象,即代理对象
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

        System.out.println("房子出租前,装修一下....");
        Object returnVal = method.invoke(target, args);
        System.out.println("房子出租后,与物业协调....");
        return returnVal;

    }

}

客户类:

/**
 * @author tcy
 * @Date 02-08-2022
 */
public class Client {
    public static void main(String[] args) {

        //创建目标对象
        Host HostTarget = new Host();
        //获取到代理对象,并且将目标对象传递给代理对象
        Host Hostproxy = (Host)new HostProxy(HostTarget).getProxyInstance();
        //执行代理对象的方法,触发intecept 方法,从而实现 对目标对象的调用
        Hostproxy.rent();
    }

}

现在不仅老王和中介都不需要实现接口了,而且完美的实现了他们之间的功能。

jdk和CGLIB实现动态代理的区别我们对比一下:

2、JDK动态代理与CGLIB对比

JDK动态代理:基于Java反射机制实现,必须要实现了接口的业务类才生成代理对象。

CGLIB动态代理:基于ASM机制实现,通过生成业务类的子类作为代理类。

JDK Proxy的优势:

最小化依赖关系、代码实现简单、简化开发和维护、JDK原生支持,比CGLIB更加可靠,随JDK版本平滑升级。而字节码类库通常需要进行更新以保证在新版Java上能够使用。

基于CGLIB的优势:

无需实现接口,达到代理类无侵入,只操作关心的类,而不必为其他相关类增加工作量。高性能。

静态代理和JDK代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理-这就是Cglib。

为了让代理模式理解的更加深刻,我们来看代理模式在两个经典框架SpringAop和Mybtis中的应用。

五、典型应用

1、在SpringAop的运用

动态代理一个显著的作用就是,在不改变目标对象的前提下,能增强目标对象的功能,这其实就是AOP的核心。

AOP(Aspect Oriented Programming)是基于切面编程的,可无侵入的在原本功能的切面层添加自定义代码,一般用于日志收集、权限认证等场景。

SpringAop同时实现了Jdk的动态代理和Cglib的动态代理。

运用动态代理直接作用到需要增强的方法上面,而不改变我们原本的业务代码。

2、在MyBatis的运用

在Mybitis实现的是Jdk的动态代理。

源码中有一个MapperProxyFactory类,其中有一个方法。

//构建handler的过程。
protected T newInstance(MapperProxy<T> mapperProxy) {
  //标准的类加载器,接口,以及invocationHandler接口实现。
  return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

Proxy.newProxyInstance()正是我们在Jdk动态代理中的使用。

结合典型应用,认真体会动态代理设计模式,参考软件设计七大原则 在实际应用中更加灵活的使用,不生搬硬套。

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我们的支持。如果你想了解更多相关内容请查看下面相关链接

(0)

相关推荐

  • 详解JAVA设计模式之代理模式

    什么是设计模式(Design Pattern)? 设计模式是一套被反复使用,多数人知晓的,经过分类编目的,代码设计经验的总结. 代理模式的定义? 代理模式就是为其他对象提供一种代理,以控制对这个对象的访问. 代理对象起到中介作用,可去掉功能服务或增加额外的服务. 代理对象和目标对象的关系? 代理对象:增强后的对象 目标对象:被增强的对象 他们不是绝对的,会根据情况发生变化. 代理模式的两种实现方式? 1.静态代理:代理和被代理对象在代理之前是确定的,它们都实现相同的接口或者继承相同的抽象类. 2

  • Java结构型设计模式中代理模式示例详解

    目录 代理模式 分类 主要角色 作用 静态代理与动态代理的区别 静态代理的基本使用 创建抽象主题 创建真实主题 创建代理主题 客户端调用 JDK动态代理的基本使用 创建抽象主题 创建真实主题 创建代理主题 客户端调用 小优化 CGLIB动态代理的基本使用 创建抽象主题 创建真实主题 创建代理主题 客户端调用 小优化 CGLIB与JDK动态代理区别 1.执行条件 2.实现机制 3.性能 代理模式 代理模式(Proxy Pattern)属于结构型模式. 它是指为其他对象提供一种代理以控制对这个对象的

  • Java设计模式之代理模式详解

    一.代理模式 代理模式就是有一个张三,别人都没有办法找到他,只有他的秘书可以找到他.那其他人想和张三交互,只能通过他的秘书来进行转达交互.这个秘书就是代理者,他代理张三. 再看看另一个例子:卖房子 卖房子的步骤: 1.找买家 2.谈价钱 3.签合同 4.和房产局签订一些乱七八糟转让协议 一般卖家只在签合同的时候可能出面一下,其他的1,2,4都由中介去做.那你问这样有什么用呢? 首先,一个中介可以代理多个卖房子的卖家,其次,我们可以在不修改卖家的代码的情况下,给他实现房子加价.打广告等等夹带私货的

  • 浅谈JAVA设计模式之代理模式

    代理模式 在代理模式(Proxy Pattern)中,一个类代表另一个类的功能.这种类型的设计模式属于结构型模式. 在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口. 介绍 意图: 为其他对象提供一种代理以控制对这个对象的访问. 主要解决: 在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上.在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时

  • 23种设计模式(7) java代理模式

    23种设计模式第七篇:java代理模式 定义: 为其他对象提供一种代理以控制对这个对象的访问.在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用 角色: 1.抽象角色:声明真实对象和代理对象的共同接口. 2.代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象.同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装. 3.真实角色:

  • Java设计模式--代理模式

    目录 1.代码设计的代理模式 2.计算机网络中的正向代理和反向代理 总结 1.代码设计的代理模式 代理模式属于构建型模式(Proxy),提供了对目标对象的一种访问方式; 即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法. 主要思想: 由被代理对象(业务类)和代理类组成,代理类通常包括日志代理类.权限代理类. 转载:ht

  • Java躲不过设计模式的坑之代理模式详解

    目录 前言 使用场景 代码分析 总结 前言 设计模式在我看来更像是一种设计思维或设计思想,它就像<孙子兵法>一样,为你的项目工程提供方向,让你的项目工程更加健壮.灵活,延续生命力.本文即将分享的是设计模式的其中一种:代理模式. 代理模式 通用官方定义:代理模式(Proxy Pattern) 是一种结构型设计模式,通过代理对象控制对原对象的访问,并允许在访问前或访问后做一些处理. 简单理解就是给一个对象找了一个替代品,这个替代品得到原对象授权,可以拦截一些无效或低效的访问,从而使得原对象可以释放

  • Java设计模式之代理模式详细解读

    目录 Java设计模式-代理模式 什么是代理模式? 代理模式-UML图: 源代码: 运行结果: 总结: 应用实例: 优点: 缺点: 使用场景: Java设计模式-代理模式 什么是代理模式? 在代理模式(Proxy Pattern)中,一个类代表另一个类的功能.这种类型的设计模式属于结构型模式. 在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口. 所谓的代理严格来讲就是两个子类共同实现一个接口,其中一个子类负责真实业务实现,另一个辅助完成主类业务逻辑操作. 代理模式-UML图: 源

  • 深入理解Java设计模式之代理模式

    目录 一.引言 二.什么是代理模式 三.代理模式的结构 四.代理模式和装饰模式的异同 五.代理模式和委托 六.代理模式的种类 七.代理模式的应用场景 八.代理模式的优缺点 九.代理模式的实现 总结 一.引言 我们都知道,数据库连接是很珍贵的资源,频繁的开关数据库连接是非常浪费服务器的CPU资源以及内存的,所以我们一般都是使用数据库连接池来解决这一问题,即创造一堆等待被使用的连接,等到用的时候就从池里取一个,不用了再放回去,数据库连接在整个应用启动期间,几乎是不关闭的,除非是超过了最大闲置时间.

  • Java设计模式之代理模式与装饰模式实例详解

    本文实例讲述了Java设计模式之代理模式与装饰模式.分享给大家供大家参考,具体如下: 之所以把这两种模式放在一起说,是因为我发现这了两种模式几乎一模一样! 从网上也搜了一些资料,发现两者还是有一些区别的.我们在学习的同时也把这种困惑搞清楚. 定义: 代理模式,为其他对象提供一种代理以控制对这个对象的访问. 装饰模式,动态地给一个对象添加一些额外的职责. 代理模式,很好理解,就是把一个对象再次封装,以后就对封装的对象访问就可以了. 因为代理对象已经取代了被代理对象. 装饰模式,给一个对象增加功能,

  • java设计模式—静态代理模式(聚合与继承方式对比)

    一.概述 1.目标:要在Tank的move()方法做时间代理及日志代理(可以设想以后还要增加很多代理处理),且代理间的顺序可活更换 2.思路: (1)聚合:代理类聚合了被代理类,且代理类及被代理类都实现了movable接口,则可实现灵活多变,具体看代码 (2)继承:继承不够灵活,随着功能需求增多,继承体系会非常臃肿.具体看代码 二.代码 1.Movable.java 2.Tank.java 3.TankTimeProxy.java 4.TankLogProxy.java 5.Tank2Time.

  • Java设计模式之代理模式原理及实现代码分享

    简介 Java编程的目标是实现现实不能完成的,优化现实能够完成的,是一种虚拟技术.生活中的方方面面都可以虚拟到代码中.代理模式所讲的就是现实生活中的这么一个概念:中介. 代理模式的定义:给某一个对象提供一个代理,并由代理对象控制对原对象的引用. 代理模式包含如下角色: ISubject:抽象主题角色,是一个接口.该接口是对象和它的代理共用的接口. RealSubject:真实主题角色,是实现抽象主题接口的类. Proxy:代理角色,内部含有对真实对象RealSubject的引用,从而可以操作真实

  • Java设计模式之代理模式与@Async异步注解失效的解决

    目录 JDK动态代理实现自定义异步注解(@Async) SpringAOP实现自定义异步注解 Spring的异步注解@Async失效分析 自定义注解实现方式 JDK动态代理实现自定义异步注解(@Async) 实现思路: 首先自定义一个注解,命名为:ExtAsync 实现一个接口,这个接口的实现类就是被代理类 实现jdk的InvocationHandler接口,根据反射获取目标方法的信息,判断是否有异步注解,如果有则另起一个线程异步执行去. 1.异步注解 @Target({ElementType.

  • Java设计模式之代理模式_动力节点Java学院整理

    引言 Java 动态代理机制的出现,使得 Java 开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类.代理类会负责将所有的方法调用分派到委托对象上反射执行,在分派执行的过程中,开发人员还可以按需调整委托类对象及其功能,这是一套非常灵活有弹性的代理框架.通过阅读本文,读者将会对 Java 动态代理机制有更加深入的理解.本文首先从 Java 动态代理的运行机制和特点出发,对其代码进行了分析,推演了动态生成类的内部实现. 代理:设计模式 代理是一种常用的设计模式,其

  • Java设计模式之代理模式(Proxy模式)介绍

    理解并使用设计模式,能够培养我们良好的面向对象编程习惯,同时在实际应用中,可以如鱼得水,享受游刃有余的乐趣. Proxy是比较有用途的一种模式,而且变种较多,应用场合覆盖从小结构到整个系统的大结构,Proxy是代理的意思,我们也许有代理服务器等概念,代理概念可以解释为:在出发点到目的地之间有一道中间层,意为代理. 设计模式中定义:为其他对象提供一种代理以控制对这个对象的访问. 为什么要使用代理模式 1.授权机制 不同级别的用户对同一对象拥有不同的访问权利,如Jive论坛系统中,就使用Proxy进

随机推荐