动态代理详解

news2024/9/23 17:21:04

目录

一、动态代理_代理模式简介

二、动态代理_JDK动态代理 dynamic

三、动态代理_CGLib动态代理

四、JDK和CGLib动态代理的区别


一、动态代理_代理模式简介

        代理模式是23种设计模式之一。设计模式是前人总结的,在软件开发过程遇到常用问题的解决方案,常见的设计模式有单例模式、工厂模式、适配器模式等等。

        代理模式的作用是在不修改原对象的基础上增强该对象的方法。比如官方购买苹果手机不赠送充电头,此时京东平台作为苹果的代理商,可以在代理销售苹果手机时赠送充电头

        代理模式分为静态代理、动态代理。静态代理会生成一个代理类,动态代理不会生成代理类,直接生成代理对象。

二、动态代理_JDK动态代理 dynamic

        JDK动态代理是针对接口进行代理,所以我们要写被代理的接口和该接口的实现类。

// 被代理接口

package com.example.dynamic;

public interface Apple {

    // 卖产品
    String sell(double price);

    // 维修
    void repair();
}

// 被代理接口的实现类

package com.example.dynamic;

public class AppleImpl implements Apple{

    @Override
    public String sell(double price) {
        System.out.println("产品卖了" + price + "元");
        return "ipone14";
    }

    @Override
    public void repair() {
        System.out.println("苹果售后维修");
    }
    
}

// 代理方式类,定义被代理方法的增强方式

// 该类实现InvocationHandler接口,重写invoke方法,定义方法的增强方式

package com.example.dynamic;

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

// 代理方式类,定义被代理方法的增强方式
// 该类实现InvocationHandler接口,重写invoke方法,定义方法的增强方式
public class ShoppingProxy implements InvocationHandler{

    // 被代理对象
    private Apple apple;

    public ShoppingProxy(Apple apple){
        this.apple = apple;
    }

    /**
     * 定义原方法的增强方式
     * @param proxy 被代理对象
     * @param method 被代理对象
     * @param args 被代理对象调用的方法时,传入的参数
     * @return 方法的返回值
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 被代理对象执行的方法名
        String name = method.getName();
        if("sell".equals(name)){
            // 增强参数
            double price = (double) args[0]*0.9;
            // 执行方法
            Object result = method.invoke(apple, price);
            // 增强返回值
            return result + "和充电头";
        }
        else if("repair".equals(name)){
            // 增强方法流程
            System.out.println("专属客服为你服务!");
            return method.invoke(apple, args);
        }
        else{
            // 什么都不增强
            return method.invoke(name, args);
        }
    }
}

接下来测试一下:

package com.example.dynamic;

import java.lang.reflect.Proxy;

public class Test {
    public static void main(String[] args) {
        // 被代理对象
        Apple apple = new AppleImpl();
        // 代理方式对象
        ShoppingProxy shoppingProxy = new ShoppingProxy(apple);
        // 生成代理对象
        Apple appleJD = (Apple) Proxy.newProxyInstance(
            apple.getClass().getClassLoader(), // 类加载器
            apple.getClass().getInterfaces(), // 被代理接口
            shoppingProxy  // 代理方式对象
        );

        // 执行未增强的方法
        System.out.println("执行未增强的方法");
        String sell = apple.sell(6000);
        System.out.println(sell);
        apple.repair();

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

        // 执行增强后的方法
        System.out.println("执行增强后的方法");
        String sellProxy = appleJD.sell(6000);
        System.out.println(sellProxy);
        appleJD.repair();
    }
}

运行结果:

ok 啊,确实实现了动态代理模式,但是这个时基于JDK的动态代理模式,还是需要我们自己去写接口,接下来介绍一种基于CGLib的动态代理模式 

三、动态代理_CGLib动态代理

        CGLib动态代理简化了JDK动态代理的写法,JDK是针对接口代理,而CGLib是针对类代理。

我们得在pom.xml文件引进cglib的依赖,所以我们现在pom.xml加上以下代码

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

// 被代理类

package com.example.dynamic_cglib;

public class Apple {

    // 卖产品
    public String sell(double price){
        System.out.println("产品卖了"+price+"元");
        return "ipone13";
    }

    // 维修
    public void repair(){
        System.out.println("苹果售后维修");
    }
}

// 代理方式类,实现MethodInterceptor接口,重写intercept方法

package com.example.dynamic_cglib;

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;


public class ShoppingProxy implements MethodInterceptor{

    // 被代理对象
    private Apple apple;

    public ShoppingProxy(Apple apple){
        this.apple = apple;
    }

    /**
     * 定义原方法的增强方式
     * @param o 被代理对象
     * @param method 被代理对象调用的方法
     * @param args 被代理对象调用的方法时,传入的参数
     * @param methodProxy 底层生成的代理类的引用
     * @return 方法的返回值
     * @throws Throwable
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        String name = method.getName();
        if("sell".equals(name)){
            double price = (double) args[0]*0.8;
            Object result = method.invoke(apple, price);
            return result+"数据线";
        }
        else if("repair".equals(name)){
            System.out.println("专属客服为你服务!");
            return method.invoke(apple, args);
        }
        else{
            return method.invoke(apple, args);
        }
    }
}

ok ,再让我们写一个测试类,看看能否好使,注意这里生成代理对象就不是用JDK那个方法了

package com.example.dynamic_cglib;

import org.springframework.cglib.proxy.Enhancer;


public class Test {
    public static void main(String[] args) {
        // 被代理对象
        Apple apple = new Apple();
        // 代理方式
        ShoppingProxy shoppingProxy = new ShoppingProxy(apple);
        // 生成代理对象
        Apple appleTB = (Apple) Enhancer.create(Apple.class,shoppingProxy);

        // 执行未增强的方法
        System.out.println("执行未增强的方法");
        String sell = apple.sell(9000);
        System.out.println(sell);
        apple.repair();

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

        // 执行增强后的方法
        String sellProxy = appleTB.sell(9000);
        System.out.println(sellProxy);
        appleTB.repair();
    }
    
}

ok,看一下运行结果,也确实进行了动态代理。

四、JDK和CGLib动态代理的区别

         所以,通过上述两个例子我们可以知道关于JDK动态代理和CGLib动态代理的区别就是JDK是基于接口的,而CGLib是基于类的。

        所以,你学hui了吗?

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

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

相关文章

微服务框架 SpringCloud微服务架构 微服务保护 30 初识Sentinel 30.4 引入cloud-demo

微服务框架 【SpringCloudRabbitMQDockerRedis搜索分布式&#xff0c;系统详解springcloud微服务技术栈课程|黑马程序员Java微服务】 微服务保护 文章目录微服务框架微服务保护30 初识Sentinel30.4 引入cloud-demo30.4.1 引入cloud-demo30.4.2 微服务整合Sentinel30 初识Sent…

尝试使用CubeMX做stm32开发之十三:Clock Configuration(时钟树配置)

参考《STM32中文参考手册_V10》&#xff0c;研究CubeMX中有关时钟树配置。 一、系统时钟配置 三种不同的时钟源可被用于驱动系统时钟&#xff08;SYSCLK&#xff09;&#xff1a; HSI振荡器时钟HSE振荡器时钟PLL时钟 时钟源选择对应时钟配置寄存器&#xff08;RCC_CFGR&…

Android -- 每日一问:修改 SharedPreferences 后两种提交方式有什么区别?

知识点 SharedPreferences 类是一个接口类&#xff0c;真正的实现类是 SharedPreferencesImpl 。修改 SharedPreferences 需要获取它的 Editor&#xff0c;在对Editor进行put操作后&#xff0c;最后通过 commit 或者 apply 提交修改到内存和文件。当然有了两种都可以提交的方法…

Java进程线程介绍创建和执行销毁并理解线程安全和线程池 Native Method

目录1.进程和线程2.多线程的核心3.操作系统的多任务--以非常小的时间间隔交替执行4.native 修饰的方法5.Thread创建线程的两种方式1.普遍采用实现Runnable接口的方式2.继承Thread方式6.自定义线程用 new Thread(Runnable target) 启动源码分析6.1-new Thread(myThread)6.2对实例…

mysql8.0.21安装配置方法图文教程

记录了mysql 8.0.21 的安装配置方法&#xff0c;分享给大家。 一、下载 1、下载安装包 mysql下载路径 2、解压压缩包 3、在此目录下新建my.ini配置文件 [mysqld] # 设置 3306 端口 port3306 # 设置 mysql 的安装目录 basedirD:\mysql-8.0.21-winx64 # 设置 mysql 数据…

破案了!不会讲笑话不会作诗的chatGPT!

热出圈的chatGPT, 必须亲手试试热出圈的chatGPT, 必须亲手试试1 猜猜我是谁2 问网传图片李白风格注释代码3 写个程序看看4 帮我猜猜世界杯&#xff08;发了发了&#xff0c;偷笑脸&#xff09;5 知道李白吗&#xff1f;6 那你会写诗吗&#xff1f;6 那你讲脑经急转弯吗&#xf…

linux服务器安装docker(学习中)

linux服务器安装docker1、docker官网寻找官方文档1.1、卸载之前的docker1.2、安装yum工具类1.3、配置docker下载源的地址1.4、安装最新稳定版的docker1.5、启动docker1.6、docker镜像下载加速2、docker-卷-映射和挂载2.1、nginx1、docker官网寻找官方文档 然后根据官网文档进行…

高性能零售IT系统的建设08-9年来在互联网零售O2O行业抗黑产、薅羊毛实战记录及打法

前言 2012年左右转入互联网应用&#xff0c;由于本身在学校时就涉足过远程医疗影像中的DICOM安全领域这块&#xff0c;因此也是机缘巧合我进入互联网第一年就遇上了一次亿级的DDOS攻击以及千万级CC攻击短信系统的对抗。那时在公司一战成名&#xff0c;直接从team leader升到了主…

adb remount原理

1, 输入"fastboot flashing unlock" in adb ,waiting for the device 2, 输入"fastboot flashing unlock_critical"in adb ,waiting for the device 3, 输入"fastboot reboot" reboot the stb, press any key entering the boot mode 4, after…

tensorflow入门(三)tensorflow下神经网络参数的设置

参考 Tensorflow入门 - 云社区 - 腾讯云 神经网络中的参数是神经网络实现分类或回归问题中重要的部分。在tensorflow中&#xff0c;变量(tf.Variable)的作用就是保存和更新神经网络中的参数的。在tensorflow中&#xff0c;变量(tf.Variable)的作用就是保存和更新神经网络的参…

Educational Codeforces Round 123 (Rated for Div. 2) D. Cross Coloring

Problem - D - Codeforces 翻译&#xff1a; 有一张纸&#xff0c;可以用大小为&#x1d45b;&#x1d45a;:&#x1d45b;行和&#x1d45a;列的单元格表示。所有的细胞最初都是白色的。 &#x1d45e;操作已应用到工作表。他们的&#x1d456;-th可以描述如下: &#x1d4…

前端工程化项目的思考

这是一篇个人使用前端工程开发项目的思考&#xff0c;希望可以帮助到你。完全是一篇综合概念应该是很多东西&#xff0c;我也不清楚会有多少字&#xff0c;估计会对刚刚开始的人看起来比较迷&#xff0c;但也是没有办法的事情 1.前端脚本语言开发的作者我想应该也想不到js会发展…

Spark

1 Spark作业提交流程 2 Spark提交作业参数 1&#xff09;在提交任务时的几个重要参数 executor-cores —— 每个executor使用的内核数&#xff0c;默认为1&#xff0c;官方建议2-5个 num-executors —— 启动executors的数量&#xff0c;默认为2 executor-memory —— executor…

【OpenCV学习】第9课:形态学操作的应用-提取水平线丶垂直线

仅自学做笔记用,后续有错误会更改 理论 图像在进行形态学操作的时候&#xff0c; 可以通过自定义的结构元素实现结构元素对输入图像的一些对象敏感丶对另外一些对象不敏感&#xff0c; 这样就会让敏感的对象改变而不敏感的对象保留输出。 通过使用两个最基本的形态学操作 - 膨…

华为云服务-运维篇-弹性负载均衡

文章目录一、什么是负载均衡二、我们为什么需要负载均衡1、生活中需要它的类似场景2、生活场景中协调者&#xff08;负载均衡)作用3、协调者&#xff08;负载均衡)引入后的变化三、华为云平台-如何做负载均衡弹性负载均衡-ELB四、总结一、什么是负载均衡 负载均衡构建在原有网…

【数据挖掘】薪酬分段对应工作经验/学历画柱状图【招聘网站的职位招聘数据预处理】

文章目录一.需求背景1.1 需求分析二.数据处理(对给定职位&#xff0c;汇总薪酬分段对应工作经验要求数据&#xff0c;画柱状图&#xff1b;)2.1 事前准备2,1 处理开始三.数据处理(对给定职位&#xff0c;汇总薪酬分段对应学历要求数据&#xff0c;画柱状图&#xff1b;)四.附源…

吉林大学 超星慕课 高级语言程序设计 实验08 结构化程序设计(2022级)

本人能力有限&#xff0c;发出只为帮助有需要的人。 建议同学们自己写完后再进行讨论。 其中的代码均没能在oj上进行测试&#xff0c;因此可能有误&#xff0c;请谅解。 除此以外部分题目设计深度优先搜索&#xff0c;因此可以分别用递归和堆栈实现&#xff0c;堆栈方法为了…

JavaScript进阶教程——异步编程、封装Ajax

异步编程 什么是同步与异步&#xff1a; 同步&#xff1a;一件事没做完&#xff0c;只能等待&#xff0c;完成之后再去做另一件事 异步&#xff1a; 两件事可以同时进行 前端开发中最常见的两种异步情况&#xff1a; ajax: 向后台请求数据计时器&#xff1a; setInterval se…

Python学习基础笔记四十一——sys模块

sys模块是与Python解释器交互的一个接口。 sys.argv 命令行参数List&#xff0c;第一个元素是程序本身路径 sys.exit(n) 退出程序&#xff0c;正常退出时exit(0),错误退出sys.exit(1) sys.version 获取Python解释程序的版本信息 sys.path 返…

ARM Cortex M3处理器概述

Cortex-M3概述 2004年ARM发布作为新型Corex处理器内核系列首款的Cortex-M3处理器。 STM32系列基于专为高性能、低成本、低功耗的嵌入式应用专门设计的ARM Cortex-M内核。 STM32命名规则 STMF103xx系统结构 1.使用高性能的ARM Cortex-M3 32位RISC内核 2.工作频率为72MHZ 3.内…