Close

Spring - Creating custom annotation for formatting

[Last Updated: Dec 22, 2023]

Following example shows how to create a custom annotation for formatting.

Example

This annotation is going to provide custom formatting rules for Locale field.

The annotation

package com.logicbig.example;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface LocaleFormat {
  LocaleStyle style() default LocaleStyle.CountryDisplayName;
}
package com.logicbig.example;

public enum LocaleStyle {
  CountryDisplayName,
  ISO3Country,
  ISO3Language;
}

Creating the formatter

Creating the formatter is similar to what we did in a previous tutorial. This formatter has no knowledge of the annotation we created in the last step.

package com.logicbig.example;

import org.springframework.format.Formatter;

import java.text.ParseException;
import java.util.Arrays;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.Optional;

public class LocaleFormatter implements Formatter<Locale> {
  private LocaleStyle localeStyle;

  public LocaleStyle getLocaleStyle () {
      return localeStyle;
  }

  public void setLocaleStyle (
          LocaleStyle localeStyle) {
      this.localeStyle = localeStyle;
  }

  @Override
  public Locale parse (String text, Locale locale) throws ParseException {
      Optional<Locale> o = Arrays.stream(Locale.getAvailableLocales()).parallel()
              .filter(l -> this.localeByStylePredicate(l, text))
              .findAny();
      if (o.isPresent()) {
          return o.get();
      }
      return null;
  }

  @Override
  public String print (Locale object, Locale locale) {
      switch (localeStyle) {
          case CountryDisplayName:
              return object.getDisplayCountry();
          case ISO3Country:
              return object.getISO3Country();
          case ISO3Language:
              return object.getISO3Language();
      }
      return object.toString();
  }

  private boolean localeByStylePredicate (Locale locale, String text) {
      try {
          switch (localeStyle) {
              case CountryDisplayName:
                  return locale.getDisplayCountry().equalsIgnoreCase(text);
              case ISO3Country:
                  return locale.getISO3Country().equalsIgnoreCase(text);
              case ISO3Language:
                  return locale.getISO3Language().equalsIgnoreCase(text);
          }
      } catch (MissingResourceException e) {
          //ignore;
      }
      return false;
  }
}

Binding annotation with formatter - implementing AnnotationFormatterFactory

Now we are going to bind the annotation and the formatter (created in the last two steps) together by implementing AnnotationFormatterFactory.

Spring on finding the annotation on a field, will look for annotation-formatter mapping, as provided by this factory, and then invoke the corresponding formatter to populate the value to the field.

package com.logicbig.example;

import org.springframework.format.AnnotationFormatterFactory;
import org.springframework.format.Formatter;
import org.springframework.format.Parser;
import org.springframework.format.Printer;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;

public class LocaleFormatAnnotationFormatterFactory implements
      AnnotationFormatterFactory<LocaleFormat> {

  @Override
  public Set<Class<?>> getFieldTypes() {
      return new HashSet<>(Arrays.asList(Locale.class));
  }

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

  @Override
  public Parser<?> getParser(LocaleFormat annotation,
                             Class<?> fieldType) {
      return getLocaleFormatter(annotation, fieldType);
  }

  private Formatter<?> getLocaleFormatter(LocaleFormat annotation, Class<?> fieldType) {
      LocaleFormatter lf = new LocaleFormatter();
      lf.setLocaleStyle(annotation.style());
      return lf;
  }
}

Creating the client bean

Now we are going to use our custom annotation, on an example bean.

package com.logicbig.example;

import java.util.Locale;

public class MyBean {
  @LocaleFormat(style = LocaleStyle.ISO3Country)
  private Locale myLocale;

  public Locale getMyLocale() {
      return myLocale;
  }

  public void setMyLocale(Locale myLocale) {
      this.myLocale = myLocale;
  }

  @Override
  public String toString() {
      return "MyBean{" +
              "myLocale=" + myLocale +
              '}';
  }
}

Registering the factory to DataService and Using DataBinder

package com.logicbig.example;

import org.springframework.beans.MutablePropertyValues;
import org.springframework.format.support.DefaultFormattingConversionService;
import org.springframework.validation.BindingResult;
import org.springframework.validation.DataBinder;

import java.text.DateFormat;
import java.util.Locale;

public class CustomFormatAnnotationExample {

  public static void main(String[] args) {

      DefaultFormattingConversionService service =
              new DefaultFormattingConversionService();
      service.addFormatterForFieldAnnotation(
              new LocaleFormatAnnotationFormatterFactory());

      MyBean bean = new MyBean();
      DataBinder dataBinder = new DataBinder(bean);
      dataBinder.setConversionService(service);

      MutablePropertyValues mpv = new MutablePropertyValues();
      mpv.add("myLocale", "CYP");

      dataBinder.bind(mpv);

      Object target = dataBinder.getTarget();
      System.out.println(target);

      BindingResult bindingResult = dataBinder.getBindingResult();
      System.out.println(bindingResult);
      System.out.println(bean.getMyLocale().getDisplayName());
  }
}

Output

MyBean{myLocale=el_CY}
org.springframework.validation.BeanPropertyBindingResult: 0 errors
Greek (Cyprus)

Example Project

Dependencies and Technologies Used:

  • spring-context 6.1.2 (Spring Context)
     Version Compatibility: 3.2.3.RELEASE - 6.1.2Version List
    ×

    Version compatibilities of spring-context with this example:

    • 3.2.3.RELEASE
    • 3.2.4.RELEASE
    • 3.2.5.RELEASE
    • 3.2.6.RELEASE
    • 3.2.7.RELEASE
    • 3.2.8.RELEASE
    • 3.2.9.RELEASE
    • 3.2.10.RELEASE
    • 3.2.11.RELEASE
    • 3.2.12.RELEASE
    • 3.2.13.RELEASE
    • 3.2.14.RELEASE
    • 3.2.15.RELEASE
    • 3.2.16.RELEASE
    • 3.2.17.RELEASE
    • 3.2.18.RELEASE
    • 4.0.0.RELEASE
    • 4.0.1.RELEASE
    • 4.0.2.RELEASE
    • 4.0.3.RELEASE
    • 4.0.4.RELEASE
    • 4.0.5.RELEASE
    • 4.0.6.RELEASE
    • 4.0.7.RELEASE
    • 4.0.8.RELEASE
    • 4.0.9.RELEASE
    • 4.1.0.RELEASE
    • 4.1.1.RELEASE
    • 4.1.2.RELEASE
    • 4.1.3.RELEASE
    • 4.1.4.RELEASE
    • 4.1.5.RELEASE
    • 4.1.6.RELEASE
    • 4.1.7.RELEASE
    • 4.1.8.RELEASE
    • 4.1.9.RELEASE
    • 4.2.0.RELEASE
    • 4.2.1.RELEASE
    • 4.2.2.RELEASE
    • 4.2.3.RELEASE
    • 4.2.4.RELEASE
    • 4.2.5.RELEASE
    • 4.2.6.RELEASE
    • 4.2.7.RELEASE
    • 4.2.8.RELEASE
    • 4.2.9.RELEASE
    • 4.3.0.RELEASE
    • 4.3.1.RELEASE
    • 4.3.2.RELEASE
    • 4.3.3.RELEASE
    • 4.3.4.RELEASE
    • 4.3.5.RELEASE
    • 4.3.6.RELEASE
    • 4.3.7.RELEASE
    • 4.3.8.RELEASE
    • 4.3.9.RELEASE
    • 4.3.10.RELEASE
    • 4.3.11.RELEASE
    • 4.3.12.RELEASE
    • 4.3.13.RELEASE
    • 4.3.14.RELEASE
    • 4.3.15.RELEASE
    • 4.3.16.RELEASE
    • 4.3.17.RELEASE
    • 4.3.18.RELEASE
    • 4.3.19.RELEASE
    • 4.3.20.RELEASE
    • 4.3.21.RELEASE
    • 4.3.22.RELEASE
    • 4.3.23.RELEASE
    • 4.3.24.RELEASE
    • 4.3.25.RELEASE
    • 4.3.26.RELEASE
    • 4.3.27.RELEASE
    • 4.3.28.RELEASE
    • 4.3.29.RELEASE
    • 4.3.30.RELEASE
    • 5.0.0.RELEASE
    • 5.0.1.RELEASE
    • 5.0.2.RELEASE
    • 5.0.3.RELEASE
    • 5.0.4.RELEASE
    • 5.0.5.RELEASE
    • 5.0.6.RELEASE
    • 5.0.7.RELEASE
    • 5.0.8.RELEASE
    • 5.0.9.RELEASE
    • 5.0.10.RELEASE
    • 5.0.11.RELEASE
    • 5.0.12.RELEASE
    • 5.0.13.RELEASE
    • 5.0.14.RELEASE
    • 5.0.15.RELEASE
    • 5.0.16.RELEASE
    • 5.0.17.RELEASE
    • 5.0.18.RELEASE
    • 5.0.19.RELEASE
    • 5.0.20.RELEASE
    • 5.1.0.RELEASE
    • 5.1.1.RELEASE
    • 5.1.2.RELEASE
    • 5.1.3.RELEASE
    • 5.1.4.RELEASE
    • 5.1.5.RELEASE
    • 5.1.6.RELEASE
    • 5.1.7.RELEASE
    • 5.1.8.RELEASE
    • 5.1.9.RELEASE
    • 5.1.10.RELEASE
    • 5.1.11.RELEASE
    • 5.1.12.RELEASE
    • 5.1.13.RELEASE
    • 5.1.14.RELEASE
    • 5.1.15.RELEASE
    • 5.1.16.RELEASE
    • 5.1.17.RELEASE
    • 5.1.18.RELEASE
    • 5.1.19.RELEASE
    • 5.1.20.RELEASE
    • 5.2.0.RELEASE
    • 5.2.1.RELEASE
    • 5.2.2.RELEASE
    • 5.2.3.RELEASE
    • 5.2.4.RELEASE
    • 5.2.5.RELEASE
    • 5.2.6.RELEASE
    • 5.2.7.RELEASE
    • 5.2.8.RELEASE
    • 5.2.9.RELEASE
    • 5.2.10.RELEASE
    • 5.2.11.RELEASE
    • 5.2.12.RELEASE
    • 5.2.13.RELEASE
    • 5.2.14.RELEASE
    • 5.2.15.RELEASE
    • 5.2.16.RELEASE
    • 5.2.17.RELEASE
    • 5.2.18.RELEASE
    • 5.2.19.RELEASE
    • 5.2.20.RELEASE
    • 5.2.21.RELEASE
    • 5.2.22.RELEASE
    • 5.2.23.RELEASE
    • 5.2.24.RELEASE
    • 5.2.25.RELEASE
    • 5.3.0
    • 5.3.1
    • 5.3.2
    • 5.3.3
    • 5.3.4
    • 5.3.5
    • 5.3.6
    • 5.3.7
    • 5.3.8
    • 5.3.9
    • 5.3.10
    • 5.3.11
    • 5.3.12
    • 5.3.13
    • 5.3.14
    • 5.3.15
    • 5.3.16
    • 5.3.17
    • 5.3.18
    • 5.3.19
    • 5.3.20
    • 5.3.21
    • 5.3.22
    • 5.3.23
    • 5.3.24
    • 5.3.25
    • 5.3.26
    • 5.3.27
    • 5.3.28
    • 5.3.29
    • 5.3.30
    • 5.3.31
    • Compatible Java Version: 17+
    • 6.0.0
    • 6.0.1
    • 6.0.2
    • 6.0.3
    • 6.0.4
    • 6.0.5
    • 6.0.6
    • 6.0.7
    • 6.0.8
    • 6.0.9
    • 6.0.10
    • 6.0.11
    • 6.0.12
    • 6.0.13
    • 6.0.14
    • 6.0.15
    • 6.1.0
    • 6.1.1
    • 6.1.2

    Versions in green have been tested.

  • JDK 17
  • Maven 3.8.1

Spring - Creating custom annotation for formatting Select All Download
  • spring-custom-format-annotation
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • MyBean.java

    See Also