Java中静态代理和动态代理介绍和使用

news2024/9/22 5:39:25

前言

在Java中,代理模式是一种常用的设计模式,用于为其他对象提供一种代理以控制对这个对象的访问。代理模式主要有两种实现方式:静态代理和动态代理。

一、静态代理

静态代理是由程序员手动创建或指定代理类,代理类在程序运行前就已经存在。静态代理通常用于业务层之间的解耦,使得委托类(被代理的类)和代理类之间不存在直接的依赖关系,而是通过共同的接口进行交互。

优点

  • 可以在不修改真实对象的前提下,增加额外的功能。
  • 灵活性高,可以根据需要创建多个代理类。

缺点

  • 代理类需要手动编写,增加了代码的复杂性。
  • 如果接口增加方法,所有实现接口的类都需要进行修改。

示例

假设有一个接口Subject和两个实现类RealSubject(真实对象)和ProxySubject(代理对象)。

Subject接口:

package edu.etime.proxy.demo;

/**
 * @Date 2024/7/21 9:51
 * @Author liukang
 **/
public interface Subject {
    void request();
}

RealSubject(真实对象)

package edu.etime.proxy.demo;

/**
 * @Date 2024/7/21 9:52
 * @Author liukang
 **/
public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("处理请求");
    }
}

 ProxySubject(代理对象):

package edu.etime.proxy.demo;

/**
 * @Date 2024/7/21 9:52
 * @Author liukang
 **/
public class ProxySubject implements Subject {
    private RealSubject realSubject;

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

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

    @Override
    public void request() {
        preRequest();
        realSubject.request();
        postRequest();
    }

    private void preRequest() {
        System.out.println("请求前的处理");
    }

    private void postRequest() {
        System.out.println("请求后的处理");
    }
}

测试调用:

package edu.etime.proxy.demo;

/**
 * @Date 2024/7/21 9:54
 * @Author liukang
 **/
public class MyTest {
    public static void main(String[] args) {
        // 创建代理对象
        ProxySubject proxySubject = new ProxySubject();
        // 调用代理对象中的request方法
        proxySubject.request();
    }
}

二、动态代理 

动态代理是在运行时动态生成代理类。动态代理主要依赖于Java的反射机制。Java提供了两种动态代理的实现方式:基于接口的JDK动态代理和基于类的CGLIB动态代理。

1.JDK动态代理

JDK动态代理

JDK动态代理是面向接口的代理模式,它要求被代理对象必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。

优点

  • 动态生成代理类,减少代码量。
  • 代理类实现简单,只需实现InvocationHandler接口。

缺点

  • 只能代理实现了接口的类。

示例

Subject接口:

package edu.etime.proxy.demo2;

/**
 * @Date 2024/7/21 9:51
 * @Author liukang
 **/
public interface Subject {
    void request();
}

RealSubject(真实对象)

package edu.etime.proxy.demo2;

/**
 * @Date 2024/7/21 10:09
 * @Author liukang
 **/
public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("处理请求");
    }
}

DynamicProxyHandler(代理对象):

package edu.etime.proxy.demo2;

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

/**
 * @Date 2024/7/21 10:01
 * @Author liukang
 **/
public class DynamicProxyHandler implements InvocationHandler {
    private Object target;

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

    // 实现InvocationHandler接口重写的方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        /*方法参数解释
        * Object proxy:代理的对象
        * Method method:要运行的方法
        * Object[] args:调用方法时传递的参数
        * */
        System.out.println("请求前的处理");
        Object result = method.invoke(target, args);
        System.out.println("请求后的处理");
        return result;
    }

    // 创建代理对象的方法
    public static <T> T getProxyInstance(T target, Class<T> interfaceClass) {
        return (T) Proxy.newProxyInstance(
                interfaceClass.getClassLoader(),// 指定类加载器 此处就使用传入的接口的类加载器,也可以使用其他的
                new Class<?>[]{interfaceClass},// 指定接口,这些接口用于指定生成的代理长什么样子,也就是有哪些方法
                new DynamicProxyHandler(target)// 动态代理对象 DynamicProxyHandler里面重写invoke方法,添加逻辑,用于指定生成的代理对象要干什么事情
        );
    }
}

测试调用:

package edu.etime.proxy.demo2;



/**
 * @Date 2024/7/21 10:06
 * @Author liukang
 **/
public class MyTest {
    public static void main(String[] args) {
        // 使用
        Subject subject = (Subject) DynamicProxyHandler.getProxyInstance(new RealSubject(), Subject.class);
        subject.request();
    }
}

 

 

2.CGLIB动态代理

CGLIB(Code Generation Library)是一个强大的、高性能的代码生成库,它可以在运行时扩展Java类和实现接口。CGLIB通过继承被代理类来创建代理对象,因此它不要求被代理类实现接口。

优点

  • 可以代理没有实现接口的类。

缺点

  • 性能上比JDK动态代理稍差。
  • 使用复杂度较高。

CGLIB动态代理的关键类:

  • Enhancer:CGLIB中最常用的类,用于创建代理对象。它允许你设置被代理类、回调接口等。
  • MethodInterceptor:这是CGLIB中用于拦截方法调用的接口。你需要实现这个接口,并在其intercept方法中编写你的代理逻辑。

CGLIB动态代理的关键步骤:

  1. 创建MethodInterceptor实现类:在这个类中,你编写方法拦截逻辑。
  2. 使用Enhancer设置被代理类和回调:配置Enhancer实例,指定被代理的类和MethodInterceptor实现类。
  3. 创建代理对象:调用Enhancer的create()方法创建代理对象。
  4. 通过代理对象调用方法:代理对象会拦截所有调用,并将它们转发到你的MethodInterceptor实现类。

示例

RealSubject(真实对象)

package com.etime.proxy;

/**
 * @Date 2024/7/21 10:32
 * @Author liukang
 **/
public class RealSubject {
    public void request() {
        System.out.println("处理请求");
    }
}

CglibProxy(代理对象):

package com.etime.proxy;

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

import java.lang.reflect.Method;

/**
 * @Date 2024/7/21 10:32
 * @Author liukang
 **/
public class CglibProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("Before method " + method.getName());
        Object result = proxy.invokeSuper(obj, args); // 调用父类(即被代理类)中的方法
        System.out.println("After method " + method.getName());
        return result;
    }

    public static <T> T getProxyInstance(Class<T> clazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz); // 设置被代理类
        enhancer.setCallback(new CglibProxy()); // 设置回调
        return (T) enhancer.create(); // 创建代理对象
    }
}

测试调用:

package com.etime.proxy;

/**
 * @Date 2024/7/21 10:39
 * @Author liukang
 **/
public class MyTest {
    public static void main(String[] args) {
        RealSubject realSubject = CglibProxy.getProxyInstance(RealSubject.class);
        realSubject.request(); // 这将调用代理对象的request方法,该方法会拦截并增强原始方法
    }
}

注意:cglib动态代理需要导入对应的依赖或者jar包

maven依赖:

         <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>

在这个示例中,RealSubject是一个普通的Java类,没有实现任何接口。我们使用CGLIB的Enhancer类来创建RealSubject的代理对象。CglibProxy类实现了MethodInterceptor接口,并在其intercept方法中编写了代理逻辑。当通过代理对象调用request方法时,intercept方法会被调用,并且我们可以在调用原始方法之前和之后执行额外的逻辑。

注意:由于CGLIB是通过继承来创建代理对象的,因此它不能代理final类,也不能代理final方法,因为final类和方法不能被继承。此外,由于CGLIB依赖于Java的反射机制来生成代理类,因此在性能上可能略逊于JDK动态代理(特别是在处理大量代理对象时)。然而,对于没有实现接口的类,CGLIB是一个很好的选择。

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

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

相关文章

【C语言】详解结构体(下)(位段)

文章目录 前言1. 位段的含义2. 位段的声明3. 位段的内存分配&#xff08;重点&#xff09;3.1 存储方向的问题3.2 剩余空间利用的问题 4. 位段的跨平台问题5. 位段的应用6. 总结 前言 相信大部分的读者在学校或者在自学时结构体的知识时&#xff0c;可能很少会听到甚至就根本没…

sip代理服务器、SIP用户代理服务器、sip服务器的区别和联系

一&#xff0e;SIP代理服务器&#xff08;SIP Proxy Server&#xff09;和SIP用户代理服务器&#xff08;SIP User Agent Server&#xff0c;简称SIP UAS&#xff09;的区别和联系。 1. 区别 1&#xff09;功能定位 SIP代理服务器&#xff1a;主要负责将SIP请求消息从发起方…

VBA技术资料MF175:利用文本框和列表框实现多列数据录入

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套&#xff0c;分为初级、中级、高级三大部分&#xff0c;教程是对VBA的系统讲解&#…

学习周报:文献阅读+水动力学方程推导

目录 摘要 Abstract 文献阅读&#xff1a;物理信息神经网络学习自由表面流 文献摘要 讨论|结论 预备知识 浅水方程SWE&#xff08;Shallow Water Equations&#xff09; 质量守恒方程&#xff1a; 动量守恒方程&#xff1a; Godunov通量法&#xff1a; 基本原理&…

分布式会话拦截器

1.分布式会话拦截器-构建拦截器 背景&#xff1a;对于不同的用户进行权限拦截(基于token的判断) 实现过程&#xff1a;在api下构建包以及相关的文件&#xff0c;创建UserTokenInterceptor,实现implements handlerInterceptor.重写三种主要方法。 preHandle postHandle afterCo…

MongoDB文档整理

过往mongodb文档&#xff1a; https://blog.csdn.net/qq_46921028/article/details/123361633https://blog.csdn.net/qq_46921028/article/details/131136935https://blog.csdn.net/qq_46921028/article/details/139247847 1. MongoDB前瞻 1、MongoDB概述&#xff1a; MongoDB是…

【Rust日报】在 Linux 文件系统中使用 Rust 的讨论

SIMD 加速的迭代器 单指令流多数据流&#xff08;Single Instruction Multiple Data&#xff0c;缩写&#xff1a;SIMD&#xff09;是一种采用一个控制器来控制多个处理器&#xff0c;同时对一组数据&#xff08;又称"数据向量"&#xff09;中的每一个分别执行相同的…

PDF压缩软件电脑版 电脑pdf压缩怎么压缩文件

在数字化时代&#xff0c;pdf文件因其良好的兼容性和稳定性&#xff0c;已成为工作与生活中不可或缺的文件格式。然而&#xff0c;随着内容的增多&#xff0c;pdf文件的体积也随之增大&#xff0c;给文件的传输和存储带来了一定的困扰。本文将为你详细介绍如何在电脑上压缩pdf文…

【手撕数据结构】拿捏单链表

目录 单链表介绍链表的初始化打印链表增加节点尾插头插再给定位置之后插入在给定位置之前插入 删除节点尾删头删删除给定位置的节点删除给定位置之后的节点 查找节点 单链表介绍 单链表也叫做无头单向非循环链表&#xff0c;链表也是一种线性结构。他在逻辑结构上一定连续&…

昇思25天学习打卡营第10天 | FCN图像语义分割

学习心得&#xff1a;全卷积网络&#xff08;FCN&#xff09;在图像语义分割中的应用 图像语义分割作为计算机视觉领域的一个重要分支&#xff0c;对于理解图像内容提供了非常关键的技术支持。通过学习并实践全卷积网络&#xff08;FCN&#xff09;在图像语义分割的应用&#…

2024-07-19 Unity插件 Odin Inspector9 —— Validation Attributes

文章目录 1 说明2 验证特性2.1 AssetsOnly / SceneObjectsOnly2.2 ChildGameObjectsOnly2.3 DisallowModificationsIn2.4 FilePath2.5 FolderPath2.6 MaxValue / MinValue2.7 MinMaxSlider2.8 PropertyRange2.9 Required2.10 RequiredIn2.11 RequiredListLength2.12 ValidateIn…

【学习笔记】无人机系统(UAS)的连接、识别和跟踪(八)-无人机探测与避让(DAA)机制

目录 引言 5.6 探测与避让&#xff08;DAA&#xff09;机制 5.6.1 基于PC5的探测与避让&#xff08;DAA&#xff09;机制 引言 3GPP TS 23.256 技术规范&#xff0c;主要定义了3GPP系统对无人机&#xff08;UAV&#xff09;的连接性、身份识别、跟踪及A2X&#xff08;Airc…

HP ilo4服务器硬件监控指标解读

随着企业IT架构的复杂化&#xff0c;服务器的稳定性和可靠性成为保障业务连续性的关键因素。HP ilo4作为HP服务器的一个重要组件&#xff0c;提供了强大的远程管理和监控功能。本文将对使用监控易软件通过HP ilo4进行服务器硬件监控的指标进行解读&#xff0c;帮助运维团队更好…

数学建模-----SPSS参数检验和非参数检验

目录 1.参数检验 1.1独立样本t检验案例分析 1.1.1查看数据编号 1.1.2确定变量所属类型 1.1.3选项里面的置信区间 1.1.4对于结果进行分析 1.2配对样本t检验案例分析 1.2.1相关设置 1.2.2分析结果 2.非参数检验 2.1对比分析 2.2非参数检验的方法 2.3案例分析 2.3.1相…

Codeforces Round 960 (Div. 2)(A~C)题

A. Submission Bait 思路: 如果最大值有奇数个显然Alice赢&#xff0c;否则只需要看排序后是否存在n−i1是否为奇数且ai>ai−1即可。 代码: #include<bits/stdc.h> using namespace std; #define N 2000010 typedef long long ll; typedef unsigned long long ull; …

KMeans等其他聚类算法

KMeans算法是一种经典的聚类方法&#xff0c;最早由Stuart Lloyd在1957年提出&#xff0c;并在1982年由J. MacQueen推广和普及。虽然KMeans已经有几十年的历史&#xff0c;但它依然是数据挖掘和机器学习领域中最常用的聚类算法之一。 数学原理 KMeans算法的目标是将数据集分成…

Blender中的重拓扑修改器如何使用?

许多人还不了解Blender中的重拓扑编辑器及其使用方法。Blender中的重拓扑修改器提供了一系列工具和选项&#xff0c;以简化创建优化网格的过程&#xff0c;无论是出于何种目的&#xff0c;都能为3D艺术家和建模者节省大量时间和精力。那么&#xff0c;在Blender中重拓扑的定义是…

springcloud-config客户端启用服务发现报错找不到bean EurekaHttpClient

背景 在对已有项目进行改造的时候&#xff0c;集成SpringConfigStarter&#xff0c;编写完bootstrap.yml&#xff0c;在idea 启动项中编辑并新增VM options -Dspring.cloud.config.discovery.enabledtrue&#xff0c;该版本不加spring不会从configService获取信息&#xff0c;…

网络结构-组件-AI(九)

深度学习网络组件 RNN公式讲解计算示意图讲解 CNN计算示意 Normalization(归一化层)Normalization常见两种方式 Dropout层 RNN 循环神经网络&#xff08;recurrent neural network&#xff09; 主要思想&#xff1a; 即将整个序列划分成多个时间步&#xff0c;将每一个时间步的…

OrangePi AIpro 快速上手初体验

开发板开箱 1.1.包装 1.2.开发版 Orange Pi AIpro Orange Pi官网-香橙派&#xff08;Orange Pi&#xff09;开发板 1.3.引脚分布 1.4开发板资源简介 1CPU配备了4核64位ARM处理器&#xff0c;其中默认预留1个给AI处理器使用NPU集成了华为昇腾310BAI处理器&#xff0c;拥有4TF…