42.动态代理

news2024/12/23 14:13:13

动态代理

文章目录

  • 动态代理
    • JDK动态代理
    • cglib动态代理
    • jdk动态代理和cglib动态代理的区别
      • 区别:
      • CGlib动态代理示例:

JDK动态代理

1.我们需要定义一个接口,作为代理和目标对象共同实现的约束:

package com.kang.spring.service;

/**
 * @Author Emperor Kang
 * @ClassName UserService
 * @Description 首先,我们需要定义一个接口,作为代理和目标对象共同实现的约束:
 * @Date 2023/12/26 16:14
 * @Version 1.0
 * @Motto 让营地比你来时更干净
 */
public interface UserService {
    void addUser(String name);
    void deleteUser(String name);
}

2.我们定义一个目标对象,实现该接口

package com.kang.spring.service.impl;

import com.kang.spring.service.UserService;

/**
 * @Author Emperor Kang
 * @ClassName UserServiceImpl
 * @Description TODO
 * @Date 2023/12/26 16:14
 * @Version 1.0
 * @Motto 让营地比你来时更干净
 */
public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String name) {
        System.out.println("Add user: " + name);
    }

    @Override
    public void deleteUser(String name) {
        System.out.println("Delete user: " + name);
    }
}

3.我们定义一个动态代理类

package com.kang.spring.proxy;

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

/**
 * @Author Emperor Kang
 * @ClassName JdkProxy
 * @Description 我们定义一个动态代理类
 * @Date 2023/12/26 16:16
 * @Version 1.0
 * @Motto 让营地比你来时更干净
 */
public class JdkProxy implements InvocationHandler {
    /**
     * 目标对象
     */
    private Object target;

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


    /**
     * 方法执行
     * @param proxy        调用方法的代理实例(代理类)
     * @param method       对应于在代理实例上调用的接口方法的方法实例。方法对象的声明类将是声明方法的接口,这个接口可能是代理接口的父接口,代理类通过代理接口继承方法。
     * @param args         一个对象数组,包含在代理实例上的方法调用中传递的参数值;如果接口方法没有参数,则为null。基本类型的参数包装在适当的基本包装类的实例中,如java.lang.Integer或java.lang.Boolean。
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before method " + method.getName());
        Object result = method.invoke(target, args);
        System.out.println("After method " + method.getName());
        return result;
    }
}

在该代理类中,我们实现了InvocationHandler接口,并重写了invoke方法,该方法中实现了目标对象方法的前置和后置处理,即在目标对象方法执行前输出"Before method",在方法执行后输出"After method",并返回执行结果。

最后,在main方法中,我们首先创建了一个目标对象userService,然后创建了一个代理对象proxy,并将userService作为参数传递给代理对象的构造函数中。通过调用Proxy类的静态方法newProxyInstance,我们可以动态地创建代理对象。该方法的第一个参数是目标对象的类加载器,第二个参数是目标对象实现的接口,第三个参数是InvocationHandler对象。最后,我们调用代理对象的addUser和deleteUser方法,代理对象会在执行目标对象方法前后输出相应的信息。

Proxy.invoke

在这里插入图片描述

 /**
     * 方法执行
     * @param proxy        调用方法的代理实例(代理类)
     * @param method       对应于在代理实例上调用的接口方法的方法实例。方法对象的声明类将是声明方法的接口,这个接口可能是代理接口的父接口,代理类通过代理接口继承方法。
     * @param args         一个对象数组,包含在代理实例上的方法调用中传递的参数值;如果接口方法没有参数,则为null。基本类型的参数包装在适当的基本包装类的实例中,如java.lang.Integer或java.lang.Boolean。
     * @return
     * @throws Throwable
     */

Proxy.newProxyInstance

       /**
         * ClassLoader loader:定义代理类的类加载器
         * Class<?>[] interfaces:代理类要实现的接口列表
         * InvocationHandler h:将方法调用分派给它的调用处理程序(就是调用目标对象方法本身)
         */
        UserService proxy = (UserService) Proxy.newProxyInstance(
                UserService.class.getClassLoader(),  // 类加载器
                new Class[] { UserService.class },   // class类
                handler);                            // 执行的方法
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {}

执行结果

Before method addUser
Add user: Alice
After method addUser

Before method deleteUser
Delete user: Bob
After method deleteUser

cglib动态代理

CGlib动态代理是一种基于字节码技术实现的动态代理方式,它的原理是通过生成被代理类的子类来实现代理的功能,因此,它不需要像JDK动态代理那样必须实现一个接口,可以代理任何类。

1.定义一个计算器类

package com.kang.spring.pojo;

/**
 * @Author Emperor Kang
 * @ClassName Calculator
 * @Description 定义一个计算器类
 * @Date 2023/12/27 9:47
 * @Version 1.0
 * @Motto 让营地比你来时更干净
 */
public class Calculator {
    public int add(int a, int b) {
        return a + b;
    }

    public int sub(int a, int b) {
        return a - b;
    }
}

2.CalculatorInterceptor是一个方法拦截器,用于在调用被代理对象的方法前后打印一些日志。

package com.kang.spring.cglib;

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

import java.lang.reflect.Method;

/**
 * @Author Emperor Kang
 * @ClassName CalculatorInterceptor
 * @Description  定义方法拦截器
 * @Date 2023/12/27 10:03
 * @Version 1.0
 * @Motto 让营地比你来时更干净
 */
public class CalculatorInterceptor implements MethodInterceptor {

    /**
     * 被代理的目标对象
     */
    private Object target;

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

    /**
     * 拦截
     * @param o             生成的代理对象
     * @param method        要执行的方法对象
     * @param args          执行方法的参数
     * @param methodProxy   生成的代理方法
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("执行方法 " + method.getName() + " 前...");
        Object result = methodProxy.invoke(target, args); // 通过代理对象调用方法
        System.out.println("执行方法 " + method.getName() + " 后...");
        return result;
    }
}

在这里插入图片描述

3.main

package com.kang.spring;

import com.kang.spring.cglib.CalculatorInterceptor;
import com.kang.spring.pojo.Calculator;
import net.sf.cglib.proxy.Enhancer;

/**
 * @Author Emperor Kang
 * @ClassName CglibMain
 * @Description TODO
 * @Date 2023/12/27 10:07
 * @Version 1.0
 * @Motto 让营地比你来时更干净
 */
public class CglibMain {
    public static void main(String[] args) {
        Calculator calculator = new Calculator();

        // 创建一个Enhancer对象,用于生成代理类   enhancer:增强器
        Enhancer enhancer = new Enhancer();
        /**
         * 需要注意的是,由于CGlib动态代理是通过生成被代理类的子类来实现代理的功能,因此,如果被代理类是final类,那么就无法生成其子类,也就无法使用CGlib动态代理。
         */
        enhancer.setSuperclass(Calculator.class); // 设置父类
        enhancer.setCallback(new CalculatorInterceptor(calculator)); // 设置方法拦截器

        // 生成代理类并创建代理对象
        Calculator proxy = (Calculator) enhancer.create();

        // 调用代理对象的方法
        int result = proxy.add(1, 2);
        System.out.println("result = " + result);

        result = proxy.sub(3, 2);
        System.out.println("result = " + result);
    }
}

执行结果

执行方法 toString 前...
执行方法 toString 后...
执行方法 add 前...
执行方法 toString 前...
执行方法 toString 后...
执行方法 add 后...
result = 3
执行方法 sub 前...
执行方法 toString 前...
执行方法 toString 后...
执行方法 sub 后...
result = 1

jdk动态代理和cglib动态代理的区别

区别:

  1. JDK动态代理只能代理实现了接口的类,而不能代理普通的类。而CGlib动态代理可以代理普通的类。
  2. JDK动态代理使用Java的反射机制来生成代理类,而CGlib动态代理使用ASM框架来生成代理类,因此CGlib动态代理的效率比JDK动态代理要高。
  3. JDK动态代理生成的代理类是在内存中动态生成的类,而CGlib动态代理生成的代理类是在磁盘上写入文件,然后再加载到内存中的类。因此,CGlib动态代理的代理类在生成时需要更多的时间和空间。

CGlib动态代理示例:

首先,我们需要引入cglib的依赖,比如在Maven中可以添加以下依赖:

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

接下来,我们定义一个接口:

public interface UserService {
    void addUser(String username, String password);
}

然后,我们实现这个接口:

public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String username, String password) {
        System.out.println("Add user: " + username + " with password: " + password);
    }
}

现在,我们要使用CGlib动态代理来代理这个实现类。首先,我们定义一个代理类:

public class UserServiceProxy implements MethodInterceptor {
    private Object target;

    public Object getInstance(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @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;
    }
}

在这个代理类中,我们实现了MethodInterceptor接口,这个接口有一个方法intercept,这个方法就是我们要在代理方法执行前后添加的逻辑。在这个方法中,我们使用了MethodProxy类来执行实际的方法调用。

在getInstance方法中,我们使用Enhancer类来创建代理对象。其中,setSuperclass方法用来设置代理对象的父类,setCallback方法用来设置代理对象的拦截器。

现在,我们可以使用代理对象来调用addUser方法了:

public class Main {
    public static void main(String[] args) {
        UserService userService = (UserService) new UserServiceProxy().getInstance(new UserServiceImpl());
        userService.addUser("John", "123456");
    }
}

这个示例中,我们通过代理类UserServiceProxy来代理实现类UserServiceImpl,并且在代理方法执行前后添加了逻辑,最终成功输出了方法调用前后的信息。

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

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

相关文章

c语言:用指针输入两个数组|练习题

一、题目 利用指针&#xff0c;输入两个数组 如图&#xff1a; 二、代码截图【带注释】 三、源代码【带注释】 #include <stdio.h> int main() { int a[50]; int b[50]; int *paa,*pbb; //输入第一组数组 printf("请输入第一组5个数字&#xff1a;…

创建VLAN及VLAN间通信

任务1、任务2、任务3实验背景&#xff1a; 在一家微型企业中&#xff0c;企业的办公区域分为两个房间&#xff0c;一个小房间为老板办公室&#xff0c;一个大房间为开放办公室&#xff0c;财务部和销售部的员工共同使用这个办公空间。我们需要通过VLAN的划分&#xff0c;使老板…

世微 DW01 4.2V锂电池保护电路芯片 专业电源管理芯片

一、 描述 DW01A 是一个锂电池保护电路&#xff0c;为避免锂电池因过充电、过放电、电流过大导致电池寿命缩短或电池被损坏而设计的。它具有高精确度的电压检测与时间延迟电路。 二、 主要特点 工作电流低&#xff1b; 过充检测 4.3V&#xff0c;过充释放 4.05V&#xff1b; 过…

Android Matrix剪切clipPath缩放scale图片postTranslate圆形放大镜,Kotlin(1)

Android Matrix剪切clipPath缩放scale图片postTranslate圆形放大镜&#xff0c;Kotlin&#xff08;1&#xff09; 实现查看图片的放大镜&#xff0c;放大镜随着手指在屏幕上的移动&#xff0c;放大镜里面展示手指触点为中心、半径长度的圆形放大后的图片。 剪切出一块圆形Path…

BIOS:计算机中的特洛伊木马

内容概述&#xff1a; 由于主板制造商在计算机启动时用来显示品牌徽标的图像分析组件相关的问题&#xff0c;多个安全漏洞&#xff08;统称为 LogoFAIL&#xff09;允许攻击者干扰计算机设备的启动过程并安装 bootkit。x86 和 ARM 设备都面临风险。主板固件供应链安全公司 Bin…

记一次Oracle Cloud计算实例ssh恢复过程

#ssh秘钥丢失# &#xff0c; #Oracle Cloud# 。 电脑上的ssh秘钥文件不知道什么时候丢失了&#xff0c;直到用的时候才发现没有了&#xff0c;这下可好&#xff0c;Oracle Cloud的计算实例连不上了&#xff0c;这个实例只能通过ssh连接上去&#xff1a; 以下是解决步骤&#x…

时隔五天,重温Redis基础总结

目录 字符串操作命令 Redis 字符串类型常用命令SET key value 设置指定key的值 ​编辑GET key 获取指定key的值 ​编辑SETEX key seconds value 设置指定key的值&#xff0c;并将 key 的过期时间设为 seconds 秒 SETNX key value 只有在key不存在时设置key的值 哈希操作命…

【话题】ChatGPT等大语言模型为什么没有智能2

我们接着上一次的讨论&#xff0c;继续探索大模型的存在的问题。正巧CSDN最近在搞文章活动&#xff0c;我们来看看大模型“幻觉”。当然&#xff0c;本文可能有很多我自己的“幻觉”&#xff0c;欢迎批评指正。如果这么说的话&#xff0c;其实很容易得出一个小结论——大模型如…

kubeadm搭建k8s单节点集群——适合中小企业的业务集群

实验条件&#xff1a; master 20.0.0.17 2核4G docker、kubelet、kubeadm、kubectl、fannel node1 20.0.0.37 2核4G docker、kubelet、kubeadm、kubectl、fannel node2 20.0.0.47 2核4G docker、kubelet、kubeadm、kubectl、fannel slave:harbor 仓库节点 20.0.0.…

mysql基础-表操作

环境&#xff1a; 管理工具&#xff1a;Navicat 数据库版本&#xff1a;5.7.37 mysql的版本&#xff0c;我们可以通过函数&#xff0c;version()进行查看&#xff0c;本次使用的版本如下&#xff1a; 目录 1.管理工具 1.1创建表 1.2.修改表名 1.3.复制表 1.4.删除表 2…

计算机毕业论文内容参考|基于Apriori算法的门诊药物推荐系统的设计与实现

文章目录 摘要:前言相关技术与方法介绍系统分析系统设计系统实现系统测试与优化总结与展望摘要: 本文详细介绍了一种基于Apriori算法的门诊药物推荐系统的设计与实现。该系统利用Apriori算法挖掘患者就诊记录中的药物关联规则,为医生提供药物推荐,从而优化治疗方案。文章首…

mysql基础-字段类型及几点讨论事项

目录 1.主要字段类型 1.1整数 1.2.浮点数类型 1.3.文本类型 1.4.日期与时间类型 2.讨论几点有关字段类型的事 2.1 时间类型字段用字符还是datetime&#xff1f; 2.2 时间类型字段如何设置随时间更新&#xff1f; 2.3 字段长度够用即可 2.4 小数的字段类型选择&#xf…

月薪过万,突破“创业失败+外卖小哥”标签,他这样从什么都不懂晋升为测试团队骨干~

何人年少不轻狂,一身疲惫一人扛。 只有走出学校的大门独自在社会上闯荡一番后才能够切身体会这句话的含义。 哪个少年没有志比天高的崇高的理想&#xff0c;然而现实却是命比纸薄的真实写照。任何一个未正式踏入社会的青年面对未来都是充满激情的&#xff01;真正走向社会才发…

十大排序的个人总结之——冒泡排序、插入排序

同样&#xff0c;这两几乎也是被淘汰了的算法&#xff0c;尽管它们是稳定的&#xff0c;但是时间复杂度没人喜欢&#xff0c;了解一下就好&#xff0c;没啥好说的&#xff0c;注意最后一句话就行了 一&#xff0c;冒泡排序 1. 算法步骤 共n-1趟&#xff0c;谁两敢冒泡就换了…

vue保姆级教程----深入了解 Vue Router的工作原理

&#x1f4e2; 鸿蒙专栏&#xff1a;想学鸿蒙的&#xff0c;冲 &#x1f4e2; C语言专栏&#xff1a;想学C语言的&#xff0c;冲 &#x1f4e2; VUE专栏&#xff1a;想学VUE的&#xff0c;冲这里 &#x1f4e2; CSS专栏&#xff1a;想学CSS的&#xff0c;冲这里 &#x1f4…

2024年最新版 Ubuntu 20+ 上安装 Docker

2024年最新版 Ubuntu 20 上安装 Docker 一、前言 在网上搜了一些安装docker的方法&#xff0c;怎么说感觉良莠不齐&#xff0c;参考GPT整理一下来自GPT4这边的安装docker的解决方法&#xff0c;实测能用》2024.1.2&#xff0c;大概耗时7分钟左右&#xff08;在没报错的情况下…

深度学习|2.4 梯度下降

如上图&#xff0c; J ( w , b ) J(w,b) J(w,b)是由w和b两个参数共同控制的损失函数&#xff0c;损失是不好的东西&#xff0c;所以应该求取合适的w和b使得损失最小化。 为了简单考虑&#xff0c;可以先忽略参数b。 斜率可以理解成在朝着x正方向移动单位距离所形成的损失值的变…

Springer build pdf乱码

在textstudio中编辑时没有错误&#xff0c;在editor manager生成pdf时报错。 首先不要改源文件&#xff0c;着重看你的上传顺序&#xff1a; 将.tex文件&#xff0c;.bst文件&#xff0c;.cls文件&#xff0c;.bib文件, .bbl文件的类型&#xff0c;在editor manager中是Item。…

大数据机器学习GAN:生成对抗网络GAN全维度介绍与实战

文章目录 大数据机器学习GAN&#xff1a;生成对抗网络GAN全维度介绍与实战一、引言1.1 生成对抗网络简介1.2 应用领域概览1.3 GAN的重要性 二、理论基础2.1 生成对抗网络的工作原理2.1.1 生成器生成过程 2.1.2 判别器判别过程 2.1.3 训练过程训练代码示例 2.1.4 平衡与收敛 2.2…

Python爬虫---selenium基本使用

为什么使用selenium&#xff1f; 使用urllib.request.urlopen()模拟浏览器有时候获取不到数据,所以使用selenium (1) selenium是一个用于web应用程序测试的工具 (2) selenium 测试直接运行在浏览器中&#xff0c;就像真正的用户在操作一样 (3) 支持通过各种driver (FirfoxDri…