[java安全]动态代理

news2024/11/24 8:41:46

文章目录

    • 【java安全】动态代理
      • 前言
      • 本质
      • 重要方法
        • Proxy#newProxyInstance()
        • InvocationHandler#invoke()
      • 举例

【java安全】动态代理

前言

java中代理分为两种:静态代理动态代理

而动态代理又分为:jdk动态代理CGLIB动态代理

image-20230716152714738

本文我们来谈谈jdk动态代理机制,位于java.lang.reflect.Proxy包下,动态代理的本质是通过反射来执行invoke()方法,动态执行方法

本质

Java 的动态代理机制是一种在运行时生成代理类和代理对象的机制,它允许在调用目标对象的方法前后插入额外的逻辑。动态代理本质上是基于反射来实现的。

在 Java 中,动态代理机制依赖于两个核心接口:java.lang.reflect.InvocationHandlerjava.lang.reflect.Proxy

  1. InvocationHandler 接口:它定义了一个单一的方法 invoke(),在代理对象的方法被调用时被触发。invoke() 方法接收三个参数:代理对象、被调用的方法对象以及传递给方法的参数。通过在 invoke() 方法中编写额外的逻辑,我们可以在方法调用前后执行自定义的代码。

  2. Proxy 类:它提供了创建代理对象的静态方法。Proxy.newProxyInstance() 方法是该类的主要方法,它接收一个类加载器、一组接口和一个 InvocationHandler 对象作为参数。调用该方法后,会动态生成一个代理类,并返回一个实现了指定接口的代理对象。

动态代理的本质可以概括如下:

  1. 在运行时生成代理类:Proxy.newProxyInstance() 方法利用传入的接口信息和 InvocationHandler 对象,在内存中动态生成一个代理类的字节码,并将其加载到 JVM 中。

  2. 代理对象的方法调用:当调用代理对象的方法时,实际上是通过反射调用 InvocationHandler 对象的 invoke() 方法。

  3. invoke() 方法中处理逻辑:在 invoke() 方法中,我们可以编写额外的逻辑,如记录日志、执行前置或后置操作等。然后,我们将真正的方法调用委托给原始的目标对象。

总结起来,Java 的动态代理机制本质上是利用反射生成了一个代理类并在运行时创建了代理对象。通过 InvocationHandler 接口的 invoke() 方法,我们可以在方法调用前后插入额外的逻辑来实现切面编程和其他功能。

重要方法

Proxy#newProxyInstance()

Proxy类中存在一个重要的newProxyInstance()方法,用于创建一个动态代理对象:

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        ...
    }

这个方法有三个参数:

  • ClassLoader loader 传入一个类加载器对象
  • Class<?>[] interfaces 传入一个InvocationHandler对象使用的接口的Class数组,用于指定创建的代理对象实现了哪些接口(通俗讲,就是规定代理对象有哪些方法,结构是什么样子的)
  • InvocationHandler h 传入一个InvocationHandler对象,实现了invoke()方法,规定了通过代理对象调用方法时的相关操作

InvocationHandler#invoke()

InvocationHandler接口中有一个抽象方法:invoke()

public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

参数:

  • proxy为代理对象
  • method为调用的方法对象
  • args为调用方法的参数

举例

假设杰伦要开演唱会,会表演唱歌和跳舞两个节目,那么在活动之前肯定要布置场地、检查门票,节目结束后要收拾场地等等。如果我们让杰伦来做这些事肯定是浪费时间的,我们需要叫一个中介(代理)来做这些事情

先定义一个接口,用于规定需要实现的方法(唱歌、跳舞):

interface Activity {
    public String sing(String song);
    public String dance(String name);
}

然后定义一个杰伦类,实现该接口

class Jay implements Activity {
    private String name;

    public Jay(String name) {
        this.name = name;
    }
    @Override
    public String sing(String song) {
        System.out.println("正在布置场地~");
        System.out.println("杰伦正在唱歌~");
        return name + "正在唱" + song;
    }

    @Override
    public String dance(String s) {
        System.out.println("正在布置场地~");
        System.out.println("杰伦正在跳舞~");
        return name + "正在跳" + s;
    }
}

然后我们测试一下输出:

public class Main {
    public static void main(String[] args) {
        Jay jay = new Jay("周杰伦");
        String sing = jay.sing("晴天");
        System.out.println(sing);
        String dance = jay.dance("老年迪斯科");
        System.out.println(dance);
    }
}


/*
输出:
正在布置场地~
杰伦正在唱歌~
周杰伦正在唱晴天
正在布置场地~
杰伦正在跳舞~
周杰伦正在跳老年迪斯科
*/

我们现在需要使用代理来简化代码,不需要杰伦来布置,让中介(代理)来做这些事情

首先创建一个类Handler实现InvocationHandler接口,传入杰伦对象,实现invoke()方法,通过反射来调用杰伦对象中的方法(可以在调用方法前后添加一些额外的操作,此处我们可以添加布置场地的操作)

class Handler implements InvocationHandler {
    
    private Jay jay;
    
    public Handler(Jay jay) {
        this.jay = jay;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object s;
        if(method.getName().equals("sing")) {
            System.out.println("布置唱歌场地~");
        }else if(method.getName().equals("dance")) {
            System.out.println("布置跳舞场地~");
        }
        s = method.invoke(jay,args);
        return s;
    }
}

这个类的作用就是创建对象后传入newProxyInstance()方法的参数中,在代理对象调用方法的时候会触发Handler对象的invoke()方法用反射来执行相关的方法

我们再主程序创建一个代理对象:

public class Main {
    public static void main(String[] args) {

	    Handler handler = new Handler(new Jay("杰伦"));
        Activity jayAgent = (Activity) Proxy.newProxyInstance(
                Jay.class.getClassLoader(), Jay.class.getInterfaces(), handler
        ); //将Jay对象的类加载器、使用的接口、以及Handler对象传参
        String sing = jayAgent.sing("七里香");
        System.out.println(sing);
        String dance = jayAgent.dance("老年迪斯科");
        System.out.println(dance);
    }
}

/*
输出
布置唱歌场地~
杰伦正在唱歌~
杰伦正在唱七里香
布置跳舞场地~
杰伦正在跳舞~
杰伦正在跳老年迪斯科
*/

完整代码:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Main {
    public static void main(String[] args) {
        Handler handler = new Handler(new Jay("杰伦"));

        Activity jayAgent = (Activity) Proxy.newProxyInstance(
                Jay.class.getClassLoader(), Jay.class.getInterfaces(), handler
        );
        String sing = jayAgent.sing("七里香");
        System.out.println(sing);
        String dance = jayAgent.dance("老年迪斯科");
        System.out.println(dance);
    }
}

interface Activity {
    public String sing(String song);
    public String dance(String name);
}

class Jay implements Activity {
    private String name;

    public Jay(String name) {
        this.name = name;
    }
    @Override
    public String sing(String song) {
        System.out.println("杰伦正在唱歌~");
        return name + "正在唱" + song;
    }

    @Override
    public String dance(String s) {
        System.out.println("杰伦正在跳舞~");
        return name + "正在跳" + s;
    }
}


class Handler implements InvocationHandler {

    private Jay jay;
    public Handler(Jay jay) {
        this.jay = jay;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object s;
        if(method.getName().equals("sing")) {
            System.out.println("布置唱歌场地~");
        }else if(method.getName().equals("dance")) {
            System.out.println("布置跳舞场地~");
        }
        s = method.invoke(jay,args);
        return s;
    }
}

可见,我们可以在Handler对象的invoke()调用方法前后加入一些额外的操作,通过动态代理可以简化原来的代码,并且减少原来代码的操作

https://xz.aliyun.com/t/9197#toc-6

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

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

相关文章

Iceberg从入门到精通系列之十七:Apache InLong往Iceberg同步数据

Iceberg从入门到精通系列之十七&#xff1a;Apache InLong往Iceberg同步数据 一、概览二、版本支持三、依赖项四、SQL API 用法五、多表写入六、动态表名映射七、动态建库、建表八、动态schema变更九、Iceberg Load 节点参数十、数据类型映射 一、概览 Apache Iceberg是一种用…

【机器学习算法】主成分分析(PCA)

主成分分析(PCA) PCA(Principal Component Analysis) 是实现数据降维的一种算法。正如其名&#xff0c;假设有一份数据集&#xff0c;每条数据的维度是d&#xff0c;PCA通过分析这d个维度的前k个主要特征(这k个维度在原有d维特征的基础上重新构造出来&#xff0c;且是全新的正交…

SpringBoot+React学科竞赛管理系统 附带详细运行指导视频

文章目录 一、项目演示二、项目介绍三、运行截图四、主要代码 一、项目演示 项目演示地址&#xff1a; 视频地址 二、项目介绍 项目描述&#xff1a;这是一个基于SpringBootReact框架开发的学科竞赛管理系统。首先&#xff0c;这是一个前后端分离的项目&#xff0c;代码简洁…

初学者怎么学习c++(合集)

学习c方法1 找一本好的书本教材&#xff0c;辅助看教学视频。好的教材&#xff0c;可以让你更快更好的进入C/C的世界。在校学生的话&#xff0c;你们的教材通常都是不错的。如果是自学&#xff0c;推荐使用谭浩强出的C/C经典入门教材。看视频是学习比较直观的方式。建议先看课本…

从零开始理解Linux中断架构(20)--关于二级中断控制-链式chained Handler

二级中断控制器是个双重角色,在上级中断控制器看来他是个中断设备,在连接到他的下级设备来看,他是个中断控制器。所以处理完成基本的中断控制器管理功能:映射本地中断,还要多个动作:修改上级中断的默认irq Handler,向上级中断设置自己的链式中断处理函数。 中断控制的层…

Springboot实现过滤器

一、导言 在Spring Boot中&#xff0c;过滤器是一种用于对HTTP请求进行预处理和后处理的组件。相较于拦截器&#xff0c;过滤器属于Servlet规范的一部分&#xff0c;它能够在请求进入Web容器之前或返回给客户端之前进行操作。 要在Spring Boot中实现过滤器&#xff0c;可以按…

指针进阶(万字深层次指针解析)

❤️ 作者简介 &#xff1a;对纯音乐情有独钟的阿甘 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识&#xff0c;对纯音乐有独特的喜爱 &#x1f4d7; 日后方向 : 偏向于CPP开发以及大数据方向&#xff0c;如果你也感兴趣的话欢迎关注博主&#xff0c;期待更新 指针进阶 …

Java正则表达式,不定期更新

Java正则表达式 1. 匹配数字&#xff08;包含负数、小数&#xff09;2. 匹配不是纯数字和纯字母且需要8位以上的密码3. 密码&#xff1a;字母、数字、符号&#xff08;_-*.,!#符号可自定义&#xff09;三选二4. 密码&#xff1a;必须包含大写、小写、数字、符号&#xff08;_-*…

车道线检测|利用边缘检测的原理对车道线图片进行识别

前言 那么这里博主先安利一些干货满满的专栏了&#xff01; 这两个都是博主在学习Linux操作系统过程中的记录&#xff0c;希望对大家的学习有帮助&#xff01; 操作系统Operating Syshttps://blog.csdn.net/yu_cblog/category_12165502.html?spm1001.2014.3001.5482Linux S…

工程监测振弦采集仪的解决方案案例解释

振弦采集仪是一种用于测量结构物的振动状态和应力变化的高精度仪器&#xff0c;广泛应用于建筑、桥梁、隧道、地铁等工程领域。以下是一些常见的解决方案案例分析&#xff1a; 基础监测方案&#xff1a;对于大型建筑或桥梁工程&#xff0c;需要对基础进行实时监测。使用振弦采集…

System类 BigInterger BigDecimal

System类 常用方法和案例 exit&#xff1a; 退出当前程序 System.out.println("zhang"); // 0表示一个正常退出的状态 System.exit(0); System.out.println("cheng");System.arraycopy&#xff1a; 复制数组元素&#xff0c;比较适合底层的调用&#xf…

基于linux下的高并发服务器开发(第二章)- 2.2 进程状态转换

01 / 进程的状态 &#xff08;1&#xff09;三态模型 进程状态分为三个基本状态&#xff0c;即就绪态&#xff0c;运行态&#xff0c;阻塞态 &#xff08;2&#xff09;五态模型 在五态模型中&#xff0c;进程分为新建态&#xff0c;就绪态&#xff0c;运行态&#xff0c;阻…

mongodb练习---增删改查

环境&#xff1a; 1. 创建一个数据库 名字grade 2. 数据库中创建一个集合名字 class 3. 集合中插入若干数据 文档格式如下 &#xff5b;name:zhang,age&#xff1b;10,sex:m,hobby:[a,b,c]&#xff5d; hobby: draw sing dance basketball football pingpong compu…

Java8实战-总结2

Java8实战-总结2 基础知识方法和Lambda传递代码&#xff1a;一个例子从传递方法到Lambda 基础知识 方法和Lambda Scala和Groovy等语言的实践已经证明&#xff0c;让方法等概念作为一等值可以扩充程序员的工具库&#xff0c;从而让编程变得更容易。一旦程序员熟悉了这个强大的…

理解LLM中的ReAct

large language models (LLMs)大语言模型在语义理解和交互式决策方面有着不错的表现。ReAct在一次交互中循环使用推理和行动两个操作解决复杂问题&#xff0c;推理即利用模型自身语义理解能力&#xff0c;行动则利用模型以外的能力&#xff08;如计算、搜索最新消息&#xff0c…

OpenCv之滤波器

目录 一、卷积 二、方盒滤波与均值滤波 三、高斯滤波 四、中值滤波 五、双边滤波 一、卷积 图像卷积就是卷积核在图像上按行华东遍历像素时不断的相乘求和的过程 相关知识点: 步长:就是卷积核在图像上移动的步幅.(为充分扫描图片&#xff0c;步长一般为1)padding:指在图片…

跨服务器跨库数据联合查询

今天群里有人问多个数据源, 可否显示在一个dbgrid, 我感觉是可以的 应该有两种办法 1,如果你两个服务器上都是用的mssqlserver, 那比较好办的, 如果不同数据库,如一个mssql,一个oracle。 则需要ssms方式创建。 通过SSMS查看,如果Microsoft OLE DB Provider for …

docekr-compose搭建redis集群(三主三从)

硬件&#xff1a;三台主机 172.50.2.40 172.50.2.41 172.50.2.42 需求&#xff1a;不想让它随机分配主从关系。想指定主从关系&#xff0c;如下&#xff1a; 主节点&#xff1a;172.50.2.40:6379&#xff0c;从节点172.50.2.41:6378 主节点&#xff1a;172.50.2.41:6379&…

C波段可调谐激光器控制软件系统

花了两周时间&#xff0c;利用下班时间&#xff0c;设计了一个ITLA可调谐激光器控制系统&#xff0c;从硬件到软件。下面这个图片整套硬件系统&#xff0c;软件硬件都自己设计&#xff0c;可以定制&#xff0c;做到单片机问题也不大。相当于一套光源了 这是软件使用的界面&…

PyTorch中的torch.nn.Linear函数解析

torch.nn是包含了构筑神经网络结构基本元素的包&#xff0c;在这个包中&#xff0c;可以找到任意的神经网络层。这些神经网络层都是nn.Module这个大类的子类。torch.nn.Linear就是神经网络中的线性层&#xff0c;可以实现形如yXweight^Tb的加和功能。 nn.Linear()&#xff1a;…