How to Build a Custom Annotation‑Based Data Formatter in Spring Boot
This tutorial demonstrates how to create a custom annotation and formatter in Spring Boot 3.2.5 to automatically convert request parameters like "666,China" into a User object, covering the required interfaces, implementation, registration, and testing with code examples and screenshots.
1. Introduction
Spring MVC provides powerful data type conversion, automatic binding, and formatting, but its default binding may not satisfy custom business needs such as parsing a specially formatted string into a custom object. Spring allows developers to create custom annotations and formatters to handle such conversions.
2. Practical Example
2.1 Goal
The aim is to bind a request parameter to a User object simply by adding a custom annotation on the method parameter, e.g. @UserFormat .
<code>@GetMapping("/user")</code>
<code>public User getUser(@UserFormat User user) {</code>
<code> return user;</code>
<code>}</code>2.2 Custom Annotation Formatter Factory
To enable annotation‑driven formatting, implement the AnnotationFormatterFactory interface.
<code>// The generic type is the annotation we will use to mark the parameter
public interface AnnotationFormatterFactory<A extends Annotation> {
// Which field types can be annotated
Set<Class<?>> getFieldTypes();
// Convert object to String
Printer<?> getPrinter(A annotation, Class<?> fieldType);
// Parse String to object
Parser<?> getParser(A annotation, Class<?> fieldType);
}</code>2.3 Formatter Implementation
<code>public class StringToUserFormatter implements AnnotationFormatterFactory<UserFormat> {
@Override
public Set<Class<?>> getFieldTypes() {
return Set.of(User.class);
}
@Override
public Printer<User> getPrinter(UserFormat annotation, Class<?> fieldType) {
return (object, locale) -> object.toString();
}
@Override
public Parser<User> getParser(UserFormat annotation, Class<?> fieldType) {
return (text, locale) -> {
Assert.hasText(text, "Data error");
String[] s = text.split(",");
User user = new User();
user.setId(Long.parseLong(s[0]));
user.setName(s[1]);
return user;
};
}
}</code>2.4 Custom Annotation
<code>@Retention(RetentionPolicy.RUNTIME)</code>
<code>@Target(ElementType.PARAMETER)</code>
<code>public @interface UserFormat {}</code>2.5 Register Formatter
<code>@Component
public class WebDataTypeConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addFormatterForFieldAnnotation(new StringToUserFormatter());
}
}</code>2.6 Testing the Parameter Annotation
<code>@GetMapping("/user")
public User getUser(@UserFormat User user) {
return user;
}</code>When calling http://localhost:9001/api/objects/user?user=666,China , the request is correctly converted to a User instance.
2.7 Using the Annotation on a Field
<code>public static class DTO {
@UserFormat
private User user;
private Integer age;
}</code> <code>// Note: keep it as GET, not POST with @RequestBody
@GetMapping("/dto")
public DTO save(DTO dto) {
return dto;
}</code>Calling the /dto endpoint also yields the correctly populated DTO object.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.