静态代理与动态代理

静态代理与动态代理

在这里插入图片描述

静态代理

某个对象提供一个代理,代理角色固定,以控制对这个对象的访问。 代理类和委托类有共同的父类或父接口,这样在任何使用委托类对象的地方都可以用代理对象替代。代理类负责请求的预处理、过滤、将请求分派给委托类处理、以及委托类执行完请求后的后续处理。

代理的三要素

a、有共同的行为(结婚) - 接口

b、目标角色(新人) - 实现行为

c、代理角色(婚庆公司) - 实现行为 增强目标对象行为

静态代理的特点

1、目标角色固定

2、在应用程序执行前就得到目标角色

3、代理对象会增强目标对象的行为

4、有可能存在多个代理 引起"类爆炸"(缺点)

静态代理的实现

定义行为(共同) 定义接口

/**
 * 定义行为
 */
public interface Marry {
    public void toMarry();
}

目标对象(实现行为)

/**
 * 静态代理 ——> 目标对象
 */
public class You implements  Marry {
    // 实现行为
    @Override
    public void toMarry() {
        System.out.println("我要结婚了...");
    }
}

代理对象(实现行为、增强目标对象的行为)

/**
 * 静态代理 ——> 代理对象
 */
public class MarryCompanyProxy implements Marry {

    // 目标对象
    private Marry marry;
    // 通过构造器将目标对象传入
    public MarryCompanyProxy(Marry marry) {
        this.marry = marry;
    }

    // 实现行为
    @Override
    public void toMarry() {
        // 增强行为
        before();
                
        // 执行目标对象中的方法
        marry.toMarry();
        
        // 增强行为
        after();
    }

    /**
     * 增强行为
     */
    private void after() {
        System.out.println("新婚快乐,早生贵子!");
    }

    /**
     * 增强行为
     */
    private void before() {
        System.out.println("场地正在布置中...");
    }
}

通过代理对象实现目标对象的功能

// 目标对象
You you = new You();
// 构造代理角色同时传入真实角色
MarryCompanyProxy marryCompanyProxy = new MarryCompanyProxy(you);
// 通过代理对象调用目标对象中的方法
marryCompanyProxy.toMarry();

静态代理对于代理的角色是固定的,如dao层有20个dao类,如果要对方法的访问权限进行代理,此时需要创建20个静态代理角色,引起类爆炸,无法满足生产上的需要,于是就催生了动态代理的思想。

动态代理

相比于静态代理,动态代理在创建代理对象上更加的灵活,动态代理类的字节码在程序运行时,由Java反射机制动态产生。它会根据需要,通过反射机制在程序运行期,动态的为目标对象创建代理对象,无需程序员手动编写它的源代码。动态代理不仅简化了编程工作,而且提高了软件系统的可扩展性,因为反射机制可以生成任意类型的动态代理类。代理的行为可以代理多个方法即满足生产需要的同时又达到代码通用的目的

动态代理的两种实现方式:

  1. JDK 动态代理

  2. CGLIB动态代理

动态代理的特点

  1. 目标对象不固定
  2. 在应用程序执行时动态创建目标对象
  3. 代理对象会增强目标对象的行为

JDK动态代理

注:JDK动态代理的目标对象必须有接口实现

newProxyInstance

Proxy类:

Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下操作方法:

/*
    返回一个指定接口的代理类的实例方法调用分派到指定的调用处理程序。 (返回代理对象)
        loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
        interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
        h:一个InvocationHandler接口,表示代理实例的调用处理程序实现的接口。每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法(传入InvocationHandler接口的子类)
*/
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)

获取代理对象

public class JdkHandler implements InvocationHandler {

    // 目标对象
    private Object target; // 目标对象的类型不固定,创建时动态生成
    // 通过构造器将目标对象赋值
    public JdkHandler(Object target) {
        this.target = target;
    }

    /**
     *  1、调用目标对象的方法(返回Object)
     *  2、增强目标对象的行为
     * @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)
        Object result = method.invoke(target,args);
        // 增强行为
        System.out.println("方法后执行==============");

        return result;
    }
 

    /**
     * 得到代理对象
     * public static Object newProxyInstance(ClassLoader loader,
     *                                       Class<?>[] interfaces,
     *                                       InvocationHandler h)
     *      loader:类加载器
     *      interfaces:接口数组
     *      h:InvocationHandler接口 (传入InvocationHandler接口的实现类)
     *
     *
     * @return
     */
    public Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }
}

通过代理对象实现目标对象的功能

// 目标对象
You you = new You();
// 获取代理对象
JdkHandler jdkHandler = new JdkHandler(you);
Marry marry = (Marry) jdkHandler.getProxy();
// 通过代理对象调用目标对象中的方法
marry.toMarry();    
问:Java动态代理类中的invoke是怎么调用的?
答:在生成的动态代理类$Proxy0.class中,构造方法调用了父类Proxy.class的构造方法,给成员变量invocationHandler赋值,$Proxy0.classstatic模块中创建了被代理类的方法,调用相应方法时方法体中调用了父类中的成员变量InvocationHandlerinvoke()方法。

注:JDK的动态代理依靠接口实现,如果有些类并没有接口实现,则不能使用JDK代理。

CGLIB 动态代理

JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能使用JDK的动态代理,cglib是针对类来实现代理的,它的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。

添加依赖

在pom.xml文件中引入cglib的相关依赖

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>

定义类

实现MethodInterceptor接口

public class CglibInterceptor implements MethodInterceptor {

    // 目标对象
    private Object target;
    // 通过构造器传入目标对象
    public CglibInterceptor(Object target) {
        this.target = target;
    }

    /**
     * 获取代理对象
     * @return
     */
    public Object getProxy() {
        // 通过Enhancer对象的create()方法可以生成一个类,用于生成代理对象
        Enhancer enhancer = new Enhancer();
        // 设置父类 (将目标类作为其父类)
        enhancer.setSuperclass(target.getClass());
        // 设置拦截器 回调对象为本身对象
        enhancer.setCallback(this);
        // 生成一个代理类对象,并返回
        return enhancer.create();
    }

    /**
     * 拦截器
     *  1、目标对象的方法调用
     *  2、增强行为
     * @param object  由CGLib动态生成的代理类实例
     * @param method  实体类所调用的被代理的方法引用
     * @param objects 参数值列表
     * @param methodProxy  生成的代理类对方法的代理引用
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object object, Method method, Object[] objects, 
                            MethodProxy methodProxy) throws Throwable {

        // 增强行为
        System.out.println("==============方法前执行");

        // 调用目标对象的方法(返回Object)
        Object result = methodProxy.invoke(target,objects);

        // 增强行为
        System.out.println("方法后执行==============");

        return result;
    }

}

调用方法

// 目标对象
You you = new You();
CglibInterceptor cglibInterceptor = new CglibInterceptor(you);
Marry marry = (Marry) cglibInterceptor.getProxy();
marry.toMarry();

User user = new User();
CglibInterceptor cglibInterceptor = new CglibInterceptor(user);
User u = (User) cglibInterceptor.getProxy();
u.test();

JDK代理与CGLIB代理的区别

  • JDK动态代理实现接口,目标类和代理类属于同级关系,Cglib动态代理继承思想,目标类和代理类属于父子类关系
  • JDK动态代理(目标对象存在接口时)执行效率高于Ciglib
  • 如果目标对象有接口实现,选择JDK代理,如果没有接口实现选择Cglib代理

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

在项目中数据库如何优化?【MySQL主从复制(创建一个从节点复制备份数据)】【数据库读写分离ShardingJDBC(主库写,从库读)】

MySQL主从复制 MySQL主从复制介绍MySQL复制过程分成三步&#xff1a;1). MySQL master 将数据变更写入二进制日志( binary log)2). slave将master的binary log拷贝到它的中继日志&#xff08;relay log&#xff09;3). slave重做中继日志中的事件&#xff0c;将数据变更反映它自…

基于GA优化的CNN-LSTM-Attention的时间序列回归预测matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1卷积神经网络&#xff08;CNN&#xff09;在时间序列中的应用 4.2 长短时记忆网络&#xff08;LSTM&#xff09;处理序列依赖关系 4.3 注意力机制&#xff08;Attention&#xff09; 5…

JetBrains GoLand 2023---高效Go语言开发环境的首选工具【Mac/Windows】

JetBrains GoLand 2023是一款专为Go语言开发者设计的强大集成开发环境&#xff08;IDE&#xff09;。它提供了智能代码提示、自动补全和强大的调试工具&#xff0c;帮助开发者快速编写和调试Go语言代码。JetBrains GoLand 2023的功能特色主要表现在以下几个方面&#xff1a; 强…

万象奥科携手RT-Thread,线下体验RK3568混合部署!

3月21日&#xff0c;万象奥科联合RT-Thread在上海张江举办RK3568OpenAMP混合部署线下workshop&#xff0c;体验在一个SOC上的同时运行RT-Thread和Linux&#xff0c;为电力、医疗、工业控制、机器人、新能源等行业应用提供新思路。 万象奥科为伙伴们提供RK3568全功能开发板&…

flutter使用Command库调用cmd命令或者shell命令,并支持多个参数和指定文件夹目录

想要在不同的平台上运行flutter doctor命令&#xff0c;就需要知道对应的平台是windows还是linux&#xff0c;如果是windows就需要调用cmd命令&#xff0c;如果是linux平台&#xff0c;就需要调用sh命令&#xff0c;所以可以通过cfg!实现不同平台的判断&#xff0c;然后调用不同…

私人健身与教练预约管理系统设计与实现|SpringBoot+ Mysql+Java+ B/S结构(可运行源码+数据库+设计文档)

本项目包含可运行源码数据库LW&#xff0c;文末可获取本项目的所有资料。 推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 2024年56套包含java&#xff0c;…

Java基础面试复习

一、java基础 1、jdk、jre、jvm的区别 jdk&#xff1a;Java程序开发工具包。 jre&#xff1a;Java程序运行环境。 jvm&#xff1a;Java虚拟机。 2、一个Java源文件中是否可以包含多个类有什么限制 解&#xff1a;可以包含多个类但是只有一个类生命成public并且要和文件名一致 …

开源鸿蒙系统调试模式屏幕截图功能如何实现

开源鸿蒙系统调试模式屏幕截图功能如何实现&#xff0c;适用于需要获取软件app运行界面的场景。 使用工具&#xff1a;鸿蒙官方hdc_std.exe jietu.bat里面内容为&#xff1a; set filepath/data/%date:~0,4%%date:~5,2%%date:~8,2%%time:~1,1%%time:~3,2%%time:~6,2%.jpeg ec…

[Linux]知识整理(持续更新)

前言 Linux的目录结构 Linux的目录结构是一个树型结构 Windows 系统可以拥有多个盘符, 如 C盘、D盘、E盘 Linux没有盘符这个概念, 只有一个根目录 /, 所有文件都在它下面 Linux路径的描述方式 第一章 基本命令 命令格式 例:ls –la /etc 说明: 1)个别命令使用不遵循…

力扣---最长公共子序列---二维动态规划

思想&#xff1a; 定义g[i][j]&#xff1a;text1的前i位和text2的前j位的最长公共子序列长度。递推公式&#xff1a;如果text[i]text[j]&#xff0c;那么只需要看g[i-1][j-1]即可&#xff0c;此时g[i][j]g[i-1][j-1]1。如果text[i]!text[j]&#xff0c;那么g[i][j]max(g[i-1][j…

Gif动图怎么快速制作?两招教你在线做

Gif动图作为一种实用的图片格式&#xff0c;因为其体积小&#xff0c;画面丰富&#xff0c;所以在各大聊天软件中非常的受欢迎。小伙伴们是不是很好奇这种gif动态图片是如何制作的吧&#xff01;下面&#xff0c;小编就给大家分享两个快速制作gif动画的小技巧&#xff01;不用下…

【Python机器学习系列】sklearn机器学习模型的保存---pickle法

这是我的第246篇原创文章。 一、引言 pickle是Python 的标准库&#xff0c;用于序列化对象。可以使用 pickle.dump()将模型保存到文件&#xff0c;然后使用 pickle.load()从文件中加载模型。 序列化&#xff1a;指将一个对象转换为字节流&#xff0c;能够存储在文件或网络上&…

OpenCV4.9关于矩阵上的掩码操作

返回&#xff1a;OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇:如何使用OpenCV扫描图像、查找表和时间测量 下一篇:OpenCV4.9的是如何进行图像操作 引言&#xff1a; 矩阵上的掩码操作非常简单。这个想法是&#xff0c;我们根据掩码矩阵&#xff08…

PTA题解 --- 浪漫侧影(思路题解)

今天是PTA题库解法讲解的第九天&#xff0c;今天我们要讲解浪漫侧影&#xff0c;题目如下&#xff1a; 题解思路&#xff1a; 要解决这个问题&#xff0c;首先需要根据给定的中序遍历和后序遍历序列重建二叉树。然后&#xff0c;通过分别进行层序遍历的方式&#xff0c;记录每…

阿里CICD流水线Docker部署,将阿里镜像私仓中的镜像部署到服务器中

文章目录 阿里CICD流水线Docker部署&#xff0c;将阿里镜像私仓中的镜像部署到服务器中一、CICD流水线的初步使用可以看我之前的两篇文章二、添加部署任务&#xff0c;进行Docker部署&#xff0c;创建一个阿里的试用主机1、选择主机部署&#xff0c;并添加服务主机2、创建免费体…

设计模式学习笔记 - 设计模式与范式 -结构型:2.桥接模式:如何实现支持不同类型和渠道的消息推送系统?

概述 今天学习另外一种结构型模式&#xff1a;桥接模式。桥接模式的代码实现非常简单&#xff0c;但是理解起来稍微优点难度&#xff0c;并且应用场景也比较局限&#xff0c;所以&#xff0c;相对于代理模式来说&#xff0c;桥接模式在实际的项目中并没有那么常用&#xff0c;…

全新Mistral-7B v0.2基础模型开源:32K上下文,开源界的性能巨兽

前言 在人工智能领域的发展历程中&#xff0c;开源大模型始终是推动技术进步与创新应用的关键力量。近日&#xff0c;Mistral AI再次引领开源潮流&#xff0c;发布了Mistral-7B v0.2基础模型&#xff0c;这不仅是对之前版本的升级&#xff0c;更是在性能与功能上的一次质的飞跃…

选择最佳图像处理工具OpenCV、JAI、ImageJ、Thumbnailator和Graphics2D

文章目录 1、前言2、 图像处理工具效果对比2.1 Graphics2D实现2.2 Thumbnailator实现2.3 ImageJ实现2.4 JAI&#xff08;Java Advanced Imaging&#xff09;实现2.5 OpenCV实现 3、图像处理工具结果 1、前言 SVD(stable video diffusion)开放了图生视频的API&#xff0c;但是限…

Mysql数据库:日志管理、备份与恢复

目录 前言 一、MySQL日志管理 1、存放日志和数据文件的目录 2、日志的分类 2.1 错误日志 2.2 通用查询日志 2.3 二进制日志 2.4 慢查询日志 2.5 中继日志 3、日志综合配置 4、查询日志是否开启 二、数据备份概述 1、数据备份的重要性 2、备份类型 2.1 从物理与…

【IJCAI‘23】港大提出社会推荐中的去噪自增强学习

论文标题&#xff1a; Denoised Self-Augmented Learning for Social Recommendation 收录会议&#xff1a; IJCAI 2023 论文链接&#xff1a; https://arxiv.org/abs/2305.12685 代码链接&#xff08;欢迎 ✨&#xff09;&#xff1a; https://github.com/HKUDS/DSL 港…