动态代理详解

简介: 【2月更文挑战第7天】


一、动态代理_代理模式简介
代理模式是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();
}

}
运行结果:
image.png

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

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

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


cglib
cglib
3.3.0

// 被代理类

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,看一下运行结果,也确实进行了动态代理。
image.png

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

    所以,你学hui了吗?

相关文章
|
数据采集 机器学习/深度学习 数据可视化
ICCV 2023|基于ViT的高效视频识别UniFormerV2开源,K400首次90%准确率!
ICCV 2023|基于ViT的高效视频识别UniFormerV2开源,K400首次90%准确率!
410 0
|
7月前
|
前端开发 JavaScript
什么是深拷贝;深拷贝和浅拷贝有什么区别;深拷贝和浅拷贝有哪些方法(详解)
浅拷贝适用于只复制对象的第一层属性,且这些属性不是引用类型。深拷贝适用于需要完全独立的副本,包括对象和数组的嵌套结构。选择哪种拷贝方式取决于你的具体需求和场景。 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
SecureCRT 通过xftp命令行上传文件到服务器上
SecureCRT 通过xftp命令行上传文件到服务器上
601 1
|
存储 Java C++
Synchronized底层原理
Synchronized底层原理
186 2
|
机器学习/深度学习 PyTorch 数据处理
数据增强与 DataLoader:提升模型泛化能力的策略
【8月更文第29天】在深度学习中,数据的质量和数量对于模型的性能至关重要。数据增强是一种常用的技术,它通过对原始数据进行变换(如旋转、缩放、裁剪等)来生成额外的训练样本,从而增加训练集的多样性和规模。这有助于提高模型的泛化能力,减少过拟合的风险。同时,`DataLoader` 是 PyTorch 中一个强大的工具,可以有效地加载和预处理数据,并支持并行读取数据,这对于加速训练过程非常有帮助。
1129 1
|
消息中间件 缓存 NoSQL
Redis stream 用做消息队列完美吗
Redis Stream 是 Redis 5.0 版本中引入的一种新的数据结构,它用于实现简单但功能强大的消息传递模式。 这篇文章,我们聊聊 Redis Stream 基本用法 ,以及如何在 SpringBoot 项目中应用 Redis Stream 。
Redis stream 用做消息队列完美吗
|
存储 安全 数据安全/隐私保护
DP读书:鲲鹏处理器 架构与编程(九)鲲鹏920处理器片上系统
DP读书:鲲鹏处理器 架构与编程(九)鲲鹏920处理器片上系统
651 0
|
机器学习/深度学习 人工智能 自然语言处理
Mac部署AIGC图片生成服务——基于stable-diffusion
AIGC即人工智能内容生成,是目前非常火的一个概念。随着各种大模型的问世,通过AI来生成内容的能已经越来越强大。本文将从应用实践方面进行介绍如何在自己的PC电脑上部署一个强大的AI图片生成服务。
671 0
Mac部署AIGC图片生成服务——基于stable-diffusion
|
Kubernetes 容器
Kubernetes—安装2022新版ingress-nginx步骤
Kubernetes—安装2022新版ingress-nginx步骤
383 0
|
数据采集 机器学习/深度学习 存储
ETL工程师必知的数据清洗方法【最全】
ETL工程师必知的数据清洗方法【最全】