SpringBoot AnnotationFormatterFactory接口+自定义注解实现类型转换

news2024/7/4 5:40:32

参考资料

  1. 自定义AnnotationFormatterFactory实现注解方式类型转换
  2. Spring MVC 基于AnnotationFormatterFactory接口实现自定义的规则

目录

  • 一. 前期准备
    • 1.1. 自定义转换标记注解
    • 1.2 入参form
  • 二. 实现AnnotationFormatterFactory接口,构建格式化Factory
    • 2.1 code补全Factory
    • 2.2 身份证号处理Factory
  • 三. 将自定义的格式化Factory添加到Spring中
  • 四. 效果
  • 五. 注意点


一. 前期准备

1.1. 自定义转换标记注解

⏹code补全注解,标记code补全到指定的位数

import java.lang.annotation.*;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface CompletedCodeAnnotation {
	
	// 补全的位数
    int completedLength();
}

⏹身份证处理注解,可将身份证号处理为一个实体类

import java.lang.annotation.*;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
public @interface IdentityCardHandleAnnotation {
    
}

1.2 入参form

import lombok.Data;

@Data
public class Test27Form {

    private String id = "110120119";
	
	// 若code的长度不为5,则左补全至5位
    @CompletedCodeAnnotation(completedLength = 5)
    private String applyNo;
}
import lombok.Builder;
import lombok.ToString;

import java.time.LocalDate;

// 通过lombok的@Builder注解来实现构建者模式创建对象
@Builder
@ToString
public class PersonInfo {

    private String id;

    private String sex;

    private LocalDate birthday;
}

二. 实现AnnotationFormatterFactory接口,构建格式化Factory

2.1 code补全Factory

import org.apache.commons.lang3.StringUtils;
import org.springframework.format.AnnotationFormatterFactory;
import org.springframework.format.Formatter;
import org.springframework.format.Parser;
import org.springframework.format.Printer;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.text.ParseException;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;

@Component
public class CompletedCodeFormatterFactory implements AnnotationFormatterFactory<CompletedCodeAnnotation> {

    private final Set<Class<?>> types = new HashSet<>();
	
	// 指定格式化的Field的类型为String类型
    @Override
    public Set<Class<?>> getFieldTypes() {
        types.add(String.class);
        return types;
    }

    @Override
    public Printer<?> getPrinter(CompletedCodeAnnotation annotation, Class<?> fieldType) {
        return null;
    }

    @Override
    public Parser<?> getParser(CompletedCodeAnnotation annotation, Class<?> fieldType) {
		
		// 获取注解上标记的补全长度
        int completedLength = annotation.completedLength();
        return new CompletedCodeFormatter(completedLength);
    }
	
	// 定义一个内部类,更好的聚合
    private class CompletedCodeFormatter implements Formatter<String> {

        private int completedLength;

        private CompletedCodeFormatter(int completedLength) {
            this.completedLength = completedLength;
        }

        @Override
        public String parse(String fieldValue, Locale locale) throws ParseException {
            if (ObjectUtils.isEmpty(fieldValue)) {
                return "";
            }
			
			// 使用apache.commons.lang3包补全code
            return StringUtils.leftPad(fieldValue, completedLength, '0');
        }

        @Override
        public String print(String object, Locale locale) {
            return null;
        }
    }
}

2.2 身份证号处理Factory

import org.springframework.format.AnnotationFormatterFactory;
import org.springframework.format.Formatter;
import org.springframework.format.Parser;
import org.springframework.format.Printer;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.text.ParseException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;

@Component
public class IdentityCardFormatterFactory implements AnnotationFormatterFactory<IdentityCardHandleAnnotation> {

    private final Set<Class<?>> types = new HashSet<>();
	
	// 指定格式化的Field的类型为自定义的PersonInfo类型
    @Override
    public Set<Class<?>> getFieldTypes() {
        types.add(PersonInfo.class);
        return types;
    }

    @Override
    public Printer<?> getPrinter(IdentityCardHandleAnnotation annotation, Class<?> fieldType) {
        return null;
    }

    @Override
    public Parser<?> getParser(IdentityCardHandleAnnotation annotation, Class<?> fieldType) {
        return new IdentityCardHandle();
    }

    private class IdentityCardHandle implements Formatter<PersonInfo> {

        @Override
        public PersonInfo parse(String identityId, Locale locale) throws ParseException {
            if (ObjectUtils.isEmpty(identityId)) {
                return null;
            }
			
			// 通过构建者模式创建一个PersonInfo类
            return PersonInfo.builder()
                    .id(identityId)
                    .birthday(this.getBirthday(identityId))
                    .sex(this.getSex(identityId))
                    .build();
        }

        @Override
        public String print(PersonInfo object, Locale locale) {
            return null;
        }
		
		// 从身份证号中获取出出生日期,转换为LocalDate格式
        private LocalDate getBirthday(String identityId) throws ParseException {
            String birthdayStr = identityId.substring(6, 14);
            return LocalDate.parse(birthdayStr, DateTimeFormatter.ofPattern("yyyyMMdd"));
        }

        private String getSex(String identityId) {
            // 偶数:女,奇数:男
            return identityId.charAt(16) % 2 == 0 ? "女" : "男";
        }
    }
}

三. 将自定义的格式化Factory添加到Spring中

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.AnnotationFormatterFactory;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.Map;

@Configuration
public class MyWebConfigurer implements WebMvcConfigurer {

    @Autowired
    private ApplicationContext applicationContext;

    @Override
    public void addFormatters(FormatterRegistry registry) {
		// 获取Ioc容器中实现了AnnotationFormatterFactory接口的所有的Bean
        Map<String, AnnotationFormatterFactory> beansOfType = applicationContext.getBeansOfType(AnnotationFormatterFactory.class);
        beansOfType.forEach((key, value) -> registry.addFormatterForFieldAnnotation(value));
    }
}

四. 效果

⏹前台

  • 需要使用表单提交数据,不能提交json数据
const formData = {
    applyNo: '10',
    personInfo: '370281199412164456'
};

$.ajax({
    url: "/test27/test",
    type: 'POST',
    data: formData,
    success: function (data, status, xhr) {
        console.log(data);
    }
});

⏹后台

@PostMapping("/test")
@ResponseBody
public Test27Form test(Test27Form form, @IdentityCardHandleAnnotation PersonInfo personInfo) {
    System.out.println(form);
    System.out.println(personInfo);
    return form;
}

可以看到

💪前台提交的applyNo不满5位,已经被补全到5位数
💪前台提交的personInfo(身份证号),已经被处理为一个entity对象了

五. 注意点

  1. 上述转换方式只适用于表单提交数据或者url传参提交数据这两种方式,
  2. 如果是前台使用contentType: "application/json",后台使用@RequestBody来接收数据的话,不适用于此处理。

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

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

相关文章

【7】一篇文章学习 Linux 中一些硬核的常用知识

目录 一、systemctl二、软链接三、日期&#xff08;date 命令&#xff09;四、Linux 的时区(1) 修改时区(2) ntp 五、IP 地址六、主机名七、域名解析八、配置 Linux 的固定 IP 地址(1) 在 VMwareWorkstation 中配置 IP 地址网关和网段&#xff08;IP 地址的范围&#xff09;(2)…

[陇剑杯 2021]之Misc篇(NSSCTF)刷题记录④

NSSCTF-Misc篇-[陇剑杯 2021] jwt&#xff1a;[陇剑杯 2021]jwt&#xff08;问1&#xff09;[陇剑杯 2021]jwt&#xff08;问2&#xff09;[陇剑杯 2021]jwt&#xff08;问3&#xff09;[陇剑杯 2021]jwt&#xff08;问4&#xff09;[陇剑杯 2021]jwt&#xff08;问5&#xff0…

洗地性价比高的是哪款?性价比高的洗地机推荐

在如今人工智能随处可见的时代&#xff0c;洗地机已经成为了我们家庭清洁的得力助手&#xff0c;它用高效便捷的清洁方式&#xff0c;对于地面的灰尘或者地板之间的缝隙里的细小垃圾&#xff0c;能够快速清理&#xff0c;省时省力。然而&#xff0c;对于很多消费者来说&#xf…

一文带你学会如何写一份糟糕透顶的简历

我们每个人几乎都会面对找工作这件事&#xff0c;而找工作或者说求职首先就是要写一份简历。今天狗哥将以一个不同的视角带你写一份无与伦比&#xff0c;糟糕透顶的求职简历&#xff0c;说实话&#xff0c;其实几年前&#xff0c;我就是这么写的。 目录 1. 文件名 2. 基本信…

AttributeError: ‘ChatGLMModel‘ object has no attribute ‘prefix_encoder‘

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

基于蛋白-配体复合物药效团药物设计(Pharmacophore)

基于蛋白-配体复合物药效团药物设计&#xff08;Pharmacophore&#xff09; step 1.蛋白-配体复合物准备 点击File-->Import Structures导入之前已经下载好的1IEP.pdb&#xff08;Abl蛋白和Imatinib的晶体复合物&#xff09; 蛋白准备&#xff1a;点击Tasks--->Protei…

18、越狱

一、越狱 1.1 越狱 通过iOS系统安全启动链漏洞,从而禁止掉信任链中负责验证的组件.拿到iOS系统最大权限Root权限 1.2 iOS系统安全启动链 当启动一台iOS设备时,系统首先会从只读的ROM中读取初始化指令,也就是系统的引导程序(事实上所有的操作系统启动时都要经过这一步,只是过程…

Pytorch深度学习笔记(十一)卷积神经网络CNN

目录 1.概述 2.单通道卷积 3.多通道卷积 4.卷积层常见的参数 5.代码实现&#xff08;卷积神经网络训练MNIST数据集&#xff09; 推荐课程&#xff1a;10.卷积神经网络&#xff08;基础篇&#xff09;_哔哩哔哩_bilibili 1.概述 全连接神经网络&#xff1a;完全由线性层串…

最佳实践|如何写出简单高效的 Flink SQL?

摘要&#xff1a;本文整理自阿里巴巴高级技术专家、Apache Flink PMC 贺小令&#xff0c;在 Flink Forward Asia 2022 生产实践专场的分享。本篇内容主要分为三个部分&#xff1a; 1. Flink SQL Insight 2. Best Practices 3. Future Works Tips&#xff1a;点击「阅读原文」查…

android之 Launcher改造仿桌面排版的效果

一&#xff0c;背景 1.1 新接手一个灯光控制项目&#xff0c;其页面和效果还是比交复杂的&#xff0c;其中一个功能就是仿苹果桌面来排版灯具&#xff0c;支持拖拽&#xff0c;分组&#xff0c;分页。 拖动图标的时候判断是否空白位置还是已经有占位了&#xff0c;有的话就把…

pikachu靶场-RCE

RCE漏洞概述 可以让攻击者直接向后台服务器远程注入操作系统命令或者代码&#xff0c;从而控制后台系统。 远程系统命令执行 命令执行漏洞&#xff08;Command Execution&#xff09;即黑客可以直接在Web应用中执行系统命令&#xff0c;从而获取敏感信息或者拿下shell权限 更…

Linux离线状态下安装cuda、cudnn、cudatoolkit

目录 1. 下载与安装说明2. CUDA安装3. cuDNN安装4. cudatoolkit安装5. 测试安装成功 1. 下载与安装说明 工具包下载地址 CUDA历史版本下载地址&#xff1a;https://developer.nvidia.com/cuda-toolkit-archivecuDNN历史版本下载地址&#xff1a;https://developer.nvidia.com/r…

logback日志框架集成方式

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、logback是什么&#xff1f;二、使用步骤1.使用方式控制台输出配置文件输出配置html输出配置定期删除配置方式 总结 前言 提示&#xff1a;这里可以添加本文…

C++每日一练:最长递增区间 阿波罗的魔力宝石 投篮

文章目录 前言一、最长递增区间二、阿波罗的魔力宝石三、投篮总结 前言 今天的题太简单&#xff0c;甚至 “最长递增区间” 和 “投篮” 就是一个问题。实在没事干&#xff0c;也给做了&#xff01;直接上代码算了… 提示&#xff1a;以下是本篇文章正文内容 一、最长递增区间…

LSSANet:一种用于肺结节检测的长、短切片感知网络

文章目录 LSSANet: A Long Short Slice-Aware Network for Pulmonary Nodule Detection摘要方法Long Short Slice GroupingLong Short Slice-Aware Network 实验结果 LSSANet: A Long Short Slice-Aware Network for Pulmonary Nodule Detection 摘要 提出了一个长短片感知网…

【JAVA程序设计】(C00130)基于SpringBoot的社区养老医疗综合服务系统

基于SpringBoot的社区养老医疗综合服务系统 项目简介项目获取开发环境项目技术运行截图 项目简介 基于基于SpringBoot的社区养老医疗综合服务系统共分为三个角色&#xff1a;系统管理员、医生、用户 管理员角色包含以下功能&#xff1a; 用户管理、角色管理、部门管理、字典管…

【Java EE】-JavaScript详解

作者&#xff1a;学Java的冬瓜 博客主页&#xff1a;☀冬瓜的主页&#x1f319; 专栏&#xff1a;【JavaEE】 分享: 且视他人如盏盏鬼火&#xff0c;大胆地去走你的道路。——史铁生《病隙碎笔》 主要内容&#xff1a;HTML中引入JS的三种方式。JS语法分析&#xff0c;JS是动态弱…

【Linux高级篇】什么是shell脚本,什么是shell变量

目录 &#x1f341;什么是shell &#x1f342;什么是shell脚本 &#x1f342;shell脚本能做什么 &#x1f342;学习shell需要哪些知识 &#x1f342;shell基本规范 &#x1f342;shell脚本五种运行方式 &#x1f341;shell变量 &#x1f342;变量命名规范 &#x1f342;shell变…

远程登录--SSH 你值得拥有

目录 一&#xff1a;SSH服务详解 1.什么是SSH 2.SSH服务认证类型 1&#xff09;基于口令认证 2&#xff09;基于密钥认证 3.SSH安装 二: 配置ssh服务端 1.ssh配置文件 2. ssh配置文件主要条目介绍 三:使用ssh客户端程序 1.使用ssh命令远程登录 ​2.使用scp远程复制 …

8086汇编之DIV除法指令

2023年4月22日&#xff0c;周六晚上。 今晚写汇编作业的时候&#xff0c;遇到了DIV指令&#xff0c;于是把学到的知识记录成一篇博客。此外&#xff0c;刚刚已经写了一篇关于MUL指令的博客了。 除数有8位和16位种&#xff0c;存放在寄存器或者内存中。 当除数为8位&#xff1a…