设计模式---代理模式

news2024/11/6 3:11:40

1. 简介

        代理模式(Proxy Pattern)是一种结构型设计模式,它能够为其他对象提供一个代理以控制对这个对象的访问。代理模式在不直接访问实际对象的情况下,提供了对目标对象的间接访问。通过引入一个代理对象来间接操作实际对象,可以在不改变实际对象代码的前提下,增加额外的功能操作,如访问控制、延迟初始化、日志记录、事务管理等。

代理模式通常分为几种类型:

  1. 静态代理:在代码编译时就已经确定代理类和目标类的关系。

  2. 动态代理:在代码运行时动态创建代理类。

    1. JDK动态代理:利用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口在运行时创建代理对象。

    2. CGLIB动态代理:使用CGLIB库在运行时生成目标类的子类作为代理对象(实现MethodInterceptor接口)。

2. 静态代理

        静态代理是一种编译时就确定代理关系的方式。在静态代理中,代理类和目标类的关系是固定的,需要事先定义好。

实现步骤:

  1. 定义一个接口,目标对象和代理对象都实现这个接口。

  2. 创建目标对象的实现类。

  3. 创建代理类,实现相同的接口,并在内部持有目标对象的引用。

  4. 在代理类的方法中调用目标对象的方法,并在调用前后进行额外的操作。

示例代码:

package xiaokai.proxy.staticproxy;

/**
 * Author:yang
 * Date:2024-09-30 13:58
 * Description:定义接口
 */
public interface IStaticProxyService {
    String sayHello();
}

package xiaokai.proxy.staticproxy;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * Author:yang
 * Date:2024-09-29 10:47
 * Description:基础方法实现类
 */
@Service
@Slf4j
public class DefaultService implements IStaticProxyService{

    @Override
    public String sayHello() {
        log.info("DefaultService sayHello");
        return "Hello ! ";
    }
}

/**
 * Author:yang
 * Date:2024-09-29 11:00
 * Description:代理服务--->对实现功能进行增强
 */
@Service
@Slf4j
public class ProxyService implements IStaticProxyService {

    @Autowired
    private DefaultService defaultService;

    @Override
    public String sayHello() {
        log.info("ProxyService sayHello");
        return defaultService.sayHello();
    }
}

测试:

@SpringBootTest(classes = ProxyApplication.class)
@RunWith(SpringRunner.class)
public class ProxyTest {

    @Autowired
    private ProxyService proxyService;

    @Test
    public void testProxy() {
        proxyService.sayHello();
    }
}

结果:对基础方法进行了增强

 ProxyService sayHello
 DefaultService sayHello

优点

  • 代理类和目标类的关系在编译时就已经确定,结构清晰。

缺点

  • 每一个目标类都需要一个代理类,如果有很多目标类,会导致代理类的数量大量增加,增加系统的复杂性。

3. 动态代理

        动态代理是在运行时创建代理对象的方式,不需要事先定义代理类。在Java中,动态代理通常是通过反射API实现的。

3.1 JDK

  1. 目标对象必须实现一个或多个接口。

  2. 创建一个实现了InvocationHandler接口的类。

  3. 使用Proxy.newProxyInstance方法在运行时动态创建代理对象。

示例代码:

package xiaokai.proxy.dynamicproxy.jdkservice;

/**
 * Author:yang
 * Date:2024-09-29 11:16]
 * Description:问候服务接口
 */
public interface IGreetingService {

    public String sayHello();
}


package xiaokai.proxy.dynamicproxy.jdkservice;

import lombok.extern.slf4j.Slf4j;

/**
 * Author:yang
 * Date:2024-09-29 11:17
 * Description:
 */
@Slf4j
public class GreetingService implements IGreetingService {

    @Override
    public String sayHello() {
        log.info("sayHello");
        return "1";
    }
}



package xiaokai.proxy.dynamicproxy.jdkservice;

import lombok.extern.slf4j.Slf4j;

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

/**
 * Author:yang
 * Date:2024-09-29 11:23
 */
@Slf4j
public class JDKProxyService implements InvocationHandler {

    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log.info("before invoke method: " + method.getName());
        Object result = method.invoke(target, args);
        log.info("after invoke method: " + method.getName());
        return result;
    }
}

测试:

package xiaokai.proxy.dynamicproxy;

import xiaokai.proxy.dynamicproxy.jdkservice.GreetingService;
import xiaokai.proxy.dynamicproxy.jdkservice.IGreetingService;
import xiaokai.proxy.dynamicproxy.jdkservice.JDKProxyService;

import java.lang.reflect.Proxy;

/**
 * Author:yang
 * Date:2024-09-29 14:59
 */
public class Main {
    public static void main(String[] args) {
        // 原始对象
        GreetingService original = new GreetingService();

        JDKProxyService proxyService = new JDKProxyService(original);

        // 创建代理对象
        IGreetingService instance = (IGreetingService) Proxy.newProxyInstance(original.getClass().getClassLoader(), original.getClass().getInterfaces(), proxyService);

        instance.sayHello();
    }
}

结果:

JDKProxyService - before invoke method: sayHello
GreetingService - sayHello
JDKProxyService - after invoke method: sayHello

优点

  • 可以在运行时动态创建代理对象,不需要为每个目标类编写代理类。

  • 目标类只需实现接口即可,代理类由JDK在运行时自动生成。

缺点

  • 只能代理实现了接口的类。

3.2 CGLIB

  1. 目标对象不需要实现接口,CGLIB通过生成目标对象的子类来实现代理。

  2. 创建一个实现了MethodInterceptor接口的类。

  3. 使用CGLIB的Enhancer类在运行时动态创建代理对象。

添加依赖:

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

示例代码:

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

import java.lang.reflect.Method;


/**
 * Author:yang
 * Date:2024-09-29 16:27
 */
public class CglibHandler implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("before invoke");
        // 使用invokeSuper来调用父类方法
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("after invoke");
        return result;
    }
}


public class CglibService{

    public void sayHello() {
        System.out.println("hello, I am CglibService");
    }
}

测试:


public class Main {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(CglibService.class);
        enhancer.setCallback(new CglibHandler());
        CglibService instance = (CglibService) enhancer.create();
        instance.sayHello();
    }
}

结果:

before invoke
hello, I am CglibService
after invoke

优点

  • 可以代理没有实现接口的类。

  • 通过继承目标类实现代理,不需要目标类实现接口。

缺点

  • 代理对象的创建比JDK动态代理稍微复杂一些。

  • 需要引入第三方库CGLIB。

4. 应用场景

  1. 访问控制:在访问实际对象之前进行权限检查。

  2. 延迟初始化:在实际需要时才创建对象,提高系统性能。

  3. 日志记录:在调用目标对象方法前后记录日志信息。

  4. 事务管理:在方法调用前后添加事务处理逻辑。

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

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

相关文章

https://www.aitoolpath.com/ 一个工具数据库,目前储存了有2000+各种工具。每日更新

AI 工具爆炸&#xff1f;别怕&#xff0c;这个网站帮你整理好了&#xff01; 哇塞&#xff0c;兄弟们&#xff01;AI 时代真的来了&#xff01;现在各种 AI 工具跟雨后春笋似的&#xff0c;噌噌噌地往外冒。AI 写作、AI 绘画、AI 代码生成……简直是要逆天啊&#xff01; 可是…

XSS | XSS 常用语句以及绕过思路

关注这个漏洞的其他相关笔记&#xff1a;XSS 漏洞 - 学习手册-CSDN博客 0x01&#xff1a;干货 - XSS 测试常用标签语句 0x0101&#xff1a;<a> 标签 <!-- 点击链接触发 - JavaScript 伪协议 --><a hrefjavascript:console.log(1)>XSS1</a> <!-- 字…

智能制造--EAP设备自动化程序

EAP是设备自动化程序&#xff08;Equipment Automation Program&#xff09;的缩写&#xff0c;他是一种用于控制制造设备进行自动化生产的系统。EAP系统与MES系统整合&#xff0c;校验产品信息&#xff0c;自动做账&#xff0c;同时收集产品生产过程中的制程数据和设备参数数据…

Spring MVC__入门

目录 一、SpringMVC简介1、什么是MVC2、什么是SpringMVC 二、Spring MVC实现原理2.1核心组件2.2工作流程 三、helloworld1、开发环境2、创建maven工程3、配置web.xml4、创建请求控制器5、创建springMVC的配置文件6、测试HelloWorld7、总结 一、SpringMVC简介 1、什么是MVC MV…

git 报错git: ‘remote-https‘ is not a git command. See ‘git --help‘.

报错内容 原因与解决方案 第一种情况&#xff1a;git路径错误 第一种很好解决&#xff0c;在环境变量中配置正确的git路径即可&#xff1b; 第二种情况 git缺少依赖 这个情况&#xff0c;网上提供了多种解决方案。但如果比较懒&#xff0c;可以直接把仓库地址的https改成ht…

【Kotlin基于selenium实现自动化测试】初识selenium以及搭建项目基本骨架(1)

导读大纲 1.1 Java: Selenium 首选语言1.2 配置一个强大的开发环境 1.1 Java: Selenium 首选语言 Java 是开发人员和测试人员进行自动化 Web 测试的首选 Java 和 Selenium 之间的协同作用受到各种因素的驱动,从而提高它们的有效性 为什么Java经常被认为是Selenium的首选语言 广…

记录一次出现循环依赖问题

具体的结构设计&#xff1a; 在上面的图片中&#xff1a; UnboundBlackVerifyChain类中继承了UnboundChain类。但是UnboundChain类中注入了下面三个类。 Scope(“prototype”) UnboundLinkFlowCheck类 Scope(“prototype”) UnboundUserNameCheck类 Scope(“prototype”) Un…

[linux 驱动]input输入子系统详解与实战

目录 1 描述 2 结构体 2.1 input_class 2.2 input_dev 2.4 input_event 2.4 input_dev_type 3 input接口 3.1 input_allocate_device 3.2 input_free_device 3.3 input_register_device 3.4 input_unregister_device 3.5 input_event 3.6 input_sync 3.7 input_se…

用网络分析仪测试功分器驻波的5个步骤

在射频系统中&#xff0c;功分器的驻波比直接关系到信号的稳定性和传输效率。本文将带您深入了解驻波比的测试方法和影响其结果的因素。 一、功分器驻波比 驻波(Voltage Standing Wave Ratio)&#xff0c;简称SWR或VSWR&#xff0c;是指频率相同、传输方向相反的两种波&#xf…

.NET Core 高性能并发编程

一、高性能大并发架构设计 .NET Core 是一个高性能、可扩展的开发框架&#xff0c;可以用于构建各种类型的应用程序&#xff0c;包括高性能大并发应用程序。为了设计和开发高性能大并发 .NET Core 应用程序&#xff0c;需要考虑以下几个方面&#xff1a; 1. 异步编程 异步编程…

最大正方形 Python题解

最大正方形 题目描述 在一个 n m n\times m nm 的只包含 0 0 0 和 1 1 1 的矩阵里找出一个不包含 0 0 0 的最大正方形&#xff0c;输出边长。 输入格式 输入文件第一行为两个整数 n , m ( 1 ≤ n , m ≤ 100 ) n,m(1\leq n,m\leq 100) n,m(1≤n,m≤100)&#xff0c;接…

养猪场饲料加工机械设备有哪些

养猪场饲料加工机械设备主要包括以下几类&#xff1a;1‌、粉碎机‌&#xff1a;主要用于将原料进行粉碎&#xff0c;以便与其他饲料原料混合均匀。常见的粉碎机有水滴式粉碎机和立式粉碎机两种&#xff0c;用户可以根据原料的特性选择适合的机型。2‌、搅拌机‌&#xff1a;用…

ONVIF、GB28181技术特点和使用场景分析

技术背景 好多开发者希望搞明白ONVIF和GB28181的区别和各自适合的场景&#xff0c;为什么大牛直播SDK只做了GB28181接入端&#xff0c;没有做ONVIF&#xff1f;本文就二者差别&#xff0c;做个大概的介绍。 ONVIF ONVIF&#xff08;Open Network Video Interface Forum&…

【Linux 23】线程池

文章目录 &#x1f308; 一、线程池的概念&#x1f308; 二、线程池的应用场景&#x1f308; 三、线程池的实现 &#x1f308; 一、线程池的概念 线程池 (thread pool) 是一种利用池化技术的线程使用模式。 虽然创建线程的代价比创建进程的要小很多&#xff0c;但小并不意味着…

Mysql高级篇(下)——日志

日志 一、日志概述二、日志弊端二、日志分类三、 各日志详情介绍1、慢查询日志&#xff08;Slow Query Log&#xff09;2、通用查询日志&#xff08;General Query Log&#xff09;3、错误日志&#xff08;Error Log&#xff09;4、二进制日志&#xff08;Binary Log&#xff0…

初识Linux · 进程等待

目录 前言&#xff1a; 进程等待是什么 为什么需要进程等待 进程等待都在做什么 前言&#xff1a; 通过上文的学习&#xff0c;我们了解了进程终止&#xff0c;知道终止是在干什么&#xff0c;终止的三种情况&#xff0c;以及有了退出码&#xff0c;错误码的概念&#xff…

基于大数据的学生体质健康信息系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

图像数据增强albumentations之自然景色

一 背景 最近在做关于图像数据增强方面&#xff0c;发现albumentations这个包比较好用&#xff0c;在此学习一下如何使用API二 albumentations 安装 注意&#xff0c;注意&#xff0c;注意 python版本3.8 pip install -U albumentations三 API学习 1 模拟雨水 import os i…

慢病中医药膳养生食疗管理微信小程序、基于微信小程序的慢病中医药膳养生食疗管理系统设计与实现、中医药膳养生食疗管理微信小程序的开发与应用(源码+文档+定制)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

【SpringCloud】注册中⼼的其他实现-Nacos

Nacos 1. Nacos简介 2. Nacos安装2.1 下载安装包2.2 Windows2.2.1 解压2.2.2 修改单机模式2.2.3 启动Nacos2.2.4 常⻅问题集群模式启动端⼝号冲突 2.3 Linux2.3.1 准备安装包2.3.2 单机模式启动 1. Nacos简介 2018年6⽉, Eureka 2.0宣布闭源(但是1.X版本仍然为活跃项⽬), 同年…