资讯详情

Spring 格式化体系源码解析:Printer 接口如何将对象转换为字符串

📅 2026/9/20 1:33:02 | 华诺云谱 👁 阅读
Spring 格式化体系源码解析:Printer 接口如何将对象转换为字符串
Spring 格式化体系源码解析Printer 接口如何将对象转换为字符串【免费下载链接】source-code-hunter 从源码层面剖析挖掘互联网行业主流技术的底层实现原理为广大开发者 “提升技术深度” 提供便利。目前开放 Spring 全家桶Mybatis、Netty、Dubbo 框架及 Redis、Tomcat 中间件等项目地址: https://gitcode.com/doocs/source-code-hunter本文围绕 Spring 框架org.springframework.format包中的PrinterT接口展开源码级剖析说明它作为「对象 → 字符串」转换核心在 Spring 类型转换与格式化体系中的定位并结合本仓库对Parser、Formatter、AnnotationFormatterFactory及DateTimeFormatAnnotationFormatterFactory等配套组件的解析帮助读者从接口定义、典型实现到注解驱动场景完整掌握 Spring 格式化 SPI 的设计思路与实际用法。一、Printer 接口定义对象到字符串的输出契约Printer是 Spring 格式化Format体系中最基础的一个 SPI 接口其职责非常单一将类型为 T 的对象转换成适合展示的字符串。类全路径: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); }该接口声明于 Spring-Printer.md完整代码见上。1.1 注解FunctionalInterface接口上标注了FunctionalInterface说明它只包含一个抽象方法print(T object, Locale locale)因此可以放心使用 Lambda 表达式或方法引用来快速创建Printer实例。这在注册自定义格式化器时非常实用例如在 Spring 配置中可以用一行 Lambda 注册一个日期输出器。1.2 方法签名print(T object, Locale locale)参数 / 返回值类型含义objectT需要被打印格式化输出的对象实例localeLocale当前用户的本地化环境Locale用于输出受语言、地区影响的格式如日期格式、数字格式等返回值String打印格式化后的文本字符串Locale参数是格式化场景的关键设计同一对象在不同地区应展示为不同的字符串。例如日期2026-09-19在Locale.US下通常显示为Sep 19, 2026而在Locale.CHINA下则显示为2026年9月19日。Printer接口将这一差异显式暴露给实现者处理。二、Printer 在 Spring Format SPI 中的位置Printer并非孤立存在它和Parser、Formatter共同构成了 Spring 格式化 SPI 的完整闭环PrinterT负责「对象 → 字符串」即输出/展示方向ParserT负责「字符串 → 对象」即解析/输入方向FormatterT同时继承两者是一个既能输出又能解析的组合接口。这一点在仓库配套文档 Spring-Formatter.md 中有直接体现原文明确指出该接口继承了 printer 和 parser 两个接口并举例DateFormatter就是继承这个接口的典型实现public interface FormatterT extends PrinterT, ParserT { }与之对应的 Spring-Parser.md 定义了反向转换契约其方法与print一一对应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; }可以这样理解两个方向的关系当用户提交表单时Spring 用Parser把提交的字符串解析为对象绑定请求参数当把对象回显到页面上时Spring 用Printer把对象输出为字符串。二者配合才能完成一次完整的展示-编辑-提交往返。三、典型实现解析MillisecondInstantPrinter仓库 Printer 实现目录 收录了一个PrinterLong的典型实现——org.springframework.format.datetime.joda.MillisecondInstantPrinter它负责将毫秒时间戳Long格式化为字符串public final class MillisecondInstantPrinter implements PrinterLong { private final DateTimeFormatter formatter; /** * Create a new ReadableInstantPrinter. * param formatter the Joda DateTimeFormatter instance */ 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); } }这个实现完整展示了Printer接口实现类的两个典型特征构造器注入格式化器实现类本身不持有如何格式化的硬编码逻辑而是通过构造器接收一个 Joda-Time 的DateTimeFormatter将具体格式策略与输出逻辑解耦结合 Locale 动态获取格式化器print方法内部通过JodaTimeContextHolder.getFormatter(this.formatter, locale)根据当前Locale获取可能是线程上下文中的格式化器后执行print(instant)从而让同一实现类在不同地区输出不同格式。从源码结构看MillisecondInstantPrinter是Printer接口小而专设计哲学的典型样本一个接口对应一个明确的输出方向每个实现只负责一种类型的格式化。四、注解驱动的 PrinterDateTimeFormatAnnotationFormatterFactory在真实业务中开发者通常不会直接手动调用Printer而是通过注解如DateTimeFormat声明字段格式由 Spring 框架在底层自动完成格式化。连接「注解」与「Printer」的桥梁就是AnnotationFormatterFactory。仓库文档 Spring-AnnotationFormatterFactory.md 给出了该工厂接口的定义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); }可以看到AnnotationFormatterFactory的三个方法各司其职getFieldTypes()声明哪些字段类型可以挂载该注解getPrinter(annotation, fieldType)根据注解实例与字段类型返回对应的Printer——这正是Printer在注解驱动场景下的获取入口getParser(annotation, fieldType)返回对应的Parser用于反方向解析。针对DateTimeFormat注解仓库收录了具体实现 Spring-DateTimeFormatAnnotationFormatterFactory.md。该工厂继承EmbeddedValueResolutionSupport并实现AnnotationFormatterFactoryDateTimeFormat其中getPrinter与getParser均委托给同一个getFormatter方法返回DateFormatter一个FormatterDate即同时具备Printer与Parser能力public class DateTimeFormatAnnotationFormatterFactory extends EmbeddedValueResolutionSupport implements AnnotationFormatterFactoryDateTimeFormat { private static final SetClass? FIELD_TYPES; Override public SetClass? getFieldTypes() { return FIELD_TYPES; } Override public Printer? getPrinter(DateTimeFormat annotation, Class? fieldType) { return getFormatter(annotation, fieldType); } Override public Parser? getParser(DateTimeFormat annotation, Class? fieldType) { return getFormatter(annotation, fieldType); } protected 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; } static { SetClass? fieldTypes new HashSet(4); fieldTypes.add(Date.class); fieldTypes.add(Calendar.class); fieldTypes.add(Long.class); FIELD_TYPES Collections.unmodifiableSet(fieldTypes); } }从该实现可以归纳出Printer在注解工厂中的装配规律支持字段类型固定静态初始化块中声明了Date、Calendar、Long毫秒时间戳三类字段可以使用DateTimeFormat并通过Collections.unmodifiableSet包装为不可变集合三种格式配置优先级明确style如S-样式模式→isoISO 标准格式→pattern自定义模式其中style与pattern均先经过resolveEmbeddedValue解析占位符支持${...}配置引用再通过StringUtils.hasLength判空后生效Printer 与 Parser 共享同一格式化器由于DateFormatter本身实现了FormatterDatePrinter Parser因此getPrinter与getParser返回同一个实例保证输出格式与解析格式完全对称避免出现显示为 A 格式、解析时却要求 B 格式的不一致。实际使用示意基于上述源码一个典型的使用方式是在 DTO/实体字段上标注DateTimeFormat由 Spring 的格式化服务在绑定与回显时自动调用对应的Printer/Parserpublic class OrderQuery { DateTimeFormat(pattern yyyy-MM-dd HH:mm:ss) private Date startTime; // getter / setter ... }当 Spring 将该对象的startTime渲染到视图时底层会经由DateTimeFormatAnnotationFormatterFactory.getPrinter拿到配置了pattern的DateFormatter最终以yyyy-MM-dd HH:mm:ss格式输出字符串当表单回传该字段时则通过getParser拿到同一DateFormatter将字符串解析回Date。Printer接口正是这一链路中输出方向的最终落点。五、总结Printer 接口的设计要点设计点说明仓库依据单一职责只负责对象 → 字符串的展示方向方法签名print(T, Locale)Spring-Printer.md函数式接口标注FunctionalInterface支持 Lambda 快速实现同上Locale 感知方法显式接收Locale便于按地区输出不同格式同上与 Parser 对称FormatterT同时继承PrinterT与ParserTDateFormatter为典型实现Spring-Formatter.md实现示例MillisecondInstantPrinter通过构造器注入DateTimeFormatter并借助JodaTimeContextHolder实现 Locale 感知输出Spring-MillisecondInstantPrinter.md注解驱动入口AnnotationFormatterFactory.getPrinter根据注解与字段类型返回 PrinterSpring-AnnotationFormatterFactory.md生产级装配DateTimeFormatAnnotationFormatterFactory为Date/Calendar/Long字段装配基于 style/iso/pattern 的DateFormatterSpring-DateTimeFormatAnnotationFormatterFactory.mdPrinter接口本身虽只有短短十余行代码但它在 Spring 格式化体系中扮演着输出侧契约的关键角色向上支撑Formatter组合接口向下通过AnnotationFormatterFactory接入DateTimeFormat等注解的自动化格式化流程。理解了它也就理解了 Spring 在对象展示为字符串这一方向上的全部设计脉络。【免费下载链接】source-code-hunter 从源码层面剖析挖掘互联网行业主流技术的底层实现原理为广大开发者 “提升技术深度” 提供便利。目前开放 Spring 全家桶Mybatis、Netty、Dubbo 框架及 Redis、Tomcat 中间件等项目地址: https://gitcode.com/doocs/source-code-hunter创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。