【java设计模式】——代理设计模式,两种举例说明

代理设计模式

1.介绍

Spring 框架中AOP底层使用动态代理设计模式。通过学习动态代理设计模式可以很好的理解Spring框架AOP底层

代理模式(Proxy)是GoF23种设计模式之一。所谓代理模式是指客户端并不直接调用实际的对象,而是通过调用代理,来间接的调用实际的对象。

image-20211224090242901

代理设计模式包括:静态代理和动态代理。

静态代理:代理对象由程序员自己编写,里面提供硬编码方式来访问调用者

动态代理:

  • JDK动态代理:一种基于接口的代理实现
  • Cglib动态代理:一种基于继承的代理实现

代理设计模式优点:

  • 保护代理对象。客户不能直接访问真实对象。只能通过代理对象进行访问
  • 可扩展性。复合开闭原则,可以实现在不修改真实代理对象情况下,进行扩展。
  • 对于一些访问困难的真实对象,可以使用代理对象,更轻松的访问。

代理设计模式缺点:

  • 系统复杂性增加。类的数目增加,代码相对更复杂一点。
  • 性能降低。每次访问真实对象,只能通过代理对象。

在这里插入图片描述

2.静态代理示例

以房屋买卖为例子(简易流程),客户想要买房,找中介,由中介带看房东的房子。

客户看好了,想要买,房东也同意买了,中介收取客户的中介费。

daili

创建房东(真实对象)

新建com.spring.proxy.staticproxy.Fangdong

public class Fangdong {
  public void bigHouse(){
    System.out.println("房东的大房子");
   }
}

创建中介类(代理对象)

新建com.spring.proxy.staticproxy.Zhongjie

public class Zhongjie {
  public void zhongjie(){
    System.out.println("约谈客户,进行带看");// 扩展功能
    Fangdong fangdong = new Fangdong();
    fangdong.bigHouse();
    System.out.println("交易成功,收取中介费"); // 扩展功能
   }
}

创建客户类(调用者)

com.spring.proxy.staticproxy.Kehu

public class Kehu {
  public static void main(String[] args) {
    Zhongjie zhongjie = new Zhongjie();
    zhongjie.zhongjie();
   }
}

3.动态代理示例

3.1JDK动态代理

JDK动态代理是基于接口来实现的,底层是基于Java 反射技术实现的。

创建接口

创建接口com.spring.proxy.jdkproxy.House

因为JDK动态代理要求调用者必须实现接口。所以先建立接口

public interface bigHouse {
    void bighose();
}

创建真实对象类

创建类com.spring.proxy.jdkproxy.Fangdong

/**
 * JDK动态代理强制要求:真实的被调用的类必须实现接口
 */
public class FangDong implements bigHouse{
    @Override
    public void bighose() {
        System.out.println("大房子");
    }
}

创建代理类

创建com.spring.proxy.jdkproxy.Zhongjie

/**
 * 在JDK动态代理底层是基于java的反射机制实现的。本质上就是方法的调用
 *
 * 负责调用真实对象方法的类,必须实现InvocationHandler接口
 */
public class ZhongJie implements InvocationHandler {

    private FangDong fangDong;

    public ZhongJie(FangDong fangDong) {
        this.fangDong = fangDong;
    }

    /**
     *
     * @param proxy 产生的代理对象
     * @param method 真实对象中被调用的方法对象
     * @param args 被调用方法的参数
     * @return 被调用方法的返回值,都当做invoke方法的返回值
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("带看");

        //坑:千万不要用invoke方法的第一个参数proxy,他是代理对象,而不是真实对象
        Object result = method.invoke(fangDong, args);

        System.out.println("中介费");

        return result;
    }
}

创建客户类

创建com.spring.proxy.jdkproxy.Kehu

public class KeHu {

    public static void main(String[] args) {


        /**
         * 参数1:ClassLoader   在当前程序中,所有类的类加载器都是同一个对象
         *       作用:让生成的代理对象立即生效,(invoke中的proxy对象)
         * 参数2:Class[],放接口,告诉编译器,调用的是哪个方法
         *       作用:数组中放哪个接口,接口中的方法对象就会传递给invoke中的method方法对象
         * 参数3:InvocationHandler对象,负责调用真实对象的处理类,就是ZhongJie
         */
        //动态代理:动态:不是就调用某个对象的方法,而是想调哪个对象都可以
        InvocationHandler zhongJie = new ZhongJie(new FangDong());
        bigHouse bigHouse = (bigHouse) Proxy.newProxyInstance(Kehu.class.getClassLoader(), 
                                                              new Class[]{bigHouse.class}, 																					zhongJie);
        bigHouse.bighose();
    }
}

第二例子
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 定义接口
interface Hello {
    void sayHello();
}

// 实现接口的类
class HelloImpl implements Hello {
    @Override
    public void sayHello() {
        System.out.println("Hello, World!");
    }
}

// 创建InvocationHandler实例,用于处理代理逻辑
class HelloProxy implements InvocationHandler {
    private Object target;

    public HelloProxy(Object target) {
        this.target = target; // 保存目标对象的引用
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before invoking"); // 在调用目标方法前执行一些逻辑
        Object result = method.invoke(target, args); // 调用目标对象的方法
        System.out.println("After invoking"); // 在调用目标方法后执行一些逻辑
        return result; // 返回结果
    }
}

public class Main {
    public static void main(String[] args) {
        // 创建目标对象
        Hello hello = new HelloImpl();

        // 创建代理对象
        Hello proxy = (Hello) Proxy.newProxyInstance(
                hello.getClass().getClassLoader(), // 使用目标对象的类加载器
                hello.getClass().getInterfaces(), // 获取目标对象实现的接口列表
                new HelloProxy(hello) // 指定InvocationHandler实例
        );

        // 调用代理方法
        proxy.sayHello();
    }
}

3.2Cglib动态代理

Cglig是基于继承的,是第三方提供的技术,需要导入jar包,并且。

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

创建房东类

/**
 * 真实被调用对象
 */
public class FangDong {
    public void bigHouse(){
        System.out.println("大房子");
    }
}

创建中介类

/**
 * 作用:调用真实对象的方法
 */
public class ZhongJie implements MethodInterceptor {


    /**
     *
     * @param o 目标对象:即FangDong对象
     * @param method 目标方法
     * @param objects 目标方法参数
     * @param methodProxy 生成的代理对象(即FangDong的子类)
     * @return 目标方法返回值
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("带看");
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("中介费");
        return  result;

    }
}

创建客户类

public class KeHu {

    public static void main(String[] args) {
        //所有方法都在Enhancer中
        Enhancer enhancer = new Enhancer();
        //1.setSuperClass(真实是哪个类)  真实是哪个类,根据这个类创建代理对象,即FangDong的子类
        enhancer.setSuperclass(FangDong.class);
        //2.setCallBack(对象)    设置由哪个类作为增强实现类
        enhancer.setCallback(new ZhongJie());
        //3.create()   创建代理对象,返回值为object类型,本质为代理对象
        FangDong fangDong = (FangDong) enhancer.create();
        fangDong.bigHouse();
    }

}

测试结果,发现出现异常,这是因为Java 17版本中的Java Platform Module System(java 9就开始有了)引起的,特别是强封装的实现。

Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @1376c05c

把代码原封不动的放在Java 11的环境中就可以使用。

如果必须要在Java17中使用添加JVM参数,表示允许使用未命名模块。

--add-opens java.base/java.lang=ALL-UNNAMED

使用CGLib实现动态代理需要引入相关的依赖库。在示例中,我们将使用cglib-nodep库来创建代理对象。下面是一个使用CGLib实现动态代理的示例代码:

首先,确保已添加CGLib的依赖。对于Maven项目,可以在pom.xml文件中添加以下依赖:

<dependencies>
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib-nodep</artifactId>
        <version>3.3.0</version>
    </dependency>
</dependencies>

然后,编写以下示例代码:

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

import java.lang.reflect.Method;

// 目标类
class Hello {
    public void sayHello() {
        System.out.println("Hello, World!");
    }
}

// 创建MethodInterceptor实例,用于处理代理逻辑
class HelloInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("Before invoking"); // 在调用目标方法前执行一些逻辑
        Object result = proxy.invokeSuper(obj, args); // 调用目标方法
        System.out.println("After invoking"); // 在调用目标方法后执行一些逻辑
        return result; // 返回结果
    }
}

public class Main {
    public static void main(String[] args) {
        // 创建Enhancer对象
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Hello.class); // 设置父类为目标类
        enhancer.setCallback(new HelloInterceptor()); // 设置回调对象

        // 创建代理对象
        Hello proxy = (Hello) enhancer.create();

        // 调用代理方法
        proxy.sayHello();
    }
}

在上述示例中,我们首先定义了一个Hello类作为目标类。然后,创建了一个HelloInterceptor类实现MethodInterceptor接口,用于在代理对象的方法执行前后进行处理。接下来,使用Enhancer类创建代理对象,并设置目标类和回调对象。最后,通过调用代理对象的方法来触发代理逻辑。

运行以上代码,输出结果将会是:

Before invoking
Hello, World!
After invoking

这就是一个使用CGLib实现的动态代理的例子。注意,在使用CGLib时,需要引入相应的依赖并设置目标类和回调对象。

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

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

相关文章

浅谈https

1.网络传输的安全性 http 协议&#xff1a;不安全&#xff0c;未加密https 协议&#xff1a;安全&#xff0c;对请求报文和响应报文做加密 2.对称加密与非对称加密 2.1 对称加密 特点&#xff1a; 加解密使用 相同 秘钥 高效&#xff0c;适用于大量数据的加密场景 算法公开&a…

深入了解Java Duration类,对时间的精细操作

阅读建议 嗨&#xff0c;伙计&#xff01;刷到这篇文章咱们就是有缘人&#xff0c;在阅读这篇文章前我有一些建议&#xff1a; 本篇文章大概6000多字&#xff0c;预计阅读时间长需要5分钟。本篇文章的实战性、理论性较强&#xff0c;是一篇质量分数较高的技术干货文章&#x…

Linux安装mysq 8.0服务端和客户端(亲测保成功)

1. 查看当前是否有已经安装好的mysql,先卸载 # 命令 rpm -qa|grep -i mysql# 结果显示 mysql-community-libs-5.7.42-1.el7.x86_64 mysql-community-common-5.7.42-1.el7.x86_64 mysql-community-libs-compat-5.7.42-1.el7.x86_64 mysql57-community-release-el7-10.noarch my…

perl脚本获取Windows系统常用路径信息

windows系统常用的路径,比如临时目录、资源文件夹、字体保存目录、应用程序数据存放目录等等。在日常操作的时候寻找略有不便。这里用perl写一个脚本&#xff0c;并把这些目录信息格式化为json&#xff0c;方便查找。如下是perl代码&#xff1a; #! /usr/bin/perl use v5.14; …

中国科技巨头:集体变革中 /华为鸿蒙 4:功能、模型、生态三大升级 |魔法半周报

我有魔法✨为你劈开信息大海❗ 高效获取AIGC的热门事件&#x1f525;&#xff0c;更新AIGC的最新动态&#xff0c;生成相应的魔法简报&#xff0c;节省阅读时间&#x1f47b; &#x1f525;资讯预览 中国科技巨头&#xff1a;集体变革中 华为鸿蒙 4&#xff1a;功能、模型、生…

2002-2021年全国各地级市环境规制18个相关指标数据

2002-2021年全国各地级市环境规制18个相关指标数据 1、时间&#xff1a;2002-2021年 2、来源&#xff1a;城市年鉴 3、指标&#xff1a;行政区划代码、地区、年份、工业二氧化硫排放量(吨)、工业烟粉尘排放量(吨)、工业废水排放量(万吨)、工业废水排放达标量(万吨)、工业二氧…

华为OD机试 - 攀登者1(Java JS Python C)

题目描述 攀登者喜欢寻找各种地图,并且尝试攀登到最高的山峰。 地图表示为一维数组,数组的索引代表水平位置,数组的元素代表相对海拔高度。其中数组元素0代表地面。 例如:[0,1,2,4,3,1,0,0,1,2,3,1,2,1,0],代表如下图所示的地图,地图中有两个山脉位置分别为 1,2,3,4,5…

SpringSecurity6 | 修改默认用户

✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉 🍎个人主页:Leo的博客 💞当前专栏: Java从入门到精通 ✨特色专栏: MySQL学习 🥭本文内容:SpringSecurity6 | 修改默认用户 📚个人知识库 :知识库,欢迎大家访问 学习参考…

一款带键扫的LED驱动电路

一、基本概述 TM1628A是一种带键盘扫描接口的LED&#xff08;发光二极管显示器&#xff09;驱动控制专用IC,内部集成有MCU数字接口、数据锁存器、LED 驱动、键盘扫描等电路。本产品质量可靠、稳定性好、抗干扰能力强。主要适用于家电设备(智能热水器、微波炉、洗衣机、空调、电…

ProEasy机器人案例:电池边包胶

如下图所示&#xff0c;对一个电池三边包边&#xff0c;因客户现场有很多规格电池的大小&#xff0c;所以就需要建立动态的工具坐标来实现适配所有种类的电池 程序如下&#xff1a;Ddome程序 function Speed(num) --速度设置 MaxSpdL(2000) --movl最大速度…

机器学习 sklearn 中的超参数搜索方法

✅作者简介&#xff1a;人工智能专业本科在读&#xff0c;喜欢计算机与编程&#xff0c;写博客记录自己的学习历程。 &#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&…

一个能让你涨薪5k的自动化测试小技巧项目流程优化

引言及概览 项目流程优化是一个持续过程&#xff0c;每个公司&#xff0c;每个团队情况不一样&#xff0c;总原则是&#xff1a;如果在项目过程中你感觉到某一点很别扭、很不爽、痛了&#xff0c;那么这就是优化点。 优化的手段是多样化的&#xff0c;如通过流程规范去约束、…

将图像增广应用于Mnist数据集

将图像增广应用于Mnist数据集 不用到cifar-10的原因是要下载好久。。我就直接用在Mnist上了&#xff0c;先学会用 首先我们得了解一下图像增广的基本内容&#xff0c;这是我的一张猫图片&#xff0c;以下为先导入需要的包和展示图片 import time import torch from torch im…

C++ STL容器与常用库函数

STL是提高C编写效率的一个利器 STL容器&#xff1a; 一、#include <vector> 英文翻译&#xff1a;vector &#xff1a;向量 vector是变长数组(动态变化)&#xff0c;支持随机访问&#xff0c;不支持在任意位置O(1)插入。为了保证效率&#xff0c;元素的增删一般应该在末尾…

测开速成考点

用户登录界面需要做哪些测试&#xff1f; ⑴功能&#xff1a;  正确的用户名&#xff0c;正确的密码&#xff0c;点击提交按钮&#xff0c;验证能否正确登录 是否跳转正确页面。  错误的用户名 或 密码&#xff0c;点击提交&#xff0c;提示相应的错误信息。  记住用户名…

JavaScript实现手写签名,可触屏手写,支持移动端与PC端双端保存

目录 1.HTML模板 2.获取DOM元素和定义变量 3.创建两个canvas元素&#xff0c;并设置它们的宽度和高度 4.绑定触摸事件&#xff1a;touchstart, touchmove, touchend和click 5.实现触摸事件回调函数&#xff1a;startDrawing, draw和stopDrawing 6.实现绘制线段的函数&…

C语言——输出菱形

法一&#xff1a; #include<stdio.h> #define N 7 //假设输出7层菱形 int main(){int i;//i控制第几行 int j;//j控制每一行空格的循环个数 int k;//k控制每一行*的循环次数 for(i1;i<4;i){//将图形分为两部分,前四行(第一部分) for(j1;j<4-i;j){//输出第i行的…

记录一下本地源码安装部署ThingsBoard可能踩到的坑

使用git下载源码后, 必须运行 mvn clean install -DskipTests这一步很重要, 有很多文件需要初始化, 如果直接放入idea可能存在各种问题, 最好是用命令行执行 初始化时, 可能报错停止, 这个一般是网络问题, 可以尝试修改maven镜像, 这是我成功构建的镜像 <!--阿里云仓库--…

膜结构建筑:未来体育可持续发展的绿色引擎

随着城市化的飞速发展&#xff0c;现代建筑迫切需要创新性的解决方案&#xff0c;而膜结构建筑以其独特的设计理念和可持续性特点&#xff0c;正在成为未来城市发展的重要引擎。本文将深入探讨膜结构建筑在可持续城市发展中的关键作用&#xff0c;包括其在节能减排、资源有效利…

“分割“安卓用户,对标iOS,鸿蒙崛起~

近期关于**“华为于明年推出不兼容安卓的鸿蒙版本”**的消息传出&#xff0c;引起了业界的热议关注。自从2019年8月&#xff0c;美国制裁下&#xff0c;华为不再能够获得谷歌安卓操作系统相关付费服务&#xff0c;如此情况下&#xff0c;华为“备胎”鸿蒙操作系统一夜转正。 华…