Java中代理的实现方式

news2025/1/15 20:42:01

Java中代理的实现方式

  • 1. 静态代理
  • 2. JDK动态代理
  • 3. CGLIB动态代理
    • 扩展

在这里插入图片描述

在Java中,有多种方式可以实现代理,包括:

  1. 静态代理:

    • 创建一个代理类,实现与目标类相同的接口或继承与目标类相同的父类。
    • 在代理类中持有一个目标类的引用。
    • 在代理类的方法中,调用目标类的对应方法,并在其前后执行额外的逻辑。
    • 使用代理对象来调用方法,实际上是通过代理类间接调用目标类的方法。
  2. JDK动态代理:

    • 定义一个接口,作为代理类和目标类的共同接口。
    • 创建一个实现InvocationHandler接口的代理类。
    • 通过Proxy.newProxyInstance()方法创建代理对象,传入目标类的类加载器、目标类实现的接口和代理类的实例。
    • 在代理类的invoke()方法中,可以在调用目标方法之前和之后执行额外的逻辑。
    • 使用代理对象来调用方法,实际上是通过代理类的invoke()方法间接调用目标类的方法。
  3. CGLIB动态代理:

    • 引入CGLIB库的依赖。
    • 创建一个实现MethodInterceptor接口的代理类。
    • 通过Enhancer类创建代理对象,设置目标类作为父类、代理类作为回调对象。
    • 在代理类的intercept()方法中,可以在调用目标方法之前和之后执行额外的逻辑。
    • 使用代理对象来调用方法,实际上是通过代理类的intercept()方法间接调用目标类的方法。

这些代理方式各有特点,可以根据具体需求选择适合的方式。静态代理相对简单,但需要为每个目标类编写一个代理类;JDK动态代理适用于基于接口的代理;CGLIB动态代理适用于没有实现接口的类的代理。

1. 静态代理

// 定义接口
interface Subject {
    void doSomething();
}

// 目标类
class RealSubject implements Subject {
    @Override
    public void doSomething() {
        System.out.println("RealSubject is doing something.");
    }
}

// 代理类
class ProxySubject implements Subject {
    private RealSubject realSubject;

    public ProxySubject(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public void doSomething() {
        System.out.println("ProxySubject is doing something before RealSubject.");
        realSubject.doSomething();
        System.out.println("ProxySubject is doing something after RealSubject.");
    }
}

// 使用示例
public class StaticProxyExample {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        ProxySubject proxySubject = new ProxySubject(realSubject);
        proxySubject.doSomething();
    }
}

在上面的示例中,Subject是一个接口,RealSubject是目标类,实现了Subject接口。ProxySubject是代理类,也实现了Subject接口。在ProxySubject中,我们持有一个RealSubject的引用,并在调用doSomething()方法之前和之后执行额外的逻辑。

在StaticProxyExample中,我们创建了一个RealSubject对象和一个ProxySubject对象,并调用proxySubject.doSomething()方法。在执行过程中,ProxySubject会在调用RealSubject的doSomething()方法之前和之后输出额外的信息,从而实现对目标类的增强。

这就是一个简单的静态代理示例,通过代理类包装目标类,实现了对目标类的功能增强。

2. JDK动态代理

首先,定义一个接口和一个实现该接口的目标类:

// 定义接口
interface Subject {
    void doSomething();
}

// 实现接口的目标类
class RealSubject implements Subject {
    @Override
    public void doSomething() {
        System.out.println("RealSubject is doing something.");
    }
}

然后,创建一个实现InvocationHandler接口的代理类:

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

// 代理类
class ProxyHandler implements InvocationHandler {
    private Object target;

    public ProxyHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Proxy is doing something before RealSubject.");
        Object result = method.invoke(target, args);
        System.out.println("Proxy is doing something after RealSubject.");
        return result;
    }
}

在代理类的invoke()方法中,我们可以在调用目标方法之前和之后执行额外的逻辑。
最后,创建代理对象并调用方法:

import java.lang.reflect.Proxy;

public class JDKDynamicProxyExample {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        ProxyHandler proxyHandler = new ProxyHandler(realSubject);

        // 创建代理对象
        Subject proxySubject = (Subject) Proxy.newProxyInstance(
                realSubject.getClass().getClassLoader(),
                realSubject.getClass().getInterfaces(),
                proxyHandler);

        // 调用代理对象的方法
        proxySubject.doSomething();
    }
}

在上面的示例中,我们创建了一个RealSubject对象和一个ProxyHandler对象。然后,使用Proxy.newProxyInstance()方法创建代理对象,传入目标类的类加载器、目标类实现的接口和代理类的实例。通过代理对象调用方法时,实际上是调用了代理类的invoke()方法,在其中执行了额外的逻辑,并最终调用目标对象的方法。

这就是一个简单的使用JDK动态代理的示例,通过代理类和InvocationHandler接口,我们可以在运行时动态地生成代理对象,并在调用目标方法之前和之后执行额外的逻辑。

3. CGLIB动态代理

首先,确保在项目中添加CGLIB库的依赖。

然后,定义一个目标类:

// 目标类
class RealSubject {
    public void doSomething() {
        System.out.println("RealSubject is doing something.");
    }
}

接下来,创建一个实现MethodInterceptor接口的代理类:

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

// 代理类
class ProxyInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("Proxy is doing something before RealSubject.");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("Proxy is doing something after RealSubject.");
        return result;
    }
}

在代理类的intercept()方法中,我们可以在调用目标方法之前和之后执行额外的逻辑。在CGLIB动态代理中,通过MethodProxy对象来调用目标方法。

最后,创建代理对象并调用方法:

import net.sf.cglib.proxy.Enhancer;

public class CGLIBDynamicProxyExample {
    public static void main(String[] args) {
        ProxyInterceptor proxyInterceptor = new ProxyInterceptor();

        // 创建Enhancer对象
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(RealSubject.class);
        enhancer.setCallback(proxyInterceptor);

        // 创建代理对象
        RealSubject proxySubject = (RealSubject) enhancer.create();

        // 调用代理对象的方法
        proxySubject.doSomething();
    }
}

在上面的示例中,我们创建了一个ProxyInterceptor对象,并使用Enhancer类来创建代理对象。通过setSuperclass()方法指定目标类,通过setCallback()方法指定代理类。最后,调用create()方法创建代理对象。

通过代理对象调用方法时,实际上是调用了代理类的intercept()方法,在其中执行了额外的逻辑,并最终调用目标对象的方法。

这就是一个简单的使用CGLIB动态代理的示例,通过代理类和MethodInterceptor接口,我们可以在运行时动态地生成代理对象,并在调用目标方法之前和之后执行额外的逻辑。

扩展

CGLIB在实现动态代理时使用了ASM库。ASM是一个Java字节码操作和分析框架,可以用于在运行时生成或修改字节码。CGLIB利用ASM库来生成代理类的字节码,以实现对目标类的动态代理。

在CGLIB中,Enhancer类使用了ASM库来生成代理类的字节码。ASM提供了一套API,可以直接操作字节码,包括创建类、添加字段、添加方法、修改方法等。CGLIB利用ASM的API来生成代理类的字节码,并将其加载到JVM中。

通过使用ASM库,CGLIB能够在运行时生成代理类的字节码,而无需依赖目标类的接口。这使得CGLIB可以代理那些没有实现接口的类,提供了更大的灵活性。

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

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

相关文章

6.2、Flink数据写入到Kafka

目录 1、添加POM依赖 2、API使用说明 3、序列化器 3.1 使用预定义的序列化器 3.2 使用自定义的序列化器 4、容错保证级别 4.1 至少一次 的配置 4.2 精确一次 的配置 5、这是一个完整的入门案例 1、添加POM依赖 Apache Flink 集成了通用的 Kafka 连接器,使…

Python:函数调用的实参

相关阅读 Python专栏https://blog.csdn.net/weixin_45791458/category_12403403.html 调用就是附带可能为空的一系列参数来执行一个可调用对象 (例如函数),它的语法的BNF范式如下所示,有关BNF范式的规则,可以参考之前…

无涯教程-JavaScript - CEILING.MATH函数

描述 CEILING.MATH函数将数字四舍五入到最接近的整数或最接近的有效倍数。 Excel CEILING.MATH函数是Excel中的十五个舍入函数之一。 语法 CEILING.MATH (number, [significance], [mode])争论 Argument描述Required/OptionalNumberNumber must be less than 9.99E307 and …

运维自动化:提高效率的秘诀

🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🦄 博客首页——🐅🐾猫头虎的博客🎐 🐳 《面试题大全专栏》 🦕 文章图文…

从一到无穷大 #16 ByteSeries,思考内存时序数据库的必要性

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。 本作品 (李兆龙 博文, 由 李兆龙 创作),由 李兆龙 确认,转载请注明版权。 引言 在[3]中我基于Gorilla讨论了时序数据库设置cache的可行性,最后得出结论&…

许可分析 license分析 第十章

许可分析是指对软件许可证进行详细的分析和评估,以了解组织内部对软件许可的需求和使用情况。通过许可分析,可以帮助组织更好地管理和优化软件许可证的使用。以下是一些可能的许可分析方法和步骤: 软件许可证供应商管理:评估和管理…

CSS 浮动布局

浮动的设计初衷 float: left/right/both;浮动是网页布局最古老的方式。 浮动一开始并不是为了网页布局而设计,它的初衷是将一个元素拉到一侧,这样文档流就能够包围它。 常见的用途是文本环绕图片: 浮动元素会被移出正常文档流,…

Redis集群总结

Redis,作为一款开源的、内存中的数据结构存储系统,以其出色的性能和丰富的数据结构在业界赢得了广泛的认可。然而,当我们面临大量数据和高并发请求时,单个 Redis 实例可能无法满足我们的需求。这时,我们就需要使用到 R…

Kubernetes实践:从入门到实践

🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🦄 博客首页——🐅🐾猫头虎的博客🎐 🐳 《面试题大全专栏》 🦕 文章图文…

认识Git的工作区、暂存区与版本库

使用 git init 命令在 gitcode 文件夹下创建如下图所示的Git仓库。现在思考这样一个问题:gitcode目录下创建的README文件可以直接被git管理和追踪吗? 答案是否定的,因为只有 Git 本地仓库中的文件才可以被版本控制。什么?难道当前…

内网隧道代理技术(二十七)之 DNS隧道介绍

DNS隧道介绍 DNS协议介绍 域名系统(Domain Name System,缩写:DNS)是互联网的一项服务。它作为将域名和IP地址相互映射的一个分布式数据库,能够使人更方便地访问互联网。DNS使用TCP和UDP端口53。当前,对于每一级域名长度的限制是63个字符,域名总长度则不能超过253个字符…

第二篇------Virtual I/O Device (VIRTIO) Version 1.1

上篇文章:https://blog.csdn.net/Phoenix_zxk/article/details/132917657 篇幅太大,所以分开写,接下来续上 4.3.3.2.1 设备要求:Guest->Host 通知 设备必须忽略 GPR2 的位 0-31(从左边数)。这样可以使…

Eclipse开源代码下载

当前插件开发,需要修改eclipse源码,如需要修改remote相关的代码,所以需要下载相关源码。网上大多资料都说的不清不楚的,也可能我太小白,不明白,反正就是折腾了一两天才感觉有点思路,改如何找源码…

Linux安全加固:保护你的服务器

🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🦄 博客首页——🐅🐾猫头虎的博客🎐 🐳 《面试题大全专栏》 🦕 文章图文…

基于springboot+vue的问卷调查系统

博主主页:猫头鹰源码 博主简介:Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容:毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

聊天机器人

收集窗帘相关的数据 可以用gpt生成,也可以用爬虫 图形化界面 gradio 向量数据库 faiss python代码 import gradio as gr import random import timefrom typing import Listfrom langchain.embeddings.openai import OpenAIEmbeddings from langchain.vectorstor…

央媒发稿不能改?媒体发布新闻稿有哪些注意点

传媒如春雨,润物细无声,大家好,我是51媒体网胡老师。 “央媒发稿不能改”是媒体行业和新闻传播领域的普遍理解。央媒,即中央主要媒体,是权威性的新闻源,当这些媒体发布新闻稿或报道时,其他省、…

服务器监控工具:选择与应用

🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🦄 博客首页——🐅🐾猫头虎的博客🎐 🐳 《面试题大全专栏》 🦕 文章图文…

积木报表 JimuReport v1.6.2-GA5版本发布—高危SQL漏洞安全加固版本

项目介绍 一款免费的数据可视化报表,含报表和大屏设计,像搭建积木一样在线设计报表!功能涵盖,数据报表、打印设计、图表报表、大屏设计等! Web 版报表设计器,类似于excel操作风格,通过拖拽完成报…

活锁 死锁

一、活锁(liveLock) 活锁是指线程间资源冲突激烈,引起线程不断的尝试获取资源,不断的失败。活锁有点类似于线程饥饿,虽然资源并没有被别人持有,但由于各种原因而无法得到。最常见的原因是进程组的执行顺序…