Aop+自定义注解实现数据字典映射
数据字典
Web项目开发中,字典表的一般都会存在,主要用来给整个系统提供基础服务。
比如男女性别的类型可以使用0和1来进行表示,在存储数据和查询数据的时候,就可以使用字典表中的数据进行翻译处理。
再比如之前做的一个项目中宠物类型包含老虎-1、海豚-2、大象-3、长颈鹿-4等等;做答题处理时的答题类型,比如单选题、多选题、填空题等等;
比较常用的有用户类型,普通用户还是VIP用户等等;这些类型的数据也可以存储在数据字典中进行统一处理。
数据结构以及业务
举例:
在业务表中使用的是业务目录,而其中的“数据结构”,“数据目录类型”,“数据来源”使用的是字典表中的id,但我们在页面显示的时候,想用字典表中的value值,即我们在库里相应的业务表的外键存字典表的ID,查询的时候返回给前端在字典表中的value字段。
如以下表以及内容的展示:
目录表:
内容: 外键,字典的ID
如图所示:(字典表)需要我们在表中拿出外键所对应的Label
列举方法
那有什么办法可以让我们查询出key的同时,将value值也查询出来,当时处理问题的时候想了三种解决方法:
1.要么就写sql 匹配。(在SQL查询的时候做字段的匹配,但是增加了SQL的复杂性不易维护)
2.要么就业务处理。得写个工具类(针对当时的情况感觉可以实现,然后查找了一些资料比如,自定义注解啥的)
3.再就是前端调两次接口,查询一次数据,再查询一次字典表做转译。(所涉及到的字段有四五个甚至更多,这是我问的别的同学,他们公司采用的就是这种方法)
根据查找资料以及实际考虑,最终采用了使用自定义注解的方式去做字段映射。
实现步骤:
首先展示项目的目录结构
总共三个,自定义注解,切面,具体实现
一 ,创建自定义注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Dict {/*** 字典类型,对应数据库的类型,下方展示*/String dictType();/*** 实体类内对应的中文字段名,默认为“当前字段+Text”* <p>* 例如当前字段为“type”,则对应中文字段默认为“typeText”* 就是你在实体中想要给某个字段赋值,这个字段就要写成那个字段*/String dictField() default "";}
二,创建切面
@Slf4j
@Aspect
@Component
public class BydDaoDictAspect {
// * `@Pointcut` 注解用于定义一个切点,切点是决定哪些方法应该被通知(advice)的表达式。
// * `execution(* com.wiseom.asset.manage.service..*.*(..))` 是一个切点表达式,它表示匹配 `com.wiseom.asset.manage.service` 包及其子包下的所有类的所有方法。
// * `doPointcut()` 方法本身没有执行任何操作,它只是为切点提供了一个标识符。@Pointcut("execution(* com.wiseom.asset.manage.service..*.*(..))")public void doPointcut() {}
// * `@AfterReturning` 注解表示在匹配的方法成功执行并返回结果后执行通知。
// * `pointcut = "doPointcut()"` 表示这个通知应用于之前定义的 `doPointcut` 切点。
// * `returning = "result"` 捕获返回的结果,并将其作为 `result` 参数传递给通知方法。
// * 在 `doAfterReturning` 方法中,`DictUtils.convertDict(result)` 是将返回的 `result` 对象转换为字典格式。如果在转换过程中发生异常,它会被捕获并记录到日志中。@AfterReturning(pointcut = "doPointcut()", returning = "result")public void doAfterReturning(JoinPoint pjp, Object result) {try {DictUtils.convertDict(result);} catch (Exception e) {log.error(String.valueOf(e));}}
}
备注:
1:切点在com.wiseom.asset.manage.service的原因,因为我们使用了mybitsplus ,在我们处理查询,或者执行SQL的时候大多都在service层就被处理了,所以切点设在了service层。
2:如果切点设置在Controller,则需要修改下方实现三中的 方法 convertDict,因为 Controller的返回值拿到的是 R 包裹的内容,返回值对象是R,而不是实体,具体问题查看下方的扩展四
三,创建实现
@Component
public class DictUtils {private static final String DICT_FIELD_SUFFIX = "Text";/*** 统一获取当前实体类涉及到的字典表数据,避免多次查询数据库造成的性能消耗* 这个工具类主要的改动就是这个,所以我就把这一段放在上面了* @param dictNames 字典表type值* @return 字典表数据*/@SneakyThrowsprivate static Map<String, Map<Long, String>> getDictMap(List<String> dictNames) {//创建fegin的实例,调用系统的sys_dict_item表,需要新写一个接口final RemoteDictService remoteDictService = SpringContextHolder.getBean(RemoteDictService.class);//把注解里的所有字典的类型查出来,具体根据需求做查询List<SysDictItem> dictList = remoteDictService.getDictItemByType(dictNames);//根据类型做一个分组,这里对应的是 Map<DictType, Map<ID, Label>>return dictList.stream().collect(Collectors.groupingBy(SysDictItem::getDictType, Collectors.toMap(SysDictItem::getId, SysDictItem::getLabel, (v1, v2) -> v2)));}/*** 这是主方法主要是判断AOP后返回的内容,并根据判断条件拿到里面的内容* 然后先判断有哪些带有注解,根据注解拿到对应的dictType去库里查找对应的内容 * 根据拿到内容在对指定的字段dictField进行赋值*/public static void convertDict(Object target) {//判断对象是不是分页的if (target instanceof Page) {//拿到分页中的records对象里边包含实体的内容for(Object object : ((Page<?>)target).getRecords()){//拿到实体List<DictDefinition> dictDefinitions = getMetadata(object);// 如果没有注解,则直接返回if (CollectionUtils.isEmpty(dictDefinitions)) return;// 从字典定义中提取所有的注解的 dictType(数据库字典的类型)List<String> dictNames = dictDefinitions.stream().map(d -> d.getDict().dictType()).collect(Collectors.toList());// 根据字典类型获取字典映射Map<String, Map<Long, String>> dictMapMap = getDictMap(dictNames);// 转换target对象的字典字段doConvertDict(object, dictDefinitions, dictMapMap);}} elseif (target instanceof List) {// 将target强制转换为List<?>类型List<?> objectList = ((List<?>) target);// 使用CollectionUtils.isNotEmpty来检查列表是否非空if (CollectionUtils.isNotEmpty(objectList)) {// 获取列表中第一个元素的字典定义List<DictDefinition> dictDefinitions = getMetadata(objectList.get(0));// 如果没有注解,则直接返回if (CollectionUtils.isEmpty(dictDefinitions)) return;// 从字典定义中提取所有的注解的 dictType(数据库字典的类型)List<String> dictNames = dictDefinitions.stream().map(d -> d.getDict().dictType()).collect(Collectors.toList());// 根据字典类型获取字典映射Map<String, Map<Long, String>> dictMapMap = getDictMap(dictNames);// 遍历列表中的每个元素,并转换其字典字段objectList.forEach(t -> doConvertDict(t, dictDefinitions, dictMapMap));}} else {// 如果target不是List,则直接获取其字典定义List<DictDefinition> dictDefinitions = getMetadata(target);// 如果没有注解,则直接返回if (CollectionUtils.isEmpty(dictDefinitions)) return;// 从字典定义中提取所有的注解的 dictType(数据库字典的类型)List<String> dictNames = dictDefinitions.stream().map(d -> d.getDict().dictType()).collect(Collectors.toList());// 根据字典类型获取字典映射Map<String, Map<Long, String>> dictMapMap = getDictMap(dictNames);// 转换target对象的字典字段doConvertDict(target, dictDefinitions, dictMapMap);}}/*** 仅获取一次Dict元数据,降低多次反射造成的性能消耗* @param target 目标实体类* @return Dict元数据*/private static List<DictDefinition> getMetadata(Object target) {//这一段是判断存在注解的字段List<DictDefinition> dictDefinitions = new ArrayList<>();if (ClassUtils.isPrimitiveOrWrapper(target.getClass())|| target instanceof Map || target instanceof String) {return dictDefinitions;}List<Field> fields = FieldUtils.getAllFieldsList(target.getClass());for (Field field : fields) {Dict dict = AnnotationUtils.getAnnotation(field, Dict.class);if (dict != null) {DictDefinition dictDefinition = new DictDefinition();dictDefinition.setDict(dict);dictDefinition.setField(field);dictDefinitions.add(dictDefinition);}}return dictDefinitions;}@SneakyThrowsprivate static void doConvertDict(Object target, List<DictDefinition> dictDefinitions,Map<String, Map<Long, String>> dictMapMap) {for (DictDefinition dictDefinition : dictDefinitions) {//获取Dict注解和字段信息Dict dict = dictDefinition.getDict();Field field = dictDefinition.getField();//获取字典映射Map<Long, String> dictMap = dictMapMap.get(dict.dictType());//读取实体中带有注解的字段原有的值String dictCode = String.valueOf(FieldUtils.readField(target, field.getName(), true));//类型转换,我在库中用的存储的字段是String,所以需要进行类型的转换,如果你的库设置的不是,或者其他的类型,需要注意到这个地方Long longDictCode=Long.valueOf(dictCode);//拿到字典注解的labelString dictField = StringUtils.isEmpty(dict.dictField()) ? field.getName() + DICT_FIELD_SUFFIX : dict.dictField();//设置字段的字典文本值FieldUtils.writeField(target, dictField, dictMap.get(longDictCode), true);}}@Datapublic static class DictDefinition {private Dict dict;private Field field;}}
系统表的查询接口:
//这个我是在SysDictController下添加的接口,目的是实现查找多个类型的内容,同时还需要写个fegin接口以供别的模块使用
/*** 通过字典类型查找字典* @param types 类型* @return 同类型字典*/@PostMapping("/types")public List<SysDictItem> getDictByTypes(@RequestBody List<String> types) {return sysDictItemService.list(Wrappers.<SysDictItem>query().lambda().in(SysDictItem::getDictType, types));}
使用方法
直接在实体字段上方添加注解
1.dictType:数据库中的字典的类型
2.dictField:实体中赋值的字段
如上方使用方法所示,如果你想将字典中的label值给实体中的 catalogStructure,则标注
如果前端需要,一个原来的ID,一个label,那就需要创建一个DTO,如图所示,将dictField 后边写上你想要的字段名称就行了
四,备注以及扩展
这个是公司用的pigx框架,没用到的同学,可以跳过了,仅供参考,依据具体项目实际来定
参数的入值:
@AfterReturning(pointcut = "doPointcut()", returning = "result")public void doAfterReturning(JoinPoint pjp, Object result) {try {DictUtils.convertDict(result);} catch (Exception e) {log.error(String.valueOf(e));}}
这个代码中的result需要着重注意,他可能会存在判断的问题
举例:这个是正常的,只拿到这个返回的实体就行,在我们实现三中的方法convertDict所有if()的目的都是为了拿到返回值中的实体
比如底下这个图,分页返回的就需要做一个判断(实现三我已经加上了,这个做一个参考):
判断:
五:具体实现 优化1,多个字段的匹配,用“,”隔开代码优化
@Component
public class DictUtils {private static final String DICT_FIELD_SUFFIX = "Text";/*** 执行字典转换的公共方法* @param target 待转换的对象*/public static void convertDict(Object target) {//判断是否是分页if (target instanceof Page) {for (Object object : ((Page<?>) target).getRecords()) {convertDictInternal(object);}//是否是list} else if (target instanceof List) {if (CollectionUtils.isNotEmpty((List<?>) target)) {for (Object object : (List<?>) target) {convertDictInternal(object);}}} else {convertDictInternal(target);}}/*** 内部的字典转换实现方法* @param target 待转换的对象*/@SneakyThrowsprivate static void convertDictInternal(Object target) {// 获取目标对象的字典定义List<DictDefinition> dictDefinitions = getMetadata(target);// 如果没有字典定义则直接返回if (CollectionUtils.isEmpty(dictDefinitions)) {return;}// 提取字典类型并获取字典映射String commaSeparatedString = dictDefinitions.stream().map(d -> d.getDict().dictType()).collect(Collectors.joining(","));List<String> dictNames = Arrays.asList(commaSeparatedString.split(","));Map<String, Map<Long, String>> dictMapMap = getDictMap(dictNames);// 执行具体的字典转换操作doConvertDict(target, dictDefinitions, dictMapMap);}/*** 获取目标对象的字典定义* @param target 目标对象* @return 字典定义列表*/private static List<DictDefinition> getMetadata(Object target) {List<DictDefinition> dictDefinitions = new ArrayList<>();// 排除基本类型、包装类型、Map 和 String 类型if (ClassUtils.isPrimitiveOrWrapper(target.getClass())|| target instanceof Map || target instanceof String) {return dictDefinitions;}// 获取目标对象的所有字段List<Field> fields = FieldUtils.getAllFieldsList(target.getClass());for (Field field : fields) {// 获取字段上的 Dict 注解Dict dict = AnnotationUtils.getAnnotation(field, Dict.class);if (dict!= null) {// 创建并添加字典定义DictDefinition dictDefinition = new DictDefinition();dictDefinition.setDict(dict);dictDefinition.setField(field);dictDefinitions.add(dictDefinition);}}return dictDefinitions;}/*** 根据字典名称获取字典映射* @param dictNames 字典名称列表* @return 字典映射*/@SneakyThrowsprivate static Map<String, Map<Long, String>> getDictMap(List<String> dictNames) {final RemoteDictService remoteDictService = SpringContextHolder.getBean(RemoteDictService.class);List<SysDictItem> dictList = remoteDictService.getDictItemByType(dictNames);return new HashMap<>(dictList.stream().collect(Collectors.groupingBy(SysDictItem::getDictType, Collectors.toMap(SysDictItem::getId, SysDictItem::getLabel, (v1, v2) -> v2))));}/*** 执行具体的字典字段转换* @param target 目标对象* @param dictDefinitions 字典定义列表* @param dictMapMap 字典映射*/@SneakyThrowsprivate static void doConvertDict(Object target, List<DictDefinition> dictDefinitions,Map<String, Map<Long, String>> dictMapMap) {for (DictDefinition dictDefinition : dictDefinitions) {Dict dict = dictDefinition.getDict();Field field = dictDefinition.getField();String[] dictTypes = dict.dictType().split(",");for (String dictType : dictTypes) {Map<Long, String> dictMap = dictMapMap.get(dictType.trim());// 读取字段的字典编码String dictCode = String.valueOf(FieldUtils.readField(target, field.getName(), true));// 类型转换Long longDictCode = Long.valueOf(dictCode);// 确定字典字段名String dictField = StringUtils.isEmpty(dict.dictField())? field.getName() + DICT_FIELD_SUFFIX : dict.dictField();// 设置字段的字典文本值if (dictMap!= null && dictMap.containsKey(longDictCode)) {FieldUtils.writeField(target, dictField, dictMap.get(longDictCode), true);}}}}@Datapublic static class DictDefinition {private Dict dict;private Field field;}
}
使用方式:
参考:
主要内容参考:
java自定义注解实现数据字典映射_字典映射 java-CSDN博客
分页扩展参考:
字典翻译@Dict - 莫大人 - 博客园
相关文章:

Aop+自定义注解实现数据字典映射
数据字典 Web项目开发中,字典表的一般都会存在,主要用来给整个系统提供基础服务。 比如男女性别的类型可以使用0和1来进行表示,在存储数据和查询数据的时候,就可以使用字典表中的数据进行翻译处理。 再比如之前做的一个项目中宠物…...

大语言模型(LLM)入门级选手初学教程 III
指令微调 一、指令数据的构建 包括任务描述(也称为指令)、任务输入-任务输出以及可选的示例。 Self-Instruct 指令数据生成:从任务池中随机选取少量指令数据作为示例,并针对Chat-GPT 设计精细指令来提示模型生成新的微调数据…...

STM32G0xx使用LL库将Flash页分块方式存储数据实现一次擦除可多次写入
STM32G0xx使用LL库将Flash页分块方式存储数据实现一次擦除可多次写入 参考例程例程说明一、存储到Flash中的数据二、Flash最底层操作(解锁,加锁,擦除,读写)三、从Flash块中读取数据五、测试验证 参考例程 STM32G0xx HAL和LL库Flash读写擦除操…...

SAP B1 认证考试习题 - 解析版(三)
前一篇:《SAP B1 认证考试习题 - 解析版(二)》 题目纯享版合集:《SAP B1 认证考试习题 - 纯享版》 五、运费(附加费用) 57. 以下哪个选项能够影响库存商品的价格 A. 仅为总量级别的附加费用 B. 只为行级…...

数据库开发规范
一、概述 本规范规定了,软件项目团队开发数据库的全流程规范。规范覆盖了数据库设计、管理及配套文件等。 二、项目阶段 项目阶段包括需求评审(需求分析阶段)、技术评审(方案阶段)、数据库开发…...

使用python向钉钉群聊发送消息
使用python向钉钉群聊发送消息 一、在钉钉群中新建机器人二、使用代码发送消息 一、在钉钉群中新建机器人 在群设置中添加机器人 选择自定义 勾选对应的安全设置 完成后会展示webhook,将地址复制出来,并记录,后面会用到 二、使用代码发送消…...

YOLOv11改进:SE注意力机制【注意力系列篇】(附详细的修改步骤,以及代码,与其他一些注意力机制相比,不仅准确度更高,而且模型更加轻量化。)
如果实验环境尚未搭建成功,可以参考这篇文章 ->【YOLOv11超详细环境搭建以及模型训练(GPU版本)】 文章链接为:YOLOv11超详细环境搭建以及模型训练(GPU版本)-CSDN博客 -------------------------------…...

STM32 基于HAL库和STM32cubeIDE的应用教程 (二)--GPIO的使用
如果有什么不懂的地方欢迎私聊博主,欢迎小白,博主必一一解答。 在 STM32 中,GPIO(通用输入输出)是与外部硬件接口进行交互的主要方式之一。STM32 HAL 库提供了简洁的接口来配置和控制 GPIO 引脚。下面是使用 STM32 HA…...

【毫米波雷达(七)】自动驾驶汽车中的精准定位——RTK定位技术
一、什么是RTK? RTK,英文全名叫做Real-time kinematic,也就是实时动态。这是一个简称,全称其实应该是RTK(Real-time kinematic,实时动态)载波相位差分技术。 二、RTK的组装 如上图所示&#x…...

Transformer和BERT的区别
Transformer和BERT的区别比较表: 两者的位置编码: 为什么要对位置进行编码? Attention提取特征的时候,可以获取全局每个词对之间的关系,但是并没有显式保留时序信息,或者说位置信息。就算打乱序列中token…...

linux 加载uPD720201固件
硬件 jetson orin nano jetpack 35.5.0 uPD720201是瑞萨推出的怕pcie扩展usb3.0芯片,支持flash主动加载与在系统被动加载 本文介绍如何做到没接flash情况下由系统加载固件 在uPD720201没接spi flash时候nano启动会报XhciDxe错误而自动重启,首先需要在ue…...

C语言中的信号量semaphore详解
在C语言中,**信号量(Semaphore)**是一种常用的同步机制,用于控制多个线程或进程对共享资源的访问。信号量可以实现类似于锁的效果,但更为灵活,适用于并发编程场景。 1. 什么是信号量 信号量可以看作是一个…...

0087__DirectX11 With Windows SDK--02 顶点/像素着色器的创建、顶点缓冲区
DirectX11 With Windows SDK--02 顶点/像素着色器的创建、顶点缓冲区-CSDN博客...

Windows换机华为擎云(银河麒麟V10+麒麟9000C CPU)后,使用selenium的程序怎么办(20241030)
原本的 seleniumChrome 已无法正常工作。chromedriver 报错:不支持 Linux/aarch64。 1、尝试Firefox、edge驱动。Firefox有一个geckodriver版本与Firefox版本的对照表,我看了一下,感觉他们是始终跟进新技术的。银河麒麟的很多库都是几年前的…...

linux 下 signal() 函数的用法,信号类型在哪里定义的?
--------------------------------------------------- author: hjjdebug date: 2024年 11月 07日 星期四 14:47:33 CST description: linux 下 signal() 函数的用法 --------------------------------------------------- signal 是linux 下最基础的进程通讯机制…...

享元模式及其运用场景:结合工厂模式和单例模式优化内存使用
介绍 享元模式(Flyweight Pattern)是一种结构型设计模式,它通过共享对象来减少内存使用,尤其是对于大量相似对象的场景。享元模式通常与工厂模式和单例模式结合使用,从而有效地控制和复用对象的创建。在享元模式中&am…...

【物联网技术】ESP8266 WIFI模块在STA模式下实现UDP与电脑/手机网络助手通信——UDP数据透传
前言:完成ESP8266 WIFI模块在STA模式下实现UDP与电脑/手机网络助手通信——实现UDP数据透传 STA模式,通俗来说就是模块/单片机去连接路由器/热点来通信。 UDP协议,是传输层协议,UDP没有服务器和客户端的说法。 本实验需要注意,wifi模块/单片机与电脑/手机需要连接在同一个…...

【SQL Server】华中农业大学空间数据库实验报告 实验一 数据库
实验目的 熟悉了解掌握SQL Server软件的基本操作与使用方法,认识界面,了解其两个基本操作系统文件,并能熟练区分与应用交互式与T-SQL式两种方法在SQL Server中如何进行操作;学习有关数据库的基本操作,包括:…...

操作系统页面置换算法Java实现(LFU,OPT,LRU,LFU,CLOCK)
FIFO先进先出算法 java import java.util.LinkedList; import java.util.Queue; public class Main { //先进先出的思想 是 用一个队列去模拟数据 如果当前不存在就是发生缺页中断了 就需要添加 如果已经满了 将队头的元素出队 即可 //先进先出 就是一个数组 frameCount publi…...

Request和Response
前言 这一节主要讲的是Request和Response还有一些实例 1. 介绍 就是这两个参数 WebServlet("/demo7") public class ServletDemo7 extends HttpServlet {Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletExcepti…...

【青牛科技】GC8549替代LV8549/ONSEMI在摇头机、舞台灯、打印机和白色家电等产品上的应用分析
引言 在现代电子产品中,控制芯片的性能直接影响到设备的功能和用户体验。摇头机、舞台灯、打印机和白色家电等领域对控制精度、功耗和成本等方面的要求日益提高。LV8549/ONSEMI等国际品牌的芯片曾是这些产品的主要选择,但随着国内半导体技术的进步&…...

(十二)JavaWeb后端开发——MySQL数据库
目录 1.数据库概述 2.MyQSL 3.数据库设计 DDL 4.MySQL常见数据类型 5.DML 1.数据库概述 数据库:DataBase(DB),是存储和管理数据的仓库 数据库管理系统:DataBase ManagementSystem(DBMS),操纵和管理数据库的大型软件 SQL&a…...

pnpm管理多工作区依赖
pnpm是一个支持多包仓库的一个包管理工具,那么怎么可以在项目根目录下执行pnpm install的时候,也能同步让所有的工作区都能够通安装依赖呢? 方式一,在执行pnpm install指令的时候,添加recursive参数: pnpm install --recursive 方式二,在项目的根目录下通过pnpm的配置文件p…...

如何在本地Linux服务器搭建WordPress网站结合内网穿透随时随地可访问
文章目录 前言1. 安装WordPress2. 创建WordPress数据库3. 安装相对URL插件4. 安装内网穿透发布网站4.1 命令行方式:4.2. 配置wordpress公网地址 5. 配置WordPress固定公网地址 前言 本文主要介绍如何在Linux Ubuntu系统上使用WordPress搭建一个本地网站,…...

二、应用层,《计算机网络(自顶向下方法 第7版,James F.Kurose,Keith W.Ross)》
文章目录 零、前言一、应用层协议原理1.1 网络应用的体系结构1.1.1 客户-服务器(C/S)体系结构1.1.2 对等体(P2P)体系结构1.1.3 C/S 和 P2P体系结构的混合体 1.2 进程通信1.2.1 问题1:对进程进行编址(addressing)&#…...

面粉直供系统|基于java和小程序的食品面粉直供系统设计与实现(源码+数据库+文档)
面粉直供系统 目录 基于java和小程序的食品面粉直供系统设计与实现 一、前言 二、系统设计 三、系统功能设计 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取: 博主介绍:✌️大厂码农|毕设布道师&#x…...

十四:java web(6)-- Spring Spring MVC
目录 Spring MVC 1.1 Spring MVC 概述 1.1.1 什么是 MVC 模式 1.1.2 Spring MVC 工作原理 1.2 Spring MVC 核心组件 1.2.1 DispatcherServlet 1.2.2 控制器(Controller) 1.2.3 请求映射(RequestMapping) 1.2.4 视图解析器…...

Java代码实现策略模式处理支付付款业务
1.需求:因为付款功能集成的第三方支付SDK越来越来多不好维护,改用策略模式实现,来代替代码中多余的if else 判断。 2.什么是策略模式? 策略模式(Strategy Pattern)是一种行为型设计模式,它允许在运行时选择算法的行为。该模式将不同的算法封装成独立的策略类,并使这些…...

unity3d————四元数概念
一、定义与表示 四元数是由一个实数部分和三个虚数部分组成,通常表示为q w xi yj zk,其中w是实数,x、y、z是实数系数,i、j、k是虚数单位,满足以下关系: i j k -1ij k,ji -kjk i&…...

spring相关的面试题
Spring 框架是 Java 开发中最常用的框架之一,因此在面试中经常会被问到与 Spring 相关的问题。以下是一些常见的 Spring 面试题及其答案。 基础概念 什么是 Spring 框架? Spring 框架是一个开源的 Java 平台,用于构建企业级应用程序。它提供…...