JDKCGLIB实现动态代理

在这里插入图片描述

1.代理模式

代理模式是23中设计模式中的一种。简单来说就是我们使用代理对象来替代真实对象,这样就可以实现在不修改原目标对象的前提下,提供额外的功能操作,以达到扩展目标对象的功能。

代理模式的功能主要是扩展目标对象的功能,比如说在目标对象的某个方法执行前后你可以增加一些自定义的操作。
在这里插入图片描述

代理模式有静态代理和动态代理两种实现方式,我们先看静态代理的实现。

1.1 静态代理

学习动态代理之前,我们先掌握什么是静态代理。静态代理中,我们对目标对象的每个方法的增强都是手动完成的,非常不灵活 比如接口一但新增方法,目标对象和代理对象都要进行修改。实际应用场景非常少。

静态代理在编译时就将接口、实现类、代理类都变成了一个个实际的class文件

静态代理实现步骤

1.定义接口及实现类

2.创建一个代理类同样实现这个接口

3.将目标对象注入进代理类,然后在代理类的对应方法调用目标类中对应方法。这样我们就可以通过代理类屏蔽对目标对象的访问,并可以在目标方法执行前后做一些自己想做的事情。

代码实现

1.定义购买门票的接口

public interface ITicketService{
  boolean buyTicket()}

2.实现购买门票的接口

public class TicketService implements ITicketService{
  public boolean buyTicket(){
    boolean flag = false;
    System.out.println("购买ticket成功!");
    flag = true;
    return flag;
  }
}

3.创建代理类并同样实现购买门票的接口

public class TicketServiceProxy implements ITicketService{
  private final ITicketService ticketService;
  public TicketServiceProxy(ITicketService ticketService){
    this.ticketService = ticketService;
  }
  public boolean buyTicket(){
    //调用方法之前,我们可以添加代理类自己的操作,前置操作
    System.out.println("前置操作!");
    ticketService.buyTicket();
    //调用方法之后,我们可以添加代理类自己的操作,后置操作
     System.out.println("后置操作!");
    return true;
  }
}

代码测试

public class Main{
  public static void main(String args[]){
    ITicketService  ticketService = new TicketService();
    TicketServiceProxy ticketServiceProxy = new TicketServiceProxy(ticketService);
    ticketServiceProxy.buyTicket();
  }
}

1.2 动态代理

相比静态代理,动态代理更加灵活。我们不需要针对每个目标对象都单独创建一个代理类,并且也不需要我们必须实现接口,我们可以直接代理实现类(CGLIB动态代理机制)

从JVM的角度来说,动态代理是在运行时动态生成类字节码,并加载到JVM中

Spring AOP、RPC框架都使用到了动态代理

动态代理在业务系统的开发中用的较少,但在框架级别的开发中是一门必备的技能。

Java动态代理实现方式有很多,比如JDK动态代理、CGLIB动态代理等等。

1.2.1 JDK动态代理机制

在Java动态代理机制中 InvocationHandler 接口和 Proxy类是核心

Proxy 类中使用频率最高的方法是:newProxyInstance(),这个方法主要用来生成一个代理对象。

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

这个方法一共有3个参数

1.loader:类加载器,用于加载代理对象

2.interfaces:被代理类实现的一些接口

3.h :实现了InvocationHandler接口的对象

要实现动态代理,要必须实现InvocationHandler来自定义处理逻辑。当我们的动态代理对象调用一个方法的时候,这个方法的调用就会被转发到实现InvocationHandler接口类的invoke方法来调用

public interface InvocationHandler{
  /** 
  当你使用代理对象调用方法的时候实际会调用这个方法
  **/
  public Object invoke(Object proxy,Method method,Object[]args)throws Throwable;
}

invoke()方法有下面三个参数

1.proxy:动态生成的代理类

2.method:与代理对象调用的方法相对应

3.args:当前method方法的参数

也就是说你通过Proxy类的newProxyInstance()创建的代理对象在调用方法的时候,实际会调用到实现InvocationHandler接口的类的invoke方法。你可以在invoke方法中自定义处理逻辑,比如在方法执行前后做什么事情。

JDK动态代理的使用步骤

1.定义一个接口及实现类

public interface ITicketService {

    /**
     * 购买门票
     * @return
     */
    public boolean buyTicket();

}
public class TicketService implements ITicketService {
    @Override
    public boolean buyTicket() {
        boolean flag  = false;
        System.out.println("购买门票成功");
        flag = true;
        return flag;
    }
}

2.自定义InvocationHandler并重写invoke方法,在invoke方法中我们会调用原生方法(被代理类的方法)并自定义以下处理逻辑。

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

/**
 * @author beien
 * @date 2023-09-04 15:51
 * Copyright© 2023 beien
 */
public class ProxyInvocationHandler implements InvocationHandler {
    private  Object object = null;

    public ProxyInvocationHandler(Object object){
        this.object = object;
    }

    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        System.out.println("前置操作");
        Object obj = method.invoke(object,null);
        System.out.println("后置操作");
        return obj;
    }
}

3.通过Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)方法创建代理对象

public class JdkDynamicProxyFactory{
  public static Object getProxy(Object target){
    return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new ProxyInvocationHandler(target));
  }
}

代码测试

 ITicketService ticketService =(ITicketService)JdkDynamicProxyFactory.getProxy(new TicketService());
        ticketService.buyTicket();
1.2.2 CGLIB动态代理机制

JDK动态代理有一个缺点只能代理实现了接口的类,没有实现接口的类无法进行代理。

CGLIB框架则解决了该问题。

CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。除了CGLIB包,脚本语言例如Groovy和BeanShell,也是使用ASM来生成java的字节码。当然不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。很多知名框架都是用CGLIB 例如Spring中的AOP模块,如果目标对象实现了接口,则默认采用JDK的动态代理,否则采用CGLIB动态代理。

在CGLIGB动态代理机制中,MethodInterceptor接口和Enhancer类是核心。

你需要自定义MethodInterceptor并重写intercept方法,intercept用于拦截增强被代理类的方法

public interface MethodInterceptor extends Callback{
  //拦截被代理中的方法
  public Object intercept(Object obj,java.lang.reflect.Method method,Object[]args,MethodProxy methodProxy)throws Throwable
}

1.obj:被代理的对象

2.metod:被拦截的方法

3.args:方法入参

4.methodProxy:用于调用原始方法

可以通过Enhancer类来获取被代理类,当代理类调用方法的时候,实际调用的是MethodInterceptor中的intercept方法

1.2.2.1 CGLIB动态代理使用步骤

1.定义一个类;

2.自定义MethodInterceptor并重写intercept方法,intercept用于拦截增强被代理类的方法,和JDK动态代理中的invoke方法类似;

3.通过Enhancer类的create()创建代理类。

CGLIB属于一个开源项目,我们在使用它之前必须引入jar

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

代码实现

1.实现一个票务系统购买门票功能

public class TicketService{
  public boolean buyTicket(){
    boolean flag = false;
    System.out.println("购买ticket成功!");
    flag = true;
    return flag;
  }
}

2.自定义MethodInterceptor(方法拦截器)

public class ProxyMethodInterceptor implements MethodInterceptor{
     /**
     *
     * @param obj 被代理对象(需要增强的类)
     * @param method 被拦截的方法(需要增强的方法)
     * @param args 方法入参
     * @param proxy 用于调用原始方法
     * as many times as needed
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        //调用方法之前,前置方法
        System.out.println("前置操作:"+method.getName());
        Object object = proxy.invokeSuper(obj,args);
        //调用方法之后,后置方法
        System.out.println("后置操作:"+method.getName());
        return object;
    }
}

3.获取代理类

public class CglibDynamicProxyFactory{
  public static Object getProxy(Class<?> clazz){
    //创建动态代理增强类
    Enhancer enhancer = new Enhancer();//cglib
    //设置类加载器
    enhancer.setClassLoader(clazz.getClassLoader());
    //设置被代理类
    enhancer.setSuperclass(clazz);
    //设置方法拦击
    enhancer.setCallback(new ProxyMethodInterceptor());
    //创建代理类
    return enhancer.create();
  }
}

4.代码测试

TicketService ticketService = (TicketService)CglibDynamicProxyFactory.getProxy(TicketService.class);
        ticketService.buyTicket();

2.关注我

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

35、matlab设置字体、查看工具包版本、窗口默认布局和程序发布

1、matlab设置字体 1&#xff09;找到预设并点击预设 2&#xff09;设置流程&#xff1a;字体——>自定义——>编辑器——>选择字体及格式——>确定 如图序号所示 2、matlab查看工具包版本&#xff1a;ver命令 1&#xff09;命令行窗口输入命令 即可查看工具包…

Halcon 多相机统一坐标系

小杨说事-基于Halcon的多相机坐标系统一原理个人理解_多相机标定统一坐标系-CSDN博客 一、概述 最近在搞多相机标定等的相关问题&#xff0c;对于很大的场景&#xff0c;单个相机的视野是不够的&#xff0c;就必须要统一到一个坐标系下&#xff0c;因此我也用了4个相机&#…

指挥中心操作台在轨道交通中的作用

随着城市化进程的加速和交通需求的日益增长&#xff0c;轨道交通作为高效、便捷、环保的出行方式&#xff0c;在城市交通体系中扮演着越来越重要的角色。而在轨道交通系统的运行中&#xff0c;指挥中心操作台则发挥着至关重要的作用。本文将详细探讨指挥中心操作台在轨道交通中…

龙气万象-大麗和和「腾龙图」翡翠高级珠宝系列

泠泠龙吟,气象万千。大麗和和诚献「腾龙图」翡翠高级珠宝系列,以贯穿上下五千年的龙图腾纹样为主轴,融入现代高级珠宝设计,集雅列珍,见证龙纹嬗变,探讨美学的传承与创新。 这一系列从探讨构思到惊艳呈现历时20个月,龙纹原型跨越从战国早期至明清两代的漫长岁月,设计团队自青铜…

ttkbootstrap的icon图标自定义

前言 在使用ttkbootstrap库时&#xff0c;发现icon参数使用报错&#xff0c;错误代码 root ttk.Window(themename"superhero",size(1400, 700),resizable(True, True),iconphoto"1.png" )结果报错&#xff1a;iconphoto path is bad; using default ima…

STM32存储左右互搏 模拟U盘桥接SPI总线FATS读写FLASH W25QXX

STM32存储左右互搏 模拟U盘桥接SPI总线FATS读写FLASH W25QXX STM32的USB接口可以模拟成为U盘&#xff0c;通过FATS文件系统对连接的存储单元进行U盘方式的读写。 这里介绍STM32CUBEIDE开发平台HAL库模拟U盘桥接SPI总线FATS读写W25Q各型号FLASH的例程。 FLASH是常用的一种非易失…

【八股系列】react里组件通信有几种方式,分别怎样进行通信?

文章目录 1. props传递&#xff08;父向子通信&#xff09;:2. 回调函数作为props&#xff08;子向父通信&#xff09;:3. Context API&#xff1a;4. Redux或MobX等状态管理库&#xff1a;4.1 Redux使用示例 5. refs&#xff1a; 1. props传递&#xff08;父向子通信&#xff…

netty内存马探究

0x01 前言 基于netty动态创建pipeline的特性&#xff0c;其内存马的构造思路与tomcat有一定的区别&#xff0c;目前网上有关netty内存马的文章都围绕CVE-2022-22947和XXL-JOB两种场景展开&#xff0c;并未对其做更为详细的分析。本文就以上述两种场景为始&#xff0c;尝试从源码…

打造智能饲料工厂:全自动生产线设备

全自动饲料生产线设备实现了从原料投放到成品包装的全程自动化操作。通过传感器和控制系统&#xff0c;设备能够精确控制原料的配比和混合均匀度&#xff0c;确保饲料的质量稳定可靠。同时&#xff0c;自动化生产线还具备智能调节功能&#xff0c;能够根据生产需求自动调节生产…

四季变换,制氮机使用注意事项

随着四季的轮回变换&#xff0c;大自然展现着不同的风貌。对于制氮机而言&#xff0c;季节的变换同样会带来不同的使用挑战和注意事项。本文将为您揭示四季变换对制氮机使用的影响&#xff0c;帮助您更好地掌握制氮机的季节使用须知。 春季 温湿度变化&#xff1a;春季温湿度逐…

Shell脚本从入门到实战

一、概述 shell 是一个命令行解释器&#xff0c;它接受应用程序、用户命令&#xff0c;然后调用操作系统内核。 shell 还是一个功能强大编程语言&#xff0c;易调试&#xff0c;易编写&#xff0c;灵活性强。 二、mac 怎么重启docker 1.如何重启 Docker on Mac 在 macOS 上…

Flutter笔记:关于WebView插件的用法(上)

Flutter笔记 关于WebView插件的用法&#xff08;上&#xff09; - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this article:htt…

SpringMVC:拦截器(Interceptor)

1. 简介 拦截器&#xff08;Interceptor&#xff09;类似于过滤器&#xff08;Filter&#xff09; Spring MVC的拦截器作用是在请求到达控制器之前或之后进行拦截&#xff0c;可以对请求和响应进行一些特定的处理。拦截器可以用于很多场景下&#xff1a; 1. 登录验证&#xf…

Qt Creator 输入中文

安装fcitx-libs-qt或fcitx-libs-qt5 查看系统自带libfcitxplatforminputcontextplugin.so位置 dpkg -L fcitx-frontend-qt5 | grep .so文件位于 /usr/lib/x86_64-linux-gnu/qt5/plugins/platforminputcontexts/libfcitxplatforminputcontextplugin.so将上述文件拷贝至Qt安装…

燕之屋被授予“国家食品企业质量安全检测技术示范中心(无锡)分中心”牌匾

5月27日&#xff0c;江苏教育界与产业界对话对接系列活动——新型食品与大健康产业协同创新发展大会在江南大学召开。会上&#xff0c;燕之屋荣获“国家食品企业质量安全检测技术示范中心&#xff08;无锡&#xff09;分中心”授牌&#xff0c;燕之屋副总经理、研发技术总监范群…

Linux内网数据代理与数据映射实验

背景介绍 有两台主机&#xff0c;其中一台可以联网&#xff0c;而另一台只能与联网主机&#xff0c;尝试通过配置&#xff0c;使该主机也能正常上网&#xff0c;这常应用于企业内部不联网的服务器&#xff0c;需要安全可靠或临时上网的场景&#xff0c;借助另一台可以上网的内…

Docker:认识Docker Bridge网络

文章目录 为什么需要网络管理Docker网络架构简介CNMLibNetwork驱动Docker网络管理命令网络命令基本操作网络详解docker Bridge网络 容器之间的网络通信DNS解析 为什么需要网络管理 容器的网络默认会与宿主机器以及其他的容器相互隔离&#xff0c;但是还需要考虑到下面的这些问…

微软将 AI 工具和软件的研发工作外包给 OpenAI

微软将 AI 工具和软件的研发工作外包给 OpenAI 科技媒体 CNBC 报道称&#xff0c;微软正计划将其人工智能&#xff08;AI&#xff09;工具和软件的开发工作外包给OpenAI&#xff0c;这对竞争对手谷歌来说无疑是一个利好消息。 Okta首席执行官Todd McKinnon表示&#xff0c;微…

GPT-4o多模态大模型的架构设计

GPT-4o&#xff1a;大模型风向&#xff0c;OpenAI大更新 OpenAI震撼发布两大更新&#xff01;桌面版APP与全新UI的ChatGPT上线&#xff0c;简化用户操作&#xff0c;体验更自然。同时&#xff0c;全能模型GPT-4o惊艳亮相&#xff0c;跨模态即时响应&#xff0c;性能卓越且性价比…

vue防止多次点击

1.新建js 在util文件夹下新建一个preventReClick.js文件&#xff0c;名字可自定义&#xff0c;文件夹也可以根据你自己的目录来 2.js文件中通过自定义指令的形式封装一个名为preventReClick的指令 import Vue from vue // 通过自定义指令的形式封装一个名为preventReClick的…