mybatis 01: 静态代理 + jdk动态代理 + cglib动态代理

news2025/1/21 14:15:17

背景

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

  • 图示:

  • 示例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
     */
    

CGLib动态代理(了解)

特点

  • 这是一种子类代理模式,在程序运行时,动态生成代理对象,实现功能增强
  • 目标对象不需要实现接口,使用子类来增强功能
  • 规避了jdk动态代理中目标对象必须实现接口的弊端
  • 被代理的类不能是final修饰的,被代理的方法不能是final和static修饰的
  • 底层是由字节码处理框架ASM来完成的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/40681.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

(Java)P1223 排队接水

排队接水 一、 题目描述 有 nnn 个人在一个水龙头前排队接水&#xff0c;假如每个人接水的时间为 TiT_iTi​&#xff0c;请编程找出这 nnn 个人排队的一种顺序&#xff0c;使得 nnn 个人的平均等待时间最小。 二、输入格式 第一行为一个整数 nnn。 第二行 nnn 个整数&…

Linux下加密库Libsodium 使用实践(ip监听、封装的加密消息、运行系统命令)

Libsodium 是一个用C语言编写的库&#xff0c;是一种新的易于使用的高速软件库&#xff0c;用于网络通信、加密、解密、签名等实现和简化密码学。 完成 Libsodium 安装 Libsodium 是一个用于加密&#xff0c;解密&#xff0c;数字签名&#xff0c;密码哈希&#xff0c;等的&a…

java每日一练(4)

java每日一练(4) 文章目录单选部分不定项选择题多选题编程题单选部分 1.下列与队列结构有关联的是&#xff08;&#xff09; A 函数的递归调用   B 数组元素的引用   C 多重循环的执行   D 先到先服务的作业调度 队列的特点 &#xff1a; 先进先出 , 所以 答案非常明显 D  …

【python】通过gitlab v4版本api接口获取所有项目代码示例

目录一、环境信息二、参数说明三、脚本使用说明1. 使用python2运行git.py2. python脚本执行完毕会自动生成如下四个文件3. 其他脚本说明四、脚本源码1. git.py2. update.sh五、脚本扩展说明附录一、环境信息 脚本适用于&#xff1a;python2 测试版本&#xff1a;2.7.18 二、…

于我来说,赌才是世界杯的灵魂~

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。 先看这里前言了解足球首看世界杯再看世界杯前言 身边朋友也有踢球的&#xff0c;但是不多。就两个&#xff0c;一个是我同学&#xff0c;一个是我同事…打篮球的倒是不少&#xff0c;猜想…

这五个适合上班族的副业你知道多少

第二职业赚钱的路子有什么&#xff1f;从理论上讲&#xff0c;第二职业就是一个创业的过程&#xff0c;也遵照自主创业一般规律。可是第二职业是在业余时间和没有灵活运用资源挣钱&#xff0c;和创业有所不同。第二职业门坎变低&#xff0c;更比较发达&#xff0c;因此今天小编…

Arduino--音乐频谱

本文主要介绍基于Arduino实现的音乐频谱显示&#xff0c;音乐频谱原理就是声音传感器&#xff08;MIC&#xff09;接收音频信号后通过FFT将时域信号转换成频域信号&#xff0c;再将音频信号频域分量分别显示在对应的LED点阵屏上&#xff0c;呈现出音乐随频律动的感觉&#xff0…

Windows Docker Desktop安装K8S

Docker DeskTop提供了K8S支撑&#xff0c;安装也较为简单。对于本地开发&#xff0c;测试部署项目较为方便。下面进行简单说明。 DockerDesktop配置镜像源&#xff0c;较为简单&#xff0c;有许多网上例子直接参考即可。启用K8S等待一阵子&#xff0c;K8S即可安装成功。可以看…

MySQL存储引擎介绍

首先 我们要知道 什么是引擎 我们常见的 客机 直升机 火箭等等 他们都有自己的引擎 引擎也就是指一个机器的核心 当然 你如果是一个飞机 那你自然是不能用火箭的引擎的 存储引擎就是存储数据 建立索引 更新/查询数据等技术的实现方式&#xff0c;存储引擎是基于表的&#xf…

TDK | CeraLink 电容器快速切换逆变器的革新

本周向大家介绍另一款压电技术的产品CeraLink。 CeraLink 是一系列非常紧凑的电容器&#xff0c;用于稳定直流链路中的电压。因此它们适合用作缓冲器或直流母线电容器。这些产品基于 PLZT 陶瓷&#xff0c;旨在为工程师提供针对快速开关转换器、空间要求非常紧凑的转换器和需要…

Java项目:饰品商城系统(java+SSM+JSP+javascript+jQuery+Mysql)

源码获取&#xff1a;俺的博客首页 "资源" 里下载&#xff01; 项目介绍 本项目分为前台与后台&#xff0c;有普通用户与管理员两种角色&#xff1b; 管理员角色包含以下功能&#xff1a; 管理员登录,用户管理,一级分类管理,二级分类管理,饰品管理,订单管理、发货、…

Linux中对磁盘(硬盘)分区和挂载

记录&#xff1a;346 场景&#xff1a;在CentOS 7.9操作系统上&#xff0c;使用fdisk对磁盘分区&#xff1b;使用mkfs.xfs创建文件系统&#xff1b;使用mount挂载磁盘到目录&#xff1b;使用umount卸载目录已挂载的磁盘&#xff1b;修改文件系统表fstab&#xff0c;满足开机启…

【计算机毕业设计】网上游戏代练商城系统

一、系统截图&#xff08;需要演示视频可以私聊&#xff09; 摘要 随着当今社会的发展&#xff0c;时代的进步&#xff0c;各行各业也在发生着变化&#xff0c;本系统健身房这一方面&#xff0c;利用网站游戏代练已经逐步进入人们的生活。传统的网上游戏代练&#xff0c;都是用…

前端基础入门

HTML的基本概念 WWW&#xff08;World Wide Web&#xff0c;万维网&#xff09;是一种建立在Internrt上的、全球性的、交互的、多平台的、分布式的信息资源网络。它采用HTML语言描述超文本&#xff08;Hypertext&#xff09;文件。这里所说的超文本文件指的是包含链接关系和多…

数据结构与算法中的图

数据结构与算法中的图 图的定义与术语 在线性结构之间&#xff0c;数据元素之间满足唯一的线性关系。每个数据元素&#xff08;除第一个和最后一个外&#xff09;只有一个****直接前趋和直接后继** 在树形结构中&#xff0c;数据元素之间有着明显的层次关系&#xff0c;并且每…

实验四、R_b变化对Q点和电压放大倍数的影响 ​

一、题目 研究 RbR_bRb​ 变化对 QQQ 点和 A˙u\dot A_uA˙u​ 的影响。 二、仿真电路 仿真电路如图1(b)、(ccc)所示。晶体管采用 FMMT5179。其datasheet的相关参数如下&#xff1a;(a)FMMT5179的参数(a)\textrm{FMMT5179}的参数(a)FMMT5179的参数 (b)Rb为3MΩ时的情况(b)R…

RDD——Action算子

常用Action算子 countByKeycollectreducefoldfirsttaketopcounttakeSampletakeOrderedforeachsaveAsTextFile countByKey() 返回值是一个 字典类型 ## 读取文本文件 rdd1 sc.textFile(f"file:///{ROOT}/data/input/words.txt") rdd2 rdd1.flatMap(lambda x:x.spl…

【数字信号去噪】小波软阈值+硬阈值+改进阈值数字信号去噪【含Matlab源码 1025期】

⛄一、小波语音降噪简介 对于噪声频谱遍布于语音信号频谱之中的宽带噪声&#xff0c;如果噪声振幅比大部分的语音信号振幅低&#xff0c;则削去低幅度成分也就削去了宽带噪声。基于这种思路&#xff0c;可以在频域中采取中心限幅的方法&#xff0c;即让带噪语音信号通过一限幅…

实战关于登录框的渗透测试

前言 登录框可以说是我们漏洞挖掘中最常见的点&#xff0c;漏洞的种类也是相当的多&#xff0c;相信大家在登录框中发现的漏洞数不胜数。 主要有以下这些漏洞 弱口令 SQL注入 水平越权 垂直越权 逻辑漏洞 短信轰炸 邮箱轰炸 信息泄露SQL注入 看到登录框&#xff0c;输入信…

C++智能指针之weak_ptr

C智能指针之weak_ptr前言weak_ptr总结前言 在C中&#xff0c;动态内存的申请和释放是通过运算符&#xff1a;new 和 delete 进行管理的。其中 new 负责申请内存&#xff0c;delete负责释放内存。 动态内存的使用很容易出现问题&#xff0c;这主要在于你需要保证在正确的时间释放…