Spring Formatter 格式化接口源码解析:从 Printer/Parser 到日期格式化实战
Spring Formatter 格式化接口源码解析从 Printer/Parser 到日期格式化实战【免费下载链接】source-code-hunter 从源码层面剖析挖掘互联网行业主流技术的底层实现原理为广大开发者 “提升技术深度” 提供便利。目前开放 Spring 全家桶Mybatis、Netty、Dubbo 框架及 Redis、Tomcat 中间件等项目地址: https://gitcode.com/doocs/source-code-hunter导读org.springframework.format.FormatterT是 Spring 类型转换与格式化体系spring-context 的org.springframework.format包中的核心接口它同时继承PrinterT与ParserT实现了对象 → 字符串与字符串 → 对象的双向能力。本文基于 doocs/source-code-hunter 仓库中的源码笔记从接口定义出发逐层剖析 Printer、Parser、Formatter 三者关系并结合DateFormatter、DateTimeFormatAnnotationFormatterFactory、Joda-Time 系实现等典型类讲解 Spring 如何借助这些接口完成 Web 层参数绑定、DateTimeFormat注解驱动的日期格式化最终落到FormatterRegistry/ConversionService的注册与调用链路上。读完本文你将掌握 Spring 格式化接口族的完整脉络并能独立实现自定义 Formatter 接入 Spring MVC。一、Formatter 接口总览类全路径org.springframework.format.Formatter。public interface FormatterT extends PrinterT, ParserT { }该接口本身是一个空接口marker interface全部能力都来自其继承的两个父接口父接口职责核心方法PrinterT对象 → 字符串输出/展示String print(T object, Locale locale)ParserT字符串 → 对象输入/解析T parse(String text, Locale locale) throws ParseException从设计上看Formatter把打印与解析这对互逆操作收敛到一个类型上形成对某个领域类型T的完整格式化描述。方法签名中均携带Locale参数说明格式化天然具备国际化i18n能力——不同区域下日期、数字的呈现方式可以不同。关联文档中明确指出该接口继承了 printer 和 parser 两个接口比较常见的有DateFormatter就是继承这个接口。这也是整个格式化体系中最重要的一个实现下文会重点展开。二、两个父接口Printer 与 Parser2.1 Printer把对象打印成字符串类全路径org.springframework.format.Printer类作用对象转换成字符串。FunctionalInterface public interface PrinterT { /** * Print the object of type T for display. * 打印对象 * param object the instance to print * param locale the current user locale * return the printed text string */ String print(T object, Locale locale); }要点标注了FunctionalInterface是函数式接口可用 Lambda / 方法引用快速实现print(T object, Locale locale)接收待展示的对象与当前用户区域返回展示用字符串典型用途Web 页面上将Date、Number等对象渲染为符合地区习惯的文本。2.2 Parser把字符串解析成对象类全路径org.springframework.format.Parser类作用字符串转换成 java 对象。FunctionalInterface public interface ParserT { /** * Parse a text String to produce a T. * 将字符串转换成对象 * param text the text string * param locale the current user locale * return an instance of T * throws ParseException when a parse exception occurs in a java.text parsing library * throws IllegalArgumentException when a parse exception occurs */ T parse(String text, Locale locale) throws ParseException; }要点同样是FunctionalInterfaceparse(String text, Locale locale)将用户提交的文本转换为T类型实例是 MVC 层表单/请求参数绑定的关键入口异常约定java.text 解析库抛ParseException其余情况抛IllegalArgumentException两个接口分别对应Formatter的出与入Parser方向的实现类如 Spring-DateTimeParserPrinter方向的实现类如 Spring-MillisecondInstantPrinter。从源码结构看Printer与Parser均为独立顶层接口为不需要双向能力的场景如只读展示提供了更细粒度的抽象选择。三、Formatter 的典型实现DateFormatter在 Spring 的org.springframework.format.datetime包中DateFormatter implements FormatterDate是Formatter接口最经典的落地实现。它完整体现了接口设计意图打印方向print(Date date, Locale locale)内部通过DateFormat如SimpleDateFormat把Date格式化为字符串解析方向parse(String text, Locale locale)内部通过DateFormat.parse(text)把字符串解析为Date同时支持三种配置维度stylesetStylePattern如S-对应 SHORT 风格日期、ISOsetIso遵循DateTimeFormat.ISO枚举、patternsetPattern自定义如yyyy-MM-dd HH:mm:ss格式串。DateFormatter之所以被反复强调是因为它不仅是Formatter的直接实现还被DateTimeFormatAnnotationFormatterFactory内部复用见第五节形成了注解 → 工厂 → Formatter的完整链路。四、注册与调度Formatter 如何进入 Spring 体系Formatter本身只是接口要让其生效必须注册进FormatterRegistryConversionService的子接口Spring 会在需要类型转换时按目标类型查找并调用对应的 FormatterFormattingConversionService实现FormatterRegistry提供addFormatter(Formatter?)、addFormatterForFieldType(Class?, Formatter?)、addFormatterForFieldAnnotation(AnnotationFormatterFactory?)等注册入口Spring MVC 启动时会通过FormattingConversionServiceFactoryBean/WebConversionService自动装配一批内置 Formatter如DateFormatter、NumberFormatAnnotationFormatterFactory等请求到达后WebDataBinder借助ConversionService完成字符串请求参数 → 方法参数对象的转换parse被调用渲染视图时print被调用。由此可以看出Formatter在整个 Spring 类型体系中的位置它比PropertyEditor更类型安全、比底层Converter多了 Locale 国际化上下文是 Web 层格式化的首选抽象。开发者自定义类型也可以通过实现FormatterT并注入FormatterRegistry无缝接入。五、注解驱动的格式化AnnotationFormatterFactory 与 DateTimeFormat5.1 工厂接口定义类全路径org.springframework.format.AnnotationFormatterFactory。public interface AnnotationFormatterFactoryA extends Annotation { /** * The types of fields that may be annotated with the A annotation. * 字段类型 */ SetClass? getFieldTypes(); /** * Get the Printer to print the value of a field of {code fieldType} annotated with * {code annotation}. * 通过注解和字段类型获取输出接口 */ Printer? getPrinter(A annotation, Class? fieldType); /** * Get the Parser to parse a submitted value for a field of {code fieldType} * annotated with {code annotation}. * 通过注解和字段类型获取解析接口 */ Parser? getParser(A annotation, Class? fieldType); }该工厂把注解信息与字段类型作为两个维度动态决定返回哪个 Printer / Parser。当字段上标注了某注解A且字段类型命中getFieldTypes()时Spring 就会调用工厂获取对应的格式化组件从而实现了注解驱动 类型匹配的声明式格式化。5.2 日期注解工厂实现类全路径org.springframework.format.datetime.DateTimeFormatAnnotationFormatterFactory它是AnnotationFormatterFactoryDateTimeFormat的典型实现支持Date、Calendar、Long三类字段static { SetClass? fieldTypes new HashSet(4); // 加入字段类型 fieldTypes.add(Date.class); fieldTypes.add(Calendar.class); fieldTypes.add(Long.class); FIELD_TYPES Collections.unmodifiableSet(fieldTypes); }其核心getFormatter方法负责组装DateFormatter读取注解上的style、iso、pattern三个配置项并映射到 Formatterprotected FormatterDate getFormatter(DateTimeFormat annotation, Class? fieldType) { DateFormatter formatter new DateFormatter(); // style String style resolveEmbeddedValue(annotation.style()); // 判断时间格式是否存在 if (StringUtils.hasLength(style)) { formatter.setStylePattern(style); } // iso 设置 formatter.setIso(annotation.iso()); // date time pattern String pattern resolveEmbeddedValue(annotation.pattern()); // 设置 if (StringUtils.hasLength(pattern)) { formatter.setPattern(pattern); } return formatter; }注意resolveEmbeddedValue工厂继承自EmbeddedValueResolutionSupport支持解析占位符如${date.pattern:yyyy-MM-dd}可以把格式化模式外置到配置文件中。getPrinter与getParser均委托给getFormatter因为DateFormatter本身同时具备双向能力一个实例即可复用。对应到实际使用实体/POJO 字段可以这样声明public class User { DateTimeFormat(pattern yyyy-MM-dd HH:mm:ss) private Date createTime; DateTimeFormat(iso DateTimeFormat.ISO.DATE) private LocalDate birthday; DateTimeFormat(style S-) private Date lastLogin; }请求绑定阶段2026-09-19 08:30:00通过DateFormatter.parse被解析为Date注入createTime渲染阶段Date对象通过DateFormatter.print按配置格式输出到视图。5.3 其他注解工厂类似机制同样存在于数字格式化中NumberFormatAnnotationFormatterFactory处理NumberFormat注解内部返回NumberFormatter。整个org.springframework.format包围绕注解 类型这一模式为日期、数字等常见类型提供了开箱即用的格式化能力。六、Joda-Time 方向的 Formatter 应用仓库补充在早期 Spring 版本中org.springframework.format.datetime.joda包还提供了一套基于 Joda-Time 的格式化实现仓库笔记中有两个代表性类可作佐证6.1 DateTimeParser字符串 → Joda DateTime类全路径org.springframework.format.datetime.joda.DateTimeParser实现ParserDateTimepublic final class DateTimeParser implements ParserDateTime { private final DateTimeFormatter formatter; public DateTimeParser(DateTimeFormatter formatter) { this.formatter formatter; } Override public DateTime parse(String text, Locale locale) throws ParseException { // DateTimeFormatter 转换字符串时间类型 return JodaTimeContextHolder.getFormatter(this.formatter, locale).parseDateTime(text); } }要点内部持有 JodaDateTimeFormatter通过JodaTimeContextHolder结合当前Locale获取适配的格式化器再调用parseDateTime完成解析。可见Parser接口对第三方时间库同样开放。6.2 MillisecondInstantPrinter时间戳 → 字符串类全路径org.springframework.format.datetime.joda.MillisecondInstantPrinter实现PrinterLongpublic final class MillisecondInstantPrinter implements PrinterLong { private final DateTimeFormatter formatter; public MillisecondInstantPrinter(DateTimeFormatter formatter) { this.formatter formatter; } Override public String print(Long instant, Locale locale) { // DateTimeFormatter .print return JodaTimeContextHolder.getFormatter(this.formatter, locale).print(instant); } }要点它以Long毫秒时间戳为输入类型将其格式化为可读字符串恰好覆盖了DateTimeFormatAnnotationFormatterFactory中Long.class这一字段类型的打印需求。这两个类印证了Formatter/Printer/Parser接口族任意领域类型 Locale 感知的通用设计。七、实战自定义一个 Formatter 并接入 Spring MVC掌握了接口族结构后实现自定义格式化组件只需三步步骤 1实现 Formatter 接口public class PhoneNumberFormatter implements FormatterPhoneNumber { Override public String print(PhoneNumber object, Locale locale) { // 输出格式化138****1234 return object.getAreaCode() - object.getNumber(); } Override public PhoneNumber parse(String text, Locale locale) throws ParseException { // 解析输入138-12345678 - PhoneNumber String[] parts text.split(-); if (parts.length ! 2) { throw new ParseException(电话号码格式应为 area-number, 0); } return new PhoneNumber(parts[0], parts[1]); } }步骤 2注册到 FormatterRegistryConfiguration public class FormatConfig implements WebMvcConfigurer { Override public void addFormatters(FormatterRegistry registry) { registry.addFormatter(new PhoneNumberFormatter()); } }步骤 3在 Controller 中使用GetMapping(/user) public String getUser(RequestParam(phone) PhoneNumber phone) { // 请求 /user?phone010-88888888 时phone 参数已被自动解析 return phone.getAreaCode() | phone.getNumber(); }自此Spring MVC 的参数绑定与视图渲染都会自动调用你的 Formatter与内置的DateFormatter走完全相同的调度链路。八、总结org.springframework.format.Formatter是 Spring 格式化体系的总接口它将Printer对象 → 字符串与Parser字符串 → 对象双向能力合二为一并通过FormatterRegistry/ConversionService融入 Spring MVC 的参数绑定与视图渲染流程接口层FormatterT extends PrinterT, ParserT两个父接口均为FunctionalInterface签名携带Locale以支持国际化实现层DateFormatter是FormatterDate的代表实现支持 style / ISO / pattern 三种格式配置注解驱动层AnnotationFormatterFactoryA按注解 字段类型返回 Printer/ParserDateTimeFormatAnnotationFormatterFactory为Date、Calendar、Long字段提供日期格式化扩展层Joda-Time 方向的DateTimeParser、MillisecondInstantPrinter展示了接口族对任意领域类型的适配能力。如需深入阅读仓库中的原始源码笔记可依次参阅 Spring-Formatter、Spring-Printer、Spring-Parser、Spring-AnnotationFormatterFactory 及 Spring-DateTimeFormatAnnotationFormatterFactory配合 Parser 类图 与 DateTimeFormatAnnotationFormatterFactory 类图 可快速建立整体认知。【免费下载链接】source-code-hunter 从源码层面剖析挖掘互联网行业主流技术的底层实现原理为广大开发者 “提升技术深度” 提供便利。目前开放 Spring 全家桶Mybatis、Netty、Dubbo 框架及 Redis、Tomcat 中间件等项目地址: https://gitcode.com/doocs/source-code-hunter创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

2026年AMD显卡驱动安装失败原因与精准修复指南

2026年AMD显卡驱动安装失败原因与精准修复指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/6 13:23:57 阅读更多 →
无人机城市巡检系统技术方案:从架构设计到落地实践

无人机城市巡检系统技术方案:从架构设计到落地实践

简介:这套PPT系统梳理无人机城市巡检系统的完整建设方案,面向智慧城市、城管执法、自然资源监测及电力水利巡检领域的解决方案工程师、产品经理与项目管理人员。内容围绕无人机机场、AI算法应用、管理后台与小程序四大部分展开,清晰呈现从航线…

2026/10/21 12:15:17 阅读更多 →
Cline 评测:TaoToken 实测 5 个仓库的 Context 命中率

Cline 评测:TaoToken 实测 5 个仓库的 Context 命中率

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/5 1:50:31 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/11/5 12:28:00 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/11/6 9:29:27 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/11/5 12:28:01 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/11/5 12:28:02 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/11/6 8:08:42 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/11/5 19:38:59 阅读更多 →

日新闻

周新闻

月新闻