Java Review - 关于代理的二三事儿

news2024/11/24 0:45:37

文章目录

  • Pre
  • 概述
  • 静态代理
    • 概述
    • Code
  • 动态代理
    • 概述
    • 实现方式一 - JDK代理或接口代理
      • 概述
      • Code
    • 实现方式二 - CGLib 子类代理 (Code Generation Library)
      • 概述
      • pom依赖
      • Code

在这里插入图片描述


Pre

Java-JDK动态代理

Java-CGLib动态代理


概述

代理模式是一种结构型设计模式,其目的是为其他对象提供一种代理以控制对这个对象的访问。

在 Java 中,代理模式有两种形式:

  • 静态代理
  • 动态代理

在代理模式中,代理类和目标类之间有一个抽象接口,代理类实现了这个接口,而目标类则实现了具体的业务逻辑。通过代理类调用目标类,可以实现对目标类的访问控制、权限检查、审计等功能。

代理模式在 Java 中应用广泛,例如可以使用代理模式来实现对网络请求的缓存、访问控制、限流等功能。


静态代理

在这里插入图片描述

概述

静态代理是一种在编译时生成代理类的方式。在静态代理中,代理类和委托类的关系在运行前就确定了。静态代理模式的实现需要手动编写代理类,并使用代理类来调用目标类。

静态代理是指在编译时就已经确定了代理类和被代理类的关系,代理类和被代理类都要实现同一个接口或者继承同一个父类。在静态代理中,代理类负责调用被代理类的方法,并在方法调用前后进行一些额外的处理

静态代理的优点是简单易懂,容易实现,缺点是需要为每个被代理类编写一个代理类,如果被代理类过多,会导致代码冗长和维护困难。


Code

在这里插入图片描述

package com.artisan.proxy.static_proxy;

/**
 * @author artisan
 */
public interface Subject {

    void bussiness();
}

package com.artisan.proxy.static_proxy;

/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */
public class RealSubject implements Subject {

    @Override
    public void bussiness() {
        System.out.println("RealSubject bussiness  Logic ");
    }
}
    
package com.artisan.proxy.static_proxy;

/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 * @desc: 代理类也需要实现同一个接口, 并持有真正的业务对象
 */
public class ProxySubject implements Subject {

    /**
     * 持有真正的对象
     */
    private RealSubject realSubject;


    /**
     * 传入要代理的对象, 通过构造函数实例化代理对象
     *
     * @param realSubject
     */
    public ProxySubject(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public void bussiness() {
        // 代理前的业务逻辑
        System.out.println("ProxySubject before request");

        // 真正的业务处理逻辑
        realSubject.bussiness();

        // 代理后业务逻辑
        System.out.println("ProxySubject after request");
    }
}
    
package com.artisan.proxy.static_proxy;

/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */
public class StaticProxyDemo {

    public static void main(String[] args) {
        // 真正的处理对象
        RealSubject realSubject = new RealSubject();
        // 传入真正的处理对象,初始化代理对象
        ProxySubject proxySubject = new ProxySubject(realSubject);
        // 通过代理类调用实现了同一个接口或者继承了同一个父类的方法,以便织入代理逻辑,且不影响原来的方法的逻辑
        proxySubject.bussiness();
    }
}
    

在这里插入图片描述


动态代理

概述

动态代理是一种在运行时生成代理类的方式。在动态代理中,代理类和委托类的关系是在运行时确定的。动态代理模式的实现可以使用 Java 提供的 Proxy 类和 InvocationHandler 接口来实现 或者 CGLib动态代理。


实现方式一 - JDK代理或接口代理

在这里插入图片描述

概述

动态代理是指在运行时动态生成代理类,不需要为每个被代理类编写一个代理类。在动态代理中,代理类实现了InvocationHandler接口,通过反射机制调用被代理类的方法,并在方法调用前后进行一些额外的处理

动态代理的优点是可以动态生成代理类,不需要为每个被代理类编写一个代理类,缺点是实现较为复杂。

Java动态代理是一种在运行时创建代理类的机制,它允许在不提前知道代理类的具体类型的情况下,动态地创建一个代理对象来代替原始类。相比于静态代理,动态代理更加灵活,可以代理任意的接口类型,不需要为每个被代理的类编写专门的代理类,而是通过Java的反射机制在运行时动态生成代理类。

动态代理主要使用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来实现。动态代理又被称为JDK代理或接口代理。

Code

在这里插入图片描述

package com.artisan.proxy.dynamic_jdk_proxy;

/**
 * @author artisan
 */
public interface Subject {

    void request();


    String handle(String inParams);
}


package com.artisan.proxy.dynamic_jdk_proxy;

/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */
public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("real subject execute request");
    }

    @Override
    public String handle(String inParams) {
        System.out.println("real subject execute handle, 入参: " + inParams);
        return inParams;
    }
}
    
    
package com.artisan.proxy.dynamic_jdk_proxy;

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

/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */
public class DynamicProxy implements InvocationHandler {

    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("DynamicProxy before request");

        Object result = method.invoke(target, args);

        System.out.println("DynamicProxy after request");
        return result;
    }
}
 package com.artisan.proxy.dynamic_jdk_proxy;

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

/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */
public class DynamicJdkProxyDemo {

    public static void main(String[] args) {

        // 创建真实(返回值请使用接口来接收)
        Subject subject = new RealSubject();

        // 创建动态代理类
        InvocationHandler invocationHandler = new DynamicProxy(subject);

        // 一切都是面向接口  方式一
        Subject subjectProxy = (Subject) Proxy.newProxyInstance(
                Subject.class.getClassLoader(),
                new Class[]{Subject.class},
                invocationHandler);

        // 通过动态代理进行request方法调用
        subjectProxy.request();

        System.out.println("------------------------------");

        String handle = subjectProxy.handle("1-testInParams");
        System.out.println(handle);

        System.out.println("------------------------------");

        // 一切都是面向接口  方式二
        Subject o = (Subject) Proxy.newProxyInstance(
                subject.getClass().getClassLoader(),
                subject.getClass().getInterfaces(),
                invocationHandler);

        // 通过动态代理进行request方法调用
        o.request();
        System.out.println("------------------------------");

        String handle1 = o.handle("2-testInParams");
        System.out.println(handle1);
    }
}
    
    

在这里插入图片描述

通过动态代理,我们在执行类的方法前后成功添加了额外的处理(xxxx),同时客户端代码无需关心具体的日志记录逻辑,实现了解耦。动态代理在很多场景下非常有用,例如AOP(面向切面编程)等


实现方式二 - CGLib 子类代理 (Code Generation Library)

概述

CGLIB(Code Generation Library)是一个开源的第三方库,用于在Java运行时生成字节码并创建代理类。与Java标准库中的动态代理(基于接口)不同,CGLIB代理可以代理普通类,即使它们没有实现任何接口。CGLIB使用ASM库来生成字节码,并通过继承的方式创建代理类,因此也被称为子类代理。CGLIB广泛用于各种框架和库中,如Spring AOP

cglib代理是一种基于字节码技术的代理方式,它可以在运行时动态生成被代理类的子类,并覆盖其中的方法来实现代理。在cglib代理中,被代理类不需要实现任何接口或者继承任何父类。

cglib代理的优点是不需要为每个被代理类编写一个接口或者父类,可以对任意类进行代理,缺点是由于它是基于字节码技术实现的,所以生成的子类可能会比较庞大


pom依赖

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

Code

在这里插入图片描述

 package com.artisan.proxy.dynamic_cglib_proxy;

/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 * @desc: 普通类
 */
public class NormalClass {

    public String logic() {
        System.out.println("Normal class execute Logic");
        return "OK";
    }

    public int minus(int a, int b) {
        System.out.println("Normal class execute minus");
        return a - b;
    }
}
    
    
 package com.artisan.proxy.dynamic_cglib_proxy;

/**
 * @author artisan
 */
public interface Subject {

    void request();


    String handle(String inParams);
}


package com.artisan.proxy.dynamic_cglib_proxy;

/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 * @desc: 接口类
 */
public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("real subject execute request");
    }

    @Override
    public String handle(String inParams) {
        System.out.println("real subject execute handle, 入参: " + inParams);
        return inParams;
    }
}
    
    
package com.artisan.proxy.dynamic_cglib_proxy;


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

import java.lang.reflect.Method;

/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 * @desc: Cglib动态代理回调类, 需要实现MethodInterceptor接口
 */
public class CglibProxy implements MethodInterceptor {

    /**
     *
     */
    private Object target;

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

    /**
     * 创建代理类
     *
     * @return
     */
    public Object createProxy() {
        // 创建Enhancer对象,用于生成动态代理
        Enhancer enhancer = new Enhancer();

        // 设置需要创建代理的类
        enhancer.setSuperclass(target.getClass());

        // 设置回调对象,处理代理方法调用
        enhancer.setCallback(this);

        // 生成代理类
        return enhancer.create();
    }

    /**
     * 拦截代理类方法调用
     *
     * @param o           代理类实例
     * @param method      代理类方法
     * @param args        方法参数
     * @param methodProxy 方法代理,用于调用父类方法
     */
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

        // 前置处理
        System.out.println("CglibProxy before request");

        // 调用父类方法
        Object result = methodProxy.invokeSuper(o, args);

        // 后置处理
        System.out.println("CglibProxy after request");

        return result;
    }
}
    
package com.artisan.proxy.dynamic_cglib_proxy;


/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */
public class DynamicCglibProxyDemo {

    public static void main(String[] args) {
        testNormalClass();
        System.out.println("==============================");
        testInterface();
    }

    /**
     * 代理普通类
     */
    public static void testNormalClass() {
        // 真实类
        NormalClass normalClass = new NormalClass();
        // 构建代理类
        CglibProxy cglibProxy = new CglibProxy(normalClass);
        // 创建代理类
        NormalClass proxy = (NormalClass) cglibProxy.createProxy();

        // 通过代理类执行方法调用无参方法
        proxy.logic();
        System.out.println("---------------------------");

        // 调用代理对象的方法,传入参数
        int result = proxy.minus(5, 2);
        System.out.println(result);
    }

    /**
     * 代理接口
     */
    public static void testInterface() {
        // 真实类
        Subject subject = new RealSubject();
        //  构建代理类
        CglibProxy cglibProxy = new CglibProxy(subject);
        // 创建代理类
        Subject subjectProxy = (Subject) cglibProxy.createProxy();

        // 通过代理类执行方法调用无参方法
        subjectProxy.request();
        System.out.println("---------------------------");

        // 调用代理对象的方法,传入参数
        String result = subjectProxy.handle("testInparmas");
        System.out.println(result);
    }
}
    

在这里插入图片描述
通过CGLIB代理,我们成功在方法执行前后添加了额外的处理(xx),实现了解耦。CGLIB代理在不需要接口的情况下也能很好地完成代理任务,但由于它是通过继承的方式生成代理类,可能会影响某些场景,比如无法代理final方法

在这里插入图片描述

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

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

相关文章

Windows安装MinGW和简单的使用教程

Windows安装MinGW和简单的使用教程 什么是MinGW&#xff1f; MinGW&#xff0c;是Minimalist GNU for Windows的缩写。它是一个可自由使用和自由发布的Windows特定头文件和使用GNU工具集导入库的集合&#xff0c;允许你在GNU/Linux和Windows平台生成本地的Windows程序而不需要…

在P4(Perforce)中使用TortoiseMerge来比较合并

一直习惯于svn的比较合并工具&#xff0c;会觉得p4自带的反人性。还好p4可以在设置里替换成外部的比较合并工具。方法见下图&#xff1a; 1. 比较 2. 合并 注意&#xff0c;如果合并设置有问题&#xff08;某些P4版本&#xff09;&#xff0c;则需要通过一个bat文件来做中转&a…

聊聊看React和Vue的区别

Vue 更适合小项目&#xff0c;React 更适合大公司大项目&#xff1b; Vue 的学习成本较低&#xff0c;很容易上手&#xff0c;但项目质量不能保证...... 真的是这样吗&#xff1f;借助本篇文章&#xff0c;我们来从一些方面的比较来客观的去看这个问题。 论文档的丰富性 从两个…

Linux fork()||fork()问题

以下代码会输出几个"A"&#xff1f; int main() { fork() || fork();printf("A\n");} 代码分析&#xff1a; //父进程fork() || fork();printf("A\n");这里父进程中的第一个fork先执行&#xff0c;产生一个子进程&#xff1a; //子进程…

Electron-builder打包和自动更新

前言 文本主要讲述如何为 electron 打包出来软件配置安装引导和结合 github 的 release 配置自动更新。 electron-builder 是将 Electron 工程打包成相应平台的软件的工具&#xff0c;我的工程是使用 electron-vite 构建的&#xff0c;其默认集成了 electron-builder &#x…

K8S之存储卷

K8S之存储卷 一、emptyDir emptyDir&#xff1a;可实现Pod中的容器之间共享目录数据&#xff0c;但emptyDir存储卷没有持久化数据的能力&#xff0c;存储卷会随着Pod生命周期结束而一起删除二、hostPath hostPath&#xff1a;将Node节点上的目录/文件挂载到Pod容器的指定目录…

【JavaWeb】实训的长篇笔记(下)

文章目录 八、功能实现1、注册功能2、登录功能3、问题说明4、首页数据显示5、后台管理 八、功能实现 1、注册功能 jsp&#xff1a;能够在页面中把数据动态化&#xff0c;jsp和html在元素标签上是无区别的&#xff0c;区别是html中写上java代码就成了jsp文件。filename.jsp。 需…

Threejs学习01——坐标轴展示立方体并实现来回移动

在三维坐标轴上展示立方体并实现来回移动 这是一个非常简单基础的threejs的学习应用&#xff01;创建应用先创建一个场景Scene&#xff0c;然后创建爱你一个透视相机PerspectiveCamera&#xff0c;然后创建立方体BoxGeometry&#xff0c;立方体添加一些材质&#xff0c;将立方…

SPSS多元线性回归操作入门实例

做农情反演的时候往往需要用到SPSS多元线性回归&#xff0c;这里提供一个操作案例 (一)SPSS安装 关于SPSS安装&#xff0c;请参考本人博客&#xff1a;保姆级SPSS图文安装教程_追忆苔上雪的博客-CSDN博客 (二)SPSS多元线性回归实例 在文章ArcGIS入门操作手册_追忆苔上雪的博…

Windows下升级jdk1.8小版本

1.首先下载要升级jdk最新版本&#xff0c;下载地址&#xff1a;Java Downloads | Oracle 中国 2.下载完毕之后&#xff0c;直接双击下载完毕后的文件&#xff0c;进行安装。 3.安装完毕后&#xff0c;调整环境变量至新安装的jdk位置 4.此时&#xff0c;idea启动项目有可能会出…

CCF考试:201703-1 分蛋糕(java代码)

1、【问题描述】 小明今天生日&#xff0c;他有n块蛋糕要分给朋友们吃&#xff0c;这n块蛋糕&#xff08;编号为1到n&#xff09;的重量分别为a1, a2, …, an。小明想分给每个朋友至少重量为k的蛋糕。小明的朋友们已经排好队准备领蛋糕&#xff0c;对于每个朋友&#xff0c;小明…

2023年新学期12306高铁火车学生票如何在线核验享受优惠?

2023学年优惠资质核验已开始&#xff0c;完成学生优惠资质核验后&#xff0c;您可以在线购买2022年10月1日至2023年9月30日的学生优惠票。&#xff08;注&#xff1a;非该时间段需要重新核验&#xff0c;可享受学生优惠票&#xff09;&#xff1b; 『扩展阅读』 1、美团外卖红…

日常BUG——SpringBoot关于父子工程依赖问题

&#x1f61c;作 者&#xff1a;是江迪呀✒️本文关键词&#xff1a;日常BUG、BUG、问题分析☀️每日 一言 &#xff1a;存在错误说明你在进步&#xff01; 一、问题描述 在父子工程A和B中。A依赖于B&#xff0c;但是A中却无法引入B中的依赖&#xff0c;具体出现的…

新基建助推数字经济,CosmosAI率先布局AI超算租赁新纪元

伦敦, 8月14日 - 在英国伦敦隆重的Raffles OWO举办的欧盟数字超算新时代战略合作签约仪式&#xff0c;CosmosAI、Infinite Money Fund与Internet Research Lab三方强强联手&#xff0c;达成了历史性的合作协议&#xff0c;共同迈向超算租赁新纪元。 ​ 这次跨界的合作昭示了全球…

Docker入门指南:从零开始轻松掌握容器化技术【超级详细版】

文章目录 什么是Docker&#xff1f;平时应用部署的环境问题Docker解决依赖兼容问题Docker可以解决操作系统环境差异 Docker和虚拟机的区别Docker架构镜像容器仓库Docker的安装 Docker基本操作镜像操作容器操作数据卷&#xff08;容器数据管理&#xff09;1. 什么是数据卷2.数据…

C++初阶之一篇文章教会你stack(理解使用和模拟实现)

stack&#xff08;理解使用和模拟实现&#xff09; 什么是stackstack的使用1. stack构造函数2.empty()3.size()3.top()4.push5.emplace6.pop()7.swap 模拟实现stack的准备1.什么是容器适配器&#xff1f;2. deque的简单介绍3. deque的缺陷4. 为什么STL中stack和queue默认使用de…

抢红包系统---(java)

需求&#xff1a;直播抽奖活动&#xff0c;分别有【2,588,888,1000,10000】五个奖金&#xff0c;模拟抽奖。打印每个奖项&#xff0c;奖项的出现顺序随机但不重复。package demo;import java.util.Random;/* * 案例;抢红包 * 需求&#xff1a;直播抽奖活动&#xff0c;分别有【…

shell脚本循环语句

shell脚本循环语句 一.echo命令二.查看当前系统的时间--date命令三.循环语句for四.while循环语句结构五.while循环语句结构&#xff08;迭代&#xff09;六.continue和break 一.echo命令 echo -n 表示不换行输出 echo -e输出转义符&#xff0c;将转义后的内容输出到屏幕上 常…

抖音小程序排名代发需要提供什么资料

抖音小程序排名代发需要提供什么资料 抖音小程序如何做搜索排名&#xff0c;排名提升方法#小程序搭建#小程序建设#小程序排名# 营业执照&#xff0c;注册抖音的手机号&#xff0c;对公账户&#xff0c;关键词以及对应的图片 提供以上资料就可以 小程序包括两大功能&#xff0…

springboot 数据库版本升级管理常用解决方案

目录 一、前言 1.1 单独执行初始化sql 1.2 程序自动执行 二、数据库版本升级管理问题 三、spring 框架sql自动管理机制 3.1 jdbcTemplate 方式 3.1.1 创建数据库 3.1.2 创建 springboot 工程 3.1.3 初始化sql脚本 3.1.4 核心配置类 3.1.5 执行sql初始化 3.2 配置文…