背景

  • 有时目标对象不可直接访问,只能通过代理对象访问

  • 图示:

  • 示例1:

    • 房东 ===> 目标对象
    • 房屋中介 ===> 代理对象
    • 你,我 ===> 客户端对象
  • 示例2:
    • 运营商(电信,移动,联通) ===> 目标对象
    • 第三方公司 ===> 代理对象
    • 开发的应用程序需要发送短信的功能(或者需要支付功能) ===> 客户端对象

代理模式的作用

  • 控制客户对目标对象的访问
  • 增强访问功能

代理模式的分类

  • 静态代理
  • 动态代理
    • JDK动态代理
    • CGLib动态代理

静态代理

特点

  • 目标对象和代理对象实现同一个业务接口
  • 目标对象必须实现接口
  • 代理对象在程序运行前就已经存在

静态代理示例与原理分析

业务背景

分析

  • 定义业务接口:面向接口编程,定义业务
  • 目标对象实现接口:业务的核心功能到底怎么实现
  • 代理对象(扩展业务 + 核心业务)
    • 实现了目标对象所实现的接口,说明代理对象有资历进行代理
    • 对核心业务进行扩展
    • 调用目标对象实现核心业务(只能目标对象自己完成)
  • 客户:无法直接访问目标对象,要访问代理对象

代码实现

  • 面向接口编程

    • 成员变量是接口类型
    • 传入目标对象,方法的参数设计为接口
    • 调用时,接口指向实现类
  • 静态代理对象代码

    package com.example.service.impl;
    
    import com.example.service.Service;
    
    public class Agent implements Service {
    
        //定义接口对象
    public Service target; public Agent(){} //传入接口对象
    public Agent(Service target){
    this.target = target;
    } @Override
    public void sing() {
    System.out.println("协商演出时间......");
    System.out.println("协商演出地点......"); //目标对象完成核心业务,接口指向实现类,调用实现类的方法
    target.sing(); System.out.println("协商演出费用......");
    }
    }

静态代理优缺点

  • 优点:能够灵活的进行目标对象的切换

    • 适用于业务固定,目标对象可灵活切换的场景
  • 缺点:无法进行功能的灵活处理,当业务发生改变时,所有涉及到的实现类代码和代理对象代码都要改变

动态代理

JDK动态代理

特点

  • 目标对象必须实现业务接口
  • JDK代理对象不需要实现业务接口
  • JDK代理对象在程序运行前不存在,程序运行时动态的在内存中构建(根据受代理的对象动态创建)
  • JDK动态代理可以灵活的进行业务功能的切换

JDK动态代理用到的类和接口

  • 使用现有的工具类完成JDK动态代理
  • 先了解两个单词的意思
    • InvocationHandler:调用处理程序
    • invoke:调用

Method类

  • 反射时用的类,用来进行目标对象的目标方法的反射调用
  • method对象,接住我们正在调用的方法 sing(),show()
    • method == sing(),show(),即:待调用的方法
    • method.invoke() ==> 相当于手工调用目标方法 sing(),show();

InvocationHandler接口

  • 用来实现代理和业务功能,我们在调用时使用匿名内部实现

    • 匿名内部实现:new接口的同时,重写接口中的方法(相当于定义了该接口的一个实现类)

Proxy类

  • 位于:java.lang.reflect.Proxy包下

  • 有一个核心方法:Proxy.newProxyInstance(....),专门获取动态代理对象,有三个参数

    • 参数1:ClassLoader loader

      • 目标对象的类加载器
      • 目的:获取类方法等信息,毕竟底层还是要调用受代理对象所实现的方法
      • 传入:targetObj.getClass().getClassLoader();
    • 参数2:Class<?>[] interfaces

      • 目标对象实现的所有接口,类的接口可以有多个
      • 目的:获取目标对象实现的所有接口以及接口的相关信息,毕竟底层要知道目标对象都可以完成哪些业务操作
      • 传入:targetObj.getClass().getInterfaces();
    • 上面两个参数为代理对象动态的创建和调用目标对象的方法提供了数据支持,第3个参数相当于调用程序

    • 参数3:InvocationHandler

      • 实现代理功能的接口,这里代理功能包括:扩展的功能 + 核心业务功能,传入的匿名内部实现如下
      new InvocationHandler() {
      @Override
      public Object invoke(
      Object obj,
      //用来反射调用方法
      Method method,
      //待调用方法需要的参数
      Object[] args)
      throws Throwable { //扩展业务
      System.out.println("协商演出时间......");
      System.out.println("协商演出地点......"); //核心业务,具体调用什么方法根据外层业务来反射调用对应方法
      Object res = method.invoke(target, args); //扩展业务
      System.out.println("协商演出费用......"); //目标对象执行的目标方法的返回值
      return res;
      }
      }

JDK动态代理示例

  • 代理工厂代码

    package com.example.proxy;
    
    import com.example.service.Service;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy; public class ProxyFactory {
    //目标对象
    Service target; public ProxyFactory(){} public ProxyFactory(Service target){
    this.target = target;
    } //返回代理对象
    public Object getAgent(){
    return Proxy.newProxyInstance(
    //需要知道受代理对象的类信息
    target.getClass().getClassLoader(),
    //需要知道受代理对象实现的所有接口信息
    target.getClass().getInterfaces(),
    //反射调用目标对象的目标方法
    new InvocationHandler() {
    @Override
    public Object invoke(
    Object obj,
    //用来反射调用方法
    Method method,
    //待调用方法需要的参数
    Object[] args)
    throws Throwable { //扩展业务
    System.out.println("协商演出时间......");
    System.out.println("协商演出地点......"); //核心业务,具体调用什么方法根据外层业务来反射调用对应方法
    Object res = method.invoke(target, args); //扩展业务
    System.out.println("协商演出费用......"); //目标对象执行的目标方法的返回值
    return res;
    }
    }
    );
    }
    }
  • 测试代码示例

    package com.example.proxy;
    
    import com.example.service.Service;
    import com.example.service.impl.SuperStarZhou;
    import org.junit.Test; public class TestProxyFactory {
    @Test
    public void testGetProxy(){
    //确定客户需求
    ProxyFactory factory = new ProxyFactory(new SuperStarZhou());
    //根据需求动态返回对应类型的代理对象
    Service agent = (Service) factory.getAgent();
    //依托对应类型的动态代理对象完成业务:扩展业务(动态代理对象完成) + 核心业务(目标对象完成)
    agent.sing();
    } @Test
    public void testGetProxy2(){
    ProxyFactory factory = new ProxyFactory(new SuperStarZhou());
    Service agent = (Service) factory.getAgent();
    String res = (String) agent.show(60);
    System.out.println(res);
    }
    }

注意

  • 可被代理的方法应该是受代理对象实现的所有接口中的方法与其所有实体方法的交集

    • 本类中独有的方法不被代理
  • 类型的转变

     @Test
    public void testGetProxy2() {
    ProxyFactory factory = new ProxyFactory(new SuperStarZhou());
    Service agent = (Service) factory.getAgent();
    Service liu = new SuperStarLiu();
    System.out.println("类型1: " + liu.getClass());
    System.out.println("类型2: " + agent.getClass());
    }
    /*
    输出结果:
    类型1: class com.example.service.impl.SuperStarLiu
    类型2: class com.sun.proxy.$Proxy7
    */

mybatis 01: 静态代理 + jdk动态代理的更多相关文章

  1. 从Mybatis源码理解jdk动态代理默认调用invoke方法

    一.背景最近在工作之余,把开mybatis的源码看了下,决定自己手写个简单版的.实现核心的功能即可.写完之后,执行了一下,正巧在mybatis对Mapper接口的动态代理这个核心代码这边发现一个问题. ...

  2. Java的三种代理模式:静态代理/JDK动态代理/Cglib动态代理

    1.静态代理:需要定义接口或者父类,目标对象与代理对象均实现同一接口或继承同一父类. 2.JDK动态代理:需要目标对象实现一个接口,通过动态反射的机制,生成代理对象,实现同一个接口 3.Cglib动态 ...

  3. java 静态代理 JDK动态代理 Cglib动态代理

    下面以一个简单的银行账户为例讲述讲述动态代理. 设计一个银行账户类,包含用户的账户余额,实现查询和更新余额功能 这个系统用了一段时间,有客户要求对账说账户余额给弄错了?因为上面没有存取款记录,最后银行 ...

  4. MyBatis之反射技术+JDK动态代理+cglib代理

    一.反射 引用百度百科说明: JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:这种动态获取信息以及动态调用对象方法的功 ...

  5. 【Java入门提高篇】Day11 Java代理——JDK动态代理

    今天来看看Java的另一种代理方式--JDK动态代理 我们之前所介绍的代理方式叫静态代理,也就是静态的生成代理对象,而动态代理则是在运行时创建代理对象.动态代理有更强大的拦截请求功能,因为可以获得类的 ...

  6. 动态代理 JDK动态代理 CGLIB代理

    代理模式:代理类和被代理类实现共同的接口(或继承),代理类中存有指向被代理类的索引,实际执行时通过调用代理类的方法.实际执行的是被代理类的方法. 而AOP,是通过动态代理实现的. 一.简单来说: JD ...

  7. 代理-jdk动态代理

    1.基于接口的实现,要jdk动态代理的类必须要实现一个接口: 2.中介类:实现了InvocationHandler,并重写这个接口的 方法(public Object invoke(Object pr ...

  8. MyBatis Mapper 接口如何通过JDK动态代理来包装SqlSession 源码分析

    我们以往使用ibatis或者mybatis 都是以这种方式调用XML当中定义的CRUD标签来执行SQL 比如这样 <?xml version="1.0" encoding=& ...

  9. 017 Java中的静态代理、JDK动态代理、cglib动态代理

    一.静态代理 代理模式是常用设计模式的一种,我们在软件设计时常用的代理一般是指静态代理,也就是在代码中显式指定的代理. 静态代理由业务实现类.业务代理类两部分组成.业务实现类负责实现主要的业务方法,业 ...

随机推荐

  1. 基于 BaGet 搭建 Nuget 服务器

    1 前言 1.1 BaGet 介绍 BaGet 是一个轻量级的,开源的,跨平台的 Nuget 和 symbol 服务器. 1.2 环境介绍 操作系统:CentOS 7 使用 Docker 安装 2 安 ...

  2. 爷青回,canal 1.1.6来了,几个重要特性和bug修复

    刚刚在群里看到消息说,时隔一年,canal 1.1.6正式release了,赶紧上去看看有什么新特性. (居然才发布了6个小时,前排围观) 1.什么是canal canal [kə'næl],译意为水 ...

  3. 如何把你的 Android 使用得像 Linux

    前言 最近在学校里上课,老师讲的东西又听不进去,手里只有一个手机和一个平板,之前还可以用 ssh 连接云服务器玩点东西,但是我是用的软件 Juice ssh 并不是很友好,退出到后台一段时间后竟然会自 ...

  4. 【ASP.NET Core】配置应用程序地址的N多种方法

    下面又到了老周误人子弟的时间,今天要误大伙的话题是:找找有多少种方法可以设置 ASP.NET Core 应用的地址,即 URL. 精彩马上开始! 1.UseUrls 方法 这是一个扩展方法,参数是可变 ...

  5. Linux系列之linux访问windows文件

    Linux永久挂载windows共享文件 Linux系统必须安装samba-client Linux服务器必须能访问到Windows的共享文件服务的(445端口) 1.Windows共享文件 2.测试 ...

  6. Camunda定时器事件示例Demo(Timer Events)

    ​Camunda定时器事件(Timer Events)是由定义的计时器触发的事件.它们可以用作启动事件.中间事件或边界事件.边界事件可以中断,也可以不中断. Camunda定时器事件包括:Timer ...

  7. C# 将XML转为PDF

    XML,即可扩展标记语言文,件是一种标准的文本文件,它使用特定的标记来描述文档的结构以及其他特性.通过将 XML 文档转换为 PDF格式,能够满足更多程序.设备对文件预览.读取或展示的需要,也更便于文 ...

  8. 纯css就能实现可点击切换的轮播图,feel起来很丝滑

    前言 轮播图经常会在项目里用到,但是实际上用到的轮播图都是比较简单的,没有复杂的特效,这个时候如果去引入swiper那些库的话,未免就有点杀鸡焉用牛刀了. 所以不如自己手写一个,而今天我要分享的一种写 ...

  9. Redis之Lua的应用(四)

    一.什么是Lua脚本 Lua是一个高效的轻量级脚本语言(和JavaScript类似),用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能.Lu ...

  10. 安装typescript环境并开启VSCode自动监视编译ts文件为js文件

    一.前言 小编最近开始学习typescript,懂得人都知道,typescript是vue3的基础伴生,配合更加默契.就像vue2和js一样!typescript不像js那样浏览器直接可以解读,需要我 ...