工厂+策略模式之最佳实践(疾病报卡维护模块API设计)

目录

💻业务场景

🔧应用技术

⚙概要流程

❗开发注意

服务类上标注了 自定义注解 却无法直接利用getDeclaredAnnotation 获取

*Spring代理机制

代理机制的工作原理

代理的工作机制

代理的使用场景

已获取EmrXXXServiceImpl 的Class,如何获取public class EmrXXXServiceImpl extends ServiceImpl implements EmrXXXService 泛型类型:EmrXXX,>

工厂类


💻业务场景

        某医院急诊系统需要于诊断列表关联相关疾病报卡,并且当诊断提交后,支持医生进行报卡的维护,可与HIS系统端的公卫下疾病报卡管理模块进行联动。
        经调研,考虑各个报卡的新增操作的数据流(各个报卡对应的数据库表不一)有出入,相关业务流一致,则可利用设计模式中的策略+工厂模式,以及泛型、反射、注解等技术实现相关上层API的设计。到达代码简洁,避免硬编码,且更易于扩展和支持更多类型的报卡的目的。

        利用自定义报卡标识注解将每个服务的实例化逻辑封装成不同的策略类;利用仿写Spring Bean工厂的方式,通过依赖注入和组件扫描来自动管理 Bean 的创建和查找,利用符合单一性原则自定义注解,服务类自行声明它们所支持的疾病类型,工厂可动态根据已报卡的服务去初始化报卡服务实例。

🔧应用技术

  • 枚举:报卡相关固化数据的常量(提高代码可读性和可维护性、增强类型安全、简化代码)
  • 泛型:API设计,以此提供更加灵活和通用的接口,实现接口通用。注意泛型擦除机制
  • 反射:灵活获取指定类对象,进行类之父类上的泛型Type的获取等操作
  • 注解:是标识各个报卡服务,支持后续Bean工厂初始化集合存储报卡Service Bean
  • 框架:Spring、Mybatis Plus

⚙概要流程

❗开发注意

服务类上标注了 自定义注解 却无法直接利用getDeclaredAnnotation 获取

已知注解于类的声明处,并且继承链无误(Java 的注解默认不会被继承,除非使用 @Inherited 元注解),注解作用范围为 ElementType.TYPE,并且保留策略是 RetentionPolicy.RUNTIME

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SupportedDiseaseType {
    String value();
}

最终定位错误为Spring 使用代理机制来管理 Bean,从而导致注解无法直接通过 getDeclaredAnnotation 获取。特别是当使用 CGLIB 代理时,代理类不会继承原始类的注解。

则利用Spring 提供了 AnnotatedElementUtils 工具类,它可以更可靠地获取注解,包括处理代理类的情况。

SupportedDiseaseType annotation = AnnotatedElementUtils.findMergedAnnotation(service.getClass(), SupportedDiseaseType.class);

*Spring代理机制

Spring 使用代理机制来管理 Bean,主要是为了实现 AOP(面向切面编程)和事务管理等功能。Spring 的代理机制允许在不修改原有代码的基础上,动态地为目标对象添加额外的功能,比如日志记录、性能监控、事务管理等。

代理机制的工作原理

Spring 中的代理有两种常见的方式:

1. JDK 动态代理(基于接口):
   - 通过反射机制创建一个实现目标对象接口的代理类,并将代理类与目标对象绑定。
   - 适用于目标类实现了接口的情况。

2. CGLIB 代理(基于子类):
   - 通过继承目标类,动态创建一个目标类的子类,并在该子类中织入增强代码。
   - 适用于目标类没有实现接口的情况,或者目标类没有实现接口但需要增强的情况。

Spring 通过这两种代理方式在运行时动态地生成代理对象,来增强目标对象的行为。代理对象和目标对象的使用是透明的,开发者只需要关注接口或原有类,Spring 会自动为其注入代理功能。

代理的工作机制

- **生成代理对象**:Spring 使用 JDK 动态代理或 CGLIB 代理创建一个代理对象,这个对象通常是目标对象的一个包装。
- **拦截方法调用**:每次调用代理对象的方法时,Spring 会通过代理对象执行相关的增强逻辑,例如日志、事务、权限控制等。
- **目标方法执行**:增强逻辑执行完毕后,代理对象会调用目标对象的实际方法。

代理的使用场景

1. AOP(面向切面编程):
   - 使用 Spring 的 AOP 可以在运行时动态地为对象添加横切关注点(如日志、事务等),而不修改目标对象的源代码。
   
2. 事务管理:
   - Spring 可以通过代理机制来自动管理数据库事务,确保在方法执行过程中根据需要进行事务的开启、提交或回滚。
   
3. 懒加载:
   - 代理对象可以通过懒加载的方式,在实际调用方法时才去创建目标对象,从而提高系统性能。

已获取EmrXXXServiceImpl 的Class,如何获取public class EmrXXXServiceImpl extends ServiceImpl<EmrXXXMapper, EmrXXX> implements EmrXXXService 泛型类型:EmrXXX

直接上代码!

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public class Main {
    public static void main(String[] args) {
        // 假设你已经通过 AOP 得到了 `EmrXXXServiceImpl` 的 Class 对象
        Class<?> targetClass = EmrXXXServiceImpl.class;

        // 获取父类的类型参数
        Type genericSuperclass = targetClass.getGenericSuperclass();

        // 检查是否是带有泛型的类
        if (genericSuperclass instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;

            // 获取泛型参数类型
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();

            // 这里假设第一个泛型参数是 `EmrXXXMapper`
            // 第二个泛型参数是 `EmrXXX`
            Type secondGenericType = actualTypeArguments[1];

            // 输出泛型类型的类
            if (secondGenericType instanceof Class<?>) {
                Class<?> EmrXXXClass = (Class<?>) secondGenericType;
                System.out.println("泛型中的 EmrXXX 的类是: " + EmrXXXClass.getName());
            }
        }
    }
}

工厂类

工厂模式+策略模式最佳实践

@Slf4j
@Component
public class DiseaseReportCardServiceFactory {

    private final List<DiseaseReportCardService<?>> services;
    private final Map<String, DiseaseReportCardService<?>> serviceCache = new ConcurrentHashMap<>();

    @Autowired
    public DiseaseReportCardServiceFactory(List<DiseaseReportCardService<?>> services) {
        this.services = services;
    }

    @PostConstruct
    public void initialize() {
        for (DiseaseReportCardService<?> service : services) {
            SupportedDiseaseType annotation = AnnotatedElementUtils.findMergedAnnotation(service.getClass(), SupportedDiseaseType.class);
            if (annotation == null) {
                log.error("Service {} 不含 @SupportedDiseaseType 注解", service.getClass().getName());
                continue;
            }
            String diseaseType = annotation.value();
            if (diseaseType == null || diseaseType.isEmpty()) {
                log.error("Service {} 的 @SupportedDiseaseType value为空", service.getClass().getName());
                continue;
            }
            serviceCache.put(diseaseType, service);
        }
    }

    public DiseaseReportCardService<?> getService(String diseaseType) {
        return serviceCache.get(diseaseType);
    }
}

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

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

相关文章

【智行安全】基于Synaptics SL1680的AI疲劳驾驶检测方案

随著车载技术的快速进步&#xff0c;驾驶安全越来越受到重视&#xff0c;而疲劳驾驶是造成交通事故的重要原因之一。传统的驾驶监控技术因精度不足或反应迟缓&#xff0c;无法满足实时监测需求。因此&#xff0c;结合人工智能技术的疲劳驾驶检测系统成为行业新方向&#xff0c;…

Go-知识 注释

Go-知识 注释 行注释块注释包注释结构体&接口注释函数&方法注释废弃注释文档 在 go 语言中注释有两种&#xff0c;行注释和块注释 行注释 使用双斜线 // 开始&#xff0c;一般后面紧跟一个空格。行注释是Go语言中最常见的注释形式&#xff0c;在标准包中&#xff0c;…

2025年阿里云认证改版新消息!2025年阿里云认证考试内容有变!

阿里云认证已经确定在2025年要进行大改&#xff0c;这次改动幅度会比2023年改动更大&#xff0c;2023年主要改变是在考试题型上的变化&#xff0c;这次则主要是考试内容的变化了&#xff01; 2023年阿里云ACP认证考试的改版变化主要有&#xff1a; &#xff08;一&#xff09…

ArrayList 和LinkedList的区别比较

前言 ‌ArrayList和LinkedList的主要区别在于它们的底层数据结构、性能特点以及适用场景。‌ArrayList和LinkedList从名字分析&#xff0c;他们一个是Array&#xff08;动态数组&#xff09;的数据结构&#xff0c;一个是Linked&#xff08;链表&#xff09;的数据结构&#x…

STM32-笔记22-sg90舵机

一、接线 二、实验实现 动手让 SG90 每秒转动一下&#xff0c;0 -> 20 -> 40 -> 100 -> 180 如此循环。 舵机接A6 复制18-呼吸灯&#xff0c;重命名24-sg90舵机 把PWM重命名sg90 打开项目文件 在魔术棒和品上把PWM都去掉&#xff0c;加载sg90文件夹 加载之后…

QT集成intel RealSense 双目摄像头

最近一个小项目&#xff0c;用到了双目相机&#xff0c;选用了Intel的RealSense双目相机。功能很简单&#xff0c;就是识别某一个物体&#xff0c;然后对对这个物体进行操作。具体功能随后再说&#xff0c;这里只介绍QT如何集成IntelRealSense相机&#xff0c;就是下面这个。 首…

前端小案例——520表白信封

前言&#xff1a;我们在学习完了HTML和CSS之后&#xff0c;就会想着使用这两个东西去做一些小案例&#xff0c;不过又没有什么好的案例让我们去练手&#xff0c;本篇文章就提供里一个案例——520表白信封 ✨✨✨这里是秋刀鱼不做梦的BLOG ✨✨✨想要了解更多内容可以访问我的主…

Golang的发展历程

Golang的发展历程可以分为以下几个阶段&#xff1a; 设计阶段&#xff1a;2007年&#xff0c;Google开始研究开发一种新的编程语言&#xff0c;主要出于对C和Java等编程语言的不足之处的反思。经过一年多的研究和讨论&#xff0c;Golang的设计方案得到确定&#xff0c;主要包括…

硬件设计-硬件 EMC 设计规范

目录 引言&#xff1a; 常见原因 总体概念及考虑 布局 屏蔽 滤波 引言&#xff1a; 本规范只简绍 EMC 的主要原则与结论&#xff0c;为硬件工程师们在开发设计中抛砖引玉。 电磁干扰的三要素是干扰源、干扰传输途径、干扰接收器。EMC 就围绕这些 问题进行研究。最基本的…

后端开发-Maven

环境说明&#xff1a; windows系统&#xff1a;11版本 idea版本&#xff1a;2023.3.2 Maven 介绍 Apache Maven 是一个 Java 项目的构建管理和理解工具。Maven 使用一个项目对象模型&#xff08;POM&#xff09;&#xff0c;通过一组构建规则和约定来管理项目的构建&#xf…

C++ 编译过程全解析:从源码到可执行文件的蜕变之旅

引言 C 作为一种广泛应用于系统开发、游戏编程、嵌入式系统等领域的高级编程语言&#xff0c;其代码需要经过编译才能转换为计算机可执行的机器语言。编译过程涵盖多个复杂阶段&#xff0c;每个阶段对最终生成的可执行文件的性能、稳定性及兼容性都有着深远影响。深入理解 C 编…

数据库的概念和操作

目录 1、数据库的概念和操作 1.1 物理数据库 1. SQL SERVER 2014的三种文件类型 2. 数据库文件组 1.2 逻辑数据库 2、数据库的操作 2.1 T-SQL的语法格式 2.2 创建数据库 2.3 修改数据库 2.4 删除数据库 3、数据库的附加和分离 1、数据库的概念和操作 1.1 物理数据库…

【CSS in Depth 2 精译_096】16.4:CSS 中的三维变换 + 16.5:本章小结

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第五部分 添加动效 ✔️【第 16 章 变换】 ✔️ 16.1 旋转、平移、缩放与倾斜 16.1.1 变换原点的更改16.1.2 多重变换的设置16.1.3 单个变换属性的设置 16.2 变换在动效中的应用 16.2.1 放大图标&am…

期权懂|个股期权的流动性如何?

锦鲤三三每日分享期权知识&#xff0c;帮助期权新手及时有效地掌握即市趋势与新资讯&#xff01; 个股期权的流动性如何&#xff1f; 个股期权作为场外交易工具&#xff0c;具有较高的灵活性。场外交易意味着交易双方可以直接协商交易条款&#xff0c;这有助于满足不同投资者的…

关于在M系列的Mac中使用SoftEtherClient软件

1. 前言 本文说明的是在M系列的苹果的MacBook中如何使用SoftetherClient这款软件&#xff0c;是直接在MacOS操作系统中安装连接使用&#xff0c;不是在PD环境或者非ARM架构的Mac中安装使用。 PS&#xff1a;别费劲百度了&#xff0c;很少有相关解决方案的&#xff0c;在国内会…

linux自动化批量分发SSH密钥同时批量测试SSH连接教程(包含自动化脚本代码)

1、检查端口 检查分发对象22端口是否打开 nmap -p22 ip地址如果要批量检查端口可以参考我写的这篇文章&#xff1a;linux自动化一键批量检查主机端口 2、命令行分发密钥原理 Linux分发密钥原理主要涉及SSH&#xff08;Secure Shell&#xff09;协议&#xff0c;该协议用于…

vue3学习笔记(9)-pinia、storeToRefs、getters

1.新的集中式状态&#xff08;数据&#xff09;管理库&#xff0c;redux vuex pinia 搭建 2.ref拆包 如果在reactive里面定义ref&#xff0c;则打印c时&#xff0c;无需.value 他自动拆包&#xff0c;如果直接在外面定义的ref则需要.value,他没有拆包 3.pinia存储读取数据 存…

Oracle 11G还有新BUG?ORACLE 表空间迷案!

前段时间遇到一个奇葩的问题&#xff0c;在开了SR和oracle support追踪两周以后才算是有了不算完美的结果&#xff0c;在这里整理出来给大家分享。 1.问题描述 12/13我司某基地MES全厂停线&#xff0c;系统卡死不可用&#xff0c;通知到我排查&#xff0c;查看alert log看到是…

深度学习:基于MindSpore NLP的数据并行训练

什么是数据并行&#xff1f; 数据并行&#xff08;Data Parallelism, DP&#xff09;的核心思想是将大规模的数据集分割成若干个较小的数据子集&#xff0c;并将这些子集分配到不同的 NPU 计算节点上&#xff0c;每个节点运行相同的模型副本&#xff0c;但处理不同的数据子集。…

机器学习-高斯混合模型

文章目录 高斯混合模型对无标签的数据集&#xff1a;使用高斯混合模型进行聚类对有标签的数据集&#xff1a;使用高斯混合模型进行分类总结实战 高斯混合模型 对无标签的数据集&#xff1a;使用高斯混合模型进行聚类 对有标签的数据集&#xff1a;使用高斯混合模型进行分类 总结…