设计模式-代理模式

代理模式

● 为对象提供一个代理类,增强该对象的方法,控制对这个对象的访问
● 静态代理和动态代理:静态代理就是编译的时候就已经确定,而动态代理就是运行时才会生成

静态代理的使用场景

缓存代理

● 提供数据的缓存功能,避免数据库重复查询

实践

  1. 定义数据查询的接口
public interface DataQuery {String query(String key);
}
  1. 接口实现类实现接口
package com.hillky.desgin_learn.Proxy;public class DataBaseQuery implements DataQuery{@Overridepublic String query(String key) {return "DataBaseQuery"+key;}
}
  1. 创建缓存代理类,实现接口,内部使用缓存Map,成员是被代理类
package com.hillky.desgin_learn.Proxy.cacheProxy;import lombok.NoArgsConstructor;import java.util.HashMap;public class CachingDataQueryProxy implements DataQuery{private DataQuery dataQuery;private static HashMap<String,String> cache=new HashMap<>();public CachingDataQueryProxy(DataBaseQuery dataBaseQuery) {this.dataQuery = dataBaseQuery;}public CachingDataQueryProxy() {this.dataQuery=new DataBaseQuery();}@Overridepublic String query(String key) {String result = cache.get(key);if(result != null){System.out.println("从缓存中取数据");return result;}result = dataQuery.query(key);System.out.println("从数据库取数据");cache.put(key,result);return result;}
}
  1. 建立测试类测试效果

安全代理

● 安全代理,可以实现访问控制、权限验证等安全相关功能。

实践

  1. 定义一个数据查询接口
package com.hillky.desgin_learn.Proxy.safeProxy;public interface SensitiveDataQuery {String queryData(String userId);
}
  1. 实现真实敏感数据查询实现
package com.hillky.desgin_learn.Proxy.safeProxy;public class SensitiveDataQueryImpl implements SensitiveDataQuery{@Overridepublic String queryData(String userId) {return "SensitiveDataQuery form user :"+userId;}
}
  1. 安全代理类,内部进行验证
package com.hillky.desgin_learn.Proxy.safeProxy;import lombok.Data;@Data
public class SecurityProxy implements SensitiveDataQuery{private  SensitiveDataQuery sensitiveDataQuery;private  UserAuthenticator userAuthenticator;public SecurityProxy() {this.sensitiveDataQuery=new SensitiveDataQueryImpl();}@Overridepublic String queryData(String userId) {if (userAuthenticator.hasPermission(userId)) {return sensitiveDataQuery.queryData(userId);} else {return "Access Denied: Insufficient permission for user" + userId;}}
}
  1. UserAuthenticator 类来模拟用户权限验证
package com.hillky.desgin_learn.Proxy.safeProxy;import java.util.Arrays;
import java.util.List;public class UserAuthenticator {private final List<String> authorizedUserIds;public UserAuthenticator() {// 模拟从数据库或配置文件中获取已授权的用户列表authorizedUserIds = Arrays.asList("user1", "user2", "user3");}public boolean hasPermission(String userId) {return authorizedUserIds.contains(userId);}
}
  1. 测试代码编写

虚拟代理

● 用于需要延迟创建耗时或资源密集型对象
● 虚拟代理在初始时访问才创建实际对象,之后将直接使用该对象

实践

  1. 定义一个图片接口
package com.hillky.desgin_learn.Proxy.vitruralProxy;public interface Image {void display();
}
  1. 实现一个大型图片类
package com.hillky.desgin_learn.Proxy.vitruralProxy;public class LargeImage implements Image{private  String imageUrl;public LargeImage(String imageUrl) {this.imageUrl = imageUrl;}@Overridepublic void display() {System.out.println("Displaying image: " + imageUrl);}
}
  1. 虚拟代理类
package com.hillky.desgin_learn.Proxy.vitruralProxy;public class VirtualImageProxy implements Image{private  String imageUrl;private LargeImage largeImage;public VirtualImageProxy(String imageUrl) {this.imageUrl = imageUrl;}@Overridepublic void display() {if(largeImage==null){largeImage=new LargeImage(imageUrl);}largeImage.display();}
}
  1. 测试

可以看到虚拟代理如何实现懒加载,以减少资源消耗和提高程序性能。

远程代理

● 为本地对象提供与远程对象相同的接口,使得客户端可以透明地访问远程对象。需要做序列化反序列化,网络通信(不同语言的调用)

实践

  1. 定义一个服务接口
package com.hillky.desgin_learn.Proxy.remoteProxy;public interface RemoteService {String fetchData(String dataId);
}
  1. ,实现一个远程服务类
package com.hillky.desgin_learn.Proxy.remoteProxy;public class RemoteServiceImpl implements RemoteService{@Overridepublic String fetchData(String dataId) {return "Data from remote service: " + dataId;}
}
  1. 创建一个远程代理类,它实现了 RemoteService 接口,并在内部处理网络通信等细节:
package com.hillky.desgin_learn.Proxy.remoteProxy;public class RemoteServiceProxy implements RemoteService{private  String remoteServiceUrl;private RemoteService remoteService;public RemoteServiceProxy(String remoteServiceUrl) {this.remoteServiceUrl = remoteServiceUrl;this.remoteService=new RemoteServiceImpl();}@Overridepublic String fetchData(String dataId) {// 网络通信、序列化和反序列化等逻辑System.out.println("Connecting to remote service at: " + remoteServiceUrl);// 假设我们已经获取到远程服务的数据String result = remoteService.fetchData(dataId);System.out.println("Received data from remote service.");return result;}
}
  1. 测试

请注意,为了简化代码,这里省略了实际的网 络通信、序列化和反序列化逻辑。

动态代理

● 静态代理需要手动编写代理类,代理类与被代理类实现相同的接口,对被代理对象进行包装。在程序运行前,代理类的代码就已经生成,并在程序运 行时调用。静态代理的优点是简单易懂,缺点是需要手动编写代理类,代码复杂度较 高,且不易扩展。
● 动态代理是在程序运行时动态生成代理类,无需手动编写代理类,大大降低了代码的 复杂度。通过反射实现

基于 JDK 的动态代理实现步骤

  1. 定义一个接口,声明需要代理的方法:
package com.hillky.desgin_learn.Proxy.remoteProxy;public interface RemoteService {String fetchData(String dataId);
}
  1. 创建一个被代理类,实现这个接口,并在其中定义实现方法:
package com.hillky.desgin_learn.Proxy.remoteProxy;public class RemoteServiceImpl implements RemoteService{@Overridepublic String fetchData(String dataId) {return "Data from remote service: " + dataId;}
}
  1. 创建一个代理类,实现 InvocationHandler 接口,并在其中定义一个被代理类的对象作为属性。
package com.hillky.desgin_learn.Proxy.jdkProxy;import javafx.beans.InvalidationListener;
import javafx.beans.Observable;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedHashMap;public class CacheInvocationHandler implements InvocationHandler {private DataQuery dataQuery;private HashMap<String,String> cache = new LinkedHashMap<>(256);public CacheInvocationHandler(DataQuery databaseDataQuery) {this.dataQuery = databaseDataQuery;}public CacheInvocationHandler() {this.dataQuery=new DataBaseQuery();}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String result=null;if(method.getName().equals("query")){result = cache.get(args[0].toString());if(result!=null){System.out.println("数据从缓存重获取。");return result;}result = (String) method.invoke(dataQuery, args);cache.put(args[0].toString(),result);return result;}// 当其他的方法被调用,不希望被干预,直接调用原生的方法return method.invoke(dataQuery,args);}
}
  1. 测试代码,符合要求,只有query方法加了缓存
package com.hillky.desgin_learn.Proxy.jdkProxy;import org.junit.jupiter.api.Test;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;import static org.junit.jupiter.api.Assertions.*;class CacheInvocationHandlerTest {@Testvoid test() {// jdk提供的代理实现,主要是使用Proxy类来完成// 1、classLoader:被代理类的类加载器ClassLoader classLoader = Thread.currentThread().getContextClassLoader();// 2、代理类需要实现的接口数组Class[] interfaces = new Class[]{DataQuery.class};// 3、InvocationHandlerInvocationHandler invocationHandler = new CacheInvocationHandler();DataQuery dataQuery = (DataQuery) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);// 事实上调用query方法的使用,他是调用了invokeString result = dataQuery.query("key1");System.out.println(result);System.out.println("--------------------");result = dataQuery.query("key1");System.out.println(result);System.out.println("--------------------");result = dataQuery.query("key2");System.out.println(result);System.out.println("++++++++++++++++++++++++++++++++++++");// 事实上调用queryAll方法的使用,他是调用了invokeresult = dataQuery.queryAll("key1");System.out.println(result);System.out.println("--------------------");result = dataQuery.queryAll("key1");System.out.println(result);System.out.println("--------------------");result = dataQuery.queryAll("key2");System.out.println(result);System.out.println("--------------------");}
}

spring中aop的使用步骤

● 在 Spring 中,AOP(面向切面编程)提供了一种有效的方式来对程序中的多个模块进行横切关注点的处理,例如日志、事务、缓存、安全等。从而实现对目标对象的增强

  1. 引入 AOP 相关依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  1. 开启自动代理
@EnableAspectJAutoProxy
@SpringBootApplication
@EnableAspectJAutoProxy
public class DesginLearnApplication {public static void main(String[] args) {SpringApplication.run(DesginLearnApplication.class, args);}}
  1. 定义接口和实现类,并将具体实现注入容器
package com.hillky.desgin_learn.Proxy.aop;public interface DataQuery {String query(String queryKey);
}
package com.hillky.desgin_learn.Proxy.aop;import org.springframework.stereotype.Component;@Component
public class DataBaseQuery implements DataQuery{@Overridepublic String query(String queryKey) {System.out.println("正在从数据库查询数据");return "result";}
}
  1. 定义切面类,对方法做增强
package com.hillky.desgin_learn.Proxy.aop;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;@Component
@Aspect
public class CacheAspectj {//定义切面@Pointcut("execution(* com.hillky.desgin_learn.Proxy.aop.DataQuery.query(..))")public void pointcut(){}@Around("pointcut()")public String around(ProceedingJoinPoint joinPoint){Object[] args = joinPoint.getArgs();String key = args[0].toString();// 1、查询缓存,命中则返回String result = Cache.get(key);if(result != null){System.out.println("数据从缓存中获取");return result;}// 未命中则去数据库查询,实际上是调用被代理bean的方法try {result = joinPoint.proceed().toString();// 如果查询有结果,进行缓存Cache.put(key,result);} catch (Throwable e) {throw new RuntimeException(e);}return result;}}
  1. 定义缓存
package com.hillky.desgin_learn.Proxy.aop;import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;public class Cache {private static Map<String,String> map = new ConcurrentHashMap<>(256);public static String get(String key){return map.get(key);}public static void put(String key,String value){map.put(key, value);}
}
  1. 测试
package com.hillky.desgin_learn.Proxy.aop;import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;import javax.annotation.Resource;import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class CacheAspectjTest {@Resourceprivate DataQuery dataQuery;@Testvoid test(){dataQuery.query("key1");dataQuery.query("key1");dataQuery.query("key2");}}

代理和 AOP 是两个相关的概念,代理是 AOP 的一种实现方式,它们都可以 用于在程序中实现对目标对象的增强。区别在于,代理主要是针对单个对象的方法调 用进行增强,而 AOP 则是针对程序中多个模块的横切关注点进行增强。

动态代理的应用场景

动态代理是一种代理模式,它在运行时动态生成代理对象,而无需提前创建具体的代理类。
使用场景:
● 日志记录
● 性能监控
● 事务管理
● 权限验证
● 缓存
● aop
● 速率限制

一般都可以用aop方便实现

日志记录

  1. 引入 AOP 相关依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  1. 开启自动代理
@EnableAspectJAutoProxy
@SpringBootApplication
@EnableAspectJAutoProxy
public class DesginLearnApplication {public static void main(String[] args) {SpringApplication.run(DesginLearnApplication.class, args);}}
  1. 创建一个切面类(Aspect)来实现日志记录的逻辑
package com.hillky.desgin_learn.Proxy.aop;public interface DataQuery {String query(String queryKey);
}
  1. 定义切面类,对方法做增强
package com.hillky.desgin_learn.Proxy.log;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;@Aspect
@Component
public class LoggingAspect {private final Logger logger = LoggerFactory.getLogger(this.getClass());//定义切面@Pointcut("within(@org.springframework.web.bind.annotation.RestController *)")public void restControllerMethods() {}@Before("restControllerMethods()")public void logMethodCall(JoinPoint joinPoint){String className = joinPoint.getSignature().getDeclaringTypeName();String methodName = joinPoint.getSignature().getName();logger.info("Entering method [{}.{}]", className, methodName);}@AfterReturning(pointcut = "restControllerMethods()", returning = "result")public void logMethodReturn(JoinPoint joinPoint, Object result) {String className = joinPoint.getSignature().getDeclaringTypeName();String methodName = joinPoint.getSignature().getName();logger.info("Exiting method [{}.{}], return value: {}", className,methodName, result);}
}
  1. 测试接口访问看控制台输出
package com.hillky.desgin_learn.Proxy.log;import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class SampleController {@GetMapping("/hello")public String hello() {return "Hello, World!";}
}
  1. 测试
package com.hillky.desgin_learn.Proxy.aop;import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;import javax.annotation.Resource;import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class CacheAspectjTest {@Resourceprivate DataQuery dataQuery;@Testvoid test(){dataQuery.query("key1");dataQuery.query("key1");dataQuery.query("key2");}}

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

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

相关文章

python字符串处理15题-刷题

题目一&#xff1a;最长无重复子串长度 给定一个字符串&#xff0c;找出其中不含有重复字符的最长子串的长度。 示例&#xff1a; 输入: "abcabcbb" 输出: 3 解释: 最长无重复子串是 "abc"&#xff0c;长度为 3。 解答&#xff1a; def length_of_longest…

Ubuntu vi 左下角没有提示

1 打开终端&#xff0c;输入以下命令 sudo gedit /etc/vim/vimrc.tiny 2 进入文件后&#xff0c;修改 set compatible 为set nocompatible&#xff0c;如下所示&#xff1a; " Vim configuration file, in effect when invoked as "vi". The aim of this "…

全球公链进展| Shibarium已上线;opBNB测试网PreContract硬分叉;Sui 主网 V1.7.1 版本

01 ETH 以太坊最新一次核心开发者执行会议&#xff1a;讨论 Devnet 8 更新、ElP-4788、Holesky 测试网等 以太坊核心开发者 Tim Beiko 总结最新一次以太坊核心开发者执行会议&#xff08;ACDE&#xff09;&#xff0c;讨论内容包括 Devnet 8 更新、ElP-4788、Holesky 测试网、…

【DevOps视频笔记】4.Build 阶段 - Maven安装配置

一、Build 阶段工具 二、Operate阶段工具 三、服务器中安装 四、修改网卡信息 五、安装 jdk 和 maven Stage1 : 安装 JDK Stage 2 : 安装 Maven 2-1 : 更换文件夹名称 2-2 : 替换配置文件 settings.xml- 2-3 : 修改settings.xml详情 A. 修改maven仓库地址 - 阿里云 B…

CentOS系统环境搭建(十七)——elasticsearch设置密码

centos系统环境搭建专栏&#x1f517;点击跳转 elasticsearch设置密码 没有密码是很不安全的一件事&#x1f62d; 文章目录 elasticsearch设置密码1.设置密码2.登录elasticsearch3.登录kibana4.登录elasticsearch-head 1.设置密码 关于Elasticsearch的安装请看CentOS系统环境搭…

适配小程序隐私保护指引设置

由于小程序发布了一个公告&#xff0c;那么接下来就是怎么改简单的问题了。毕竟不太想大的改动历史上的代码。尽量简单的适配隐私策略就可以了。 整体思路也是参考现在App普遍的启动就让用户同意隐私策略&#xff0c;不同意不让用&#xff0c;同意了之后才能够继续使用。 公告…

ubuntu下mysql

安装&#xff1a; sudo apt update sudo apt install my_sql 安装客户端&#xff1a; sudo apt-get install mysql-client sudo apt-get install libmysqlclient-dev 启动服务 启动方式之一&#xff1a; sudo service mysql start 检查服务器状态方式之一&#xff1a;sudo …

计算机网络 QA

DNS 的解析过程 浏览器缓存。当用户通过浏览器访问某域名时&#xff0c;浏览器首先会在自己的缓存中查找是否有该域名对应的 IP 地址&#xff08;曾经访问过该域名并且没有清空缓存&#xff09;系统缓存。当浏览器缓存中无域名对应的 IP 地址时&#xff0c;会自动检测用户计算机…

在WPF Visual Tree中查找父控件

实现 private static T FindVisualParent<T>(DependencyObject child)where T : DependencyObject{DependencyObject parentObject VisualTreeHelper.GetParent(child);if (parentObject null){return null;}T parent parentObject as T;if (parent ! null){return pa…

软件测试技术之可用性测试之WhatsApp Web

Tag&#xff1a;可行性测试、测试流程、结果分析、案例分析 WhatsApp是一款面向智能手机的网络通讯服务&#xff0c;它可以通过网络传送短信、图片、音频和视频。WhatsApp在全球范围内被广泛使用&#xff0c;是最受欢迎的即时聊天软件。 虽然&#xff0c;在电脑上使用WhatsAp…

docker搭建owncloud,Harbor,构建镜像

1、使用mysql:5.6和 owncloud 镜像&#xff0c;构建一个个人网盘。 拉取镜像 docker pull owncloud docker pull mysql:5.6 2、安装搭建私有仓库 Harbor 1.下载docker-compose 2.安装harbor 3.编辑 harbor.yml文件 使用./intall.sh安装 4.登录 3、编写Dockerfile制作Web应用系…

【学习FreeRTOS】第13章——FreeRTOS队列

1.队列简介 队列是任务到任务、任务到中断、中断到任务数据交流的一种机制&#xff08;消息传递&#xff09; FreeRTOS基于队列&#xff0c; 实现了多种功能&#xff0c;其中包括队列集、互斥信号量、计数型信号量、二值信号量、 递归互斥信号量&#xff0c;因此很有必要深入了…

奇葩的this与$emit传值

需求&#xff1a;最近遇到了一个需求&#xff0c;把一个页面内部的tab选项卡改为路由控制的页面 问题&#xff1a;查询条件单独封装一个组件后给父组件页面通过$emit事件传递表单参数&#xff1b;但是在父组件中每次得到的表单值都是undefined&#xff1b;调试了半天&#xff0…

Linux CentOS7系统,抓取http协议的数据包

使用 tcpdump 命令 1.首先确认是否安装 [rootlocalhost ~]# which tcpdump /usr/bin/which: no tcpdump in (/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin) [rootlocalhost ~]#我这里没有安装 1.1 安装 tcpdump yum install tcpdump 安装成功如下&#xf…

设计模式 07 桥接模式

桥接模式&#xff08;Bridge Pattern&#xff09;属于结构型模式 概述 桥接模式是将抽象部分与它的实现部分分离&#xff0c;使它们都可以独立地变化。它是一种对象结构型模式&#xff0c;又称为柄体&#xff08;Handle and Body&#xff09;模式或接口&#xff08;Interface&…

一生一芯6——ubuntu rpm软件安装

ubuntu不支持rpm&#xff0c;需要将rpm软件安装包转成deb进行安装 安装alien sudo apt-get install alien格式转换 sudo alien xxx.rpm 在目录下会生成deb的安装包 软件安装 sudo dpkg -i xxx_amd64.deb 安装完成

【C++ 学习 ⑯】- 继承(上)

目录 一、继承的概念和定义 1.1 - 概念 1.2 - 定义 二、继承时的对象内存模型 三、向上转型和向下转型 四、继承时的名字遮蔽问题 4.1 - 有成员变量遮蔽时的内存分布 4.2 - 重名的基类成员函数和派生类成员函数不构成重载 一、继承的概念和定义 1.1 - 概念 C 中的继承…

(五)、Redisson锁机制源码分析

1、了解分布式锁的特性 1、锁的互斥性 也就是说,在任意时刻,只能有一个客户端能获取到锁,不能同时有两个或多个客户端获取到锁。简单来说,就比如上厕所,一个厕所只有一个坑位,只能一个人上,不能同时两个人或多个人上。2、锁的同一性 也就是说,锁只能被持有该锁的客户端…

MMEdu实现摄像头图像分类(Python版)

先安装MMEdu库&#xff01; MMEdu安装&#xff1a;https://blog.csdn.net/zyl_coder/article/details/132483865 下面的代码请在Jupyter上运行&#xff0c;并自己准备数据集。若模型还未训练&#xff0c;请先在本地训练完模型后再进行模型推理。 import cv2 capture cv2.Vi…

【AWS】创建IAM用户;无法登录IAM用户怎么办?错误提示:您的身份验证信息错误,请重试(已解决)

目录 0.背景问题分析 1.解决步骤 0.背景问题分析 windows 11 &#xff0c;64位 我的问题情景&#xff1a; 首先我创建了aws的账户&#xff0c;并且可以用ROOT用户登录&#xff0c;但是在登录时选择IAM用户&#xff0c;输入ROOT的名字和密码&#xff0c;就会提示【您的身份验证…