Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to use @ DateTimeFormat and @ NumberFormat

2025-04-24 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article introduces the knowledge of "how to use @ DateTimeFormat and @ NumberFormat". Many people will encounter this dilemma in the operation of actual cases, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!

The outline of this article

Version convention

Spring Framework:5.3.x

Spring Boot:2.4.x

Text

As far as I know, @ DateTimeFormat is a high-profile annotation in development, and its purpose is easy to understand: formatting dates and times. But it is often confusing to use. For example: use it or com.fasterxml.jackson.annotation.JsonFormat annotations? Can it be used on Long types? Can it be used on JSR 310 date-time types?

It's normal to have these question marks, but don't swallow them, and it's not recommended to memorize the answers to these questions, but to understand them at the principle level through rules, which is not only more secure but also easier, which is perhaps one of the most important necessary skills for learning programming.

@ DateTimeFormat and @ NumberFormat

In terms of type conversion / formatting, Spring provides two:

@ DateTimeFormat: format Field/ method parameters to date / time type

@ NumberFormat: formats Field/ method parameters to numeric types

It is worth noting that there are many date / time types mentioned here, such as the oldest java.util.Date type, the LocalDate type of JSR 310and even the timestamp Long type can be called date-time type; similarly, numeric type is also a general concept, such as Number type, percentage type, coin type also fall into this category.

❝voiceover: these two annotations can be used in a wide range of ❞, look at the definition of these two annotations, it is not simple:

@ Target ({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})

Public @ interface DateTimeFormat {

String style () default "SS"

ISO iso () default ISO.NONE

String pattern () default ""

}

@ Target ({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE}) public @ interface NumberFormat {Style style () default Style.DEFAULT; String pattern () default "";}

The two brothers have two things in common:

Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community

All support tagging on method Method, field Field, and method parameter Parameter

Both support the flexible pattern property, which supports Spring placeholder writing

You can write the pattern attribute either literally or in a more flexible form such as a placeholder like ${xxx.xxx.pattern}

When we use these two annotations, the most common one is that pattern does not have one of the attributes, because it is very flexible and powerful to meet a wide variety of formatting needs. From this point, we can also see that our formatting in terms of date / time and numbers does not follow international standards (such as ISO), but the commonly used "Chinese standard".

Since almost all of the students have used these two annotations on Spring MVC, this article will first principle and then example. Don't use it until you know the principle behind it.

AnnotationFormatterFactory

When it comes to formatting annotations, you have to mention the factory class, which is at the core of the implementation principle.

Literal meaning: annotation format chemical factory. Explain in vernacular: this factory is used to create a corresponding formatter for annotations marked on the Field field and then format the values. Several key factors can be extracted from this sentence:

Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community

Notes

Field Field

Here, Field does not only mean java.lang.reflect.Field. For example, method return type and parameter type all fall into this category. The following example will give you some experience.

Formatter Formatter

Interface definition:

Public interface AnnotationFormatterFactory {Set getPrinter (An annotation, Class fieldType); Parser getParser (An annotation, Class fieldType);}

The interface defines three methods:

GetFieldTypes: supported type. When annotated on these types, the factory can handle it, otherwise, regardless of

GetPrinter: generate a Printer for fieldType types annotated with annotation

GetParser: generate a Parser for fieldType types annotated with annotation

The following implementations are built into this API Spring:

Although there are as many as five implementations, there are actually only two categories, that is, for users, you only need to make two distinctions, corresponding to the two annotations mentioned above. Here, Brother A draws it as shown in the picture:

The red box (and the type of Field it deals with) is the focus we need to pay attention to, and the rest can make an impression.

With regard to the date-time type, I no longer recommend using the java.util.Date type (let alone the Long type) in several articles, but use the JSR 310 date-time type provided by Java 8 instead (including replacing joda-time). However, at this stage, the java.util.Date type can not be ignored (the huge stock market, the existence of large "stock" programmers), so I decided to put DateTimeFormatAnnotationFormatterFactory on the table to describe it, but to be more comprehensive.

❝on the date and time of JDK I wrote a very complete series, details click here direct: date and time series, it is recommended to learn about ❞first

DateTimeFormatAnnotationFormatterFactory

The corresponding formatter API is: org.springframework.format.datetime.DateFormatter.

Version 3.2 of @ since already exists and is dedicated to support for the java.util.Date system + @ DateTimeFormat: create the corresponding Printer/Parser. The following interpretation of its source code:

①: this factory class is dedicated to @ DateTimeFormat annotation service ②: replace placeholders (if any) with Spring's StringValueResolver

This part of the source code tells us that the @ DateTimeFormat annotation is valid only if it is annotated on these types as shown in the figure before it can be processed by the factory to complete the creation work.

❝Note: in addition to Date and Calendar types, there are also Long types. Please don't ignore ❞.

The core processing logic is also easy to understand: both Printer and Parser are ultimately delegated to DateFormatter, and this API has been explained in detail in previous articles in this series. Direct elevator access

It is worth noting that DateFormatter can only handle Date types. In other words, fieldType, the second parameter of the getFormatter () method, is not used in this method, which means that the @ DateTimeFormat annotation does not properly handle its Case marked in the Calendar and Long types by default. To get support, you need to rewrite its methods such as getPrinter/getParser.

Use the example

Since @ DateTimeFormat can be marked on member properties, method parameters, methods (return values), and only when it is marked on Date, Calendar, Long and other types can it be handed over to this factory class to process and generate the corresponding processing class, this article uses three cases case to cover.

Case1: member attribute + Date type. Input + output

Prepare a Field property marked with @ DateTimeFormat annotation, which is of type Date

@ Data @ AllArgsConstructor class Person {@ DateTimeFormat (pattern = "yyyy-MM-dd HH:mm:ss") private Date birthday;}

Write the test program:

@ Test public void test1 () throws Exception {AnnotationFormatterFactory annotationFormatterFactory = new DateTimeFormatAnnotationFormatterFactory (); / / find the field Field field = Person.class.getDeclaredField ("birthday"); DateTimeFormat annotation = field.getAnnotation (DateTimeFormat.class); Class type = field.getType (); / / output: System.out.println ("output: Date-> String="); Printer printer = annotationFormatterFactory.getPrinter (annotation, type); Person person = new Person (new Date ()) System.out.println (printer.print (person.getBirthday (), Locale.US)); / / input: System.out.println ("input: String-> Date="); Parser parser = annotationFormatterFactory.getParser (annotation, type); Object output = parser.parse ("2021-02-06 19:00:00", Locale.US); person = new Person ((Date) output); System.out.println (person);}

Run the program, output:

Output: Date-> String= 2021-02-06 22:21:56 input: String-> Date= Person (birthday=Sat Feb 06 19:00:00 CST 2021)

Perfect.

Case2: method parameter + Calendar. Input

@ Test public void test2 () throws NoSuchMethodException, ParseException {AnnotationFormatterFactory annotationFormatterFactory = new DateTimeFormatAnnotationFormatterFactory (); / / get the method input parameter Method method = this.getClass () .getDeclaredMethod ("method", Calendar.class); Parameter parameter = method.getParameters () [0]; DateTimeFormat annotation = parameter.getAnnotation (DateTimeFormat.class); Class type = parameter.getType (); / / input: System.out.println ("input: String-> Calendar=") Parser parser = annotationFormatterFactory.getParser (annotation, type); Object output = parser.parse ("2021-02-06 19:00:00", Locale.US); / / pass the "converted" parameter to the method, indicating input method ((Calendar) output);} public void method (@ DateTimeFormat (pattern = "yyyy-MM-dd HH:mm:ss") Calendar calendar) {System.out.println (calendar); System.out.println (calendar.getTime ());}

Run the program and report an error:

Enter: String-> Calendar= java.lang.ClassCastException: java.util.Date cannot be cast to java.util.Calendar.

Through the exposition in the article, this mistake is expected. The following is done by customizing an enhanced implementation:

Class MyDateTimeFormatAnnotationFormatterFactory extends DateTimeFormatAnnotationFormatterFactory {@ Override public Parser getParser (DateTimeFormat annotation, Class fieldType) {if (fieldType.isAssignableFrom (Calendar.class)) {return new Parser () {@ Override public Calendar parse (String text, Locale locale) throws ParseException {/ / first translated as Date Formatter formatter = getFormatter (annotation, fieldType) Date date = formatter.parse (text, locale); / / then translated into Calendar Calendar calendar = Calendar.getInstance (TimeZone.getDefault ()); calendar.setTime (date); return calendar;}} } return super.getParser (annotation, fieldType);}}

Replace the factory class in the test program with a custom enhanced implementation:

AnnotationFormatterFactory annotationFormatterFactory = new MyDateTimeFormatAnnotationFormatterFactory ()

Run the program again and output:

Enter: String-> Calendar= java.util.GregorianCalendar [time=1612609200000,... Sat Feb 06 19:00:00 CST 2021

Perfect.

Case3: the method returns a value of + Long. The output suggestion should be realized by itself.

❝timestamps are often used for time transfer, so how is the Long type in the transfer automatically encapsulated as a Date type (input)? Use your hands to consolidate ~ ❞

Jsr310DateTimeFormatAnnotationFormatterFactory

The corresponding formatter API is: Spring's org.springframework.format.datetime.standard.DateTimeFormatterFactory and JDK's java.time.format.DateTimeFormatter.

@ since 4.0. The emergence of JSR 310 is accompanied by the emergence of Java 8. Spring has supported Java 8 since 4. 0 and is at least based on Java 8 since 5. 0, so this kind of since 4 is not curious.

It can be read from the class name that it is used to process JSR 310 date time. Let's interpret some of its source code and look at its essence through the phenomenon:

①: this factory is dedicated to @ DateTimeFormat annotation service ②: replace placeholders (if any) with Spring's StringValueResolver

When the @ DateTimeFormat annotation is annotated on these types, it is handed over to this factory class to take care of the creation of its formatter.

①: get a java.time.format.DateTimeFormatter of JDK, which is responsible for formatting the date / time-> String type. Since the formatting of the date / time of JSR 310 is well implemented by JDK itself, Spring only needs to integrate it. However, DateTimeFormatter is thread-safe and cannot set iso, pattern and other personalized parameters at the same time, so Spring created the DateTimeFormatterFactory factory class, which is used to smooth out the differences in use and achieve a consistent user experience. Of course, this knowledge point belongs to the content of the previous article. For more details, please click here for elevator access.

Back here, it is key to get an instance of the formatter with the getFormatter () method, as shown in the following code:

Use Spring's factory class DateTimeFormatterFactory to build a date-time formatter DateTimeFormatter for JSR 310 to process. With the groundwork of the previous article, I believe there is no need to explain this logic.

②: this chunk is a special treatment for the LocalXXX (including LocalDate/Time) standard formatter: adapting the ISO_XXX formatting template to a more suitable ISO_Local_XXX formatting template is more accurate. ③: TemporalAccessorPrinter it is a Printer, and the actual formatter is still DateTimeFormatter, but its function is compatible with the context-level formatter (and the current thread binding), so it has the ability to use context-level formatting parameters and is more customizable, as shown in the following figure (source code comes from TemporalAccessorPrinter):

Emphasize: despite the fact that this feature is very small, but very useful, it has the effect of dialing a thousand pounds. Because it is closely related to our business system, mastering this point can easily achieve twice the result with half the effort, others work overtime and you get a raise. With regard to the application of this knowledge, Brother A thinks it is worth writing a special article to describe it. Please look forward to the following.

Let's take a look at the implementation of the getParser () section:

①: TemporalAccessorParser is a Parser, which also uses DateTimeFormatter with Context context features to do String-> TemporalAccessor work. Students who are familiar with the conversion logic in this direction will know that because they are all static method calls, they must be dealt with one by one in the way of "enumeration". The screenshot is as follows (the source code is from TemporalAccessorParser):

At this point, the source code of the entire Jsr310DateTimeFormatAnnotationFormatterFactory is analyzed. Let's sum it up:

Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community

This factory is designed for @ DateTimeFormat annotation services marked in JSR 310 date / time type

The underlying formatter uses context-sensitive DateTimeFormatter in both directions and is highly customizable. Although this feature is small, it has an effect of four or two thousand jin, which will be given in a special article later.

The style and pattern attributes of @ DateTimeFormat annotations are written in placeholder format, which is more flexible.

Use the example

Unlike DateTimeFormatAnnotationFormatterFactory, which only provides partial support, it provides full functionality. Feel it.

Case1: member attribute + LocalDate type. Input + output

@ Data @ AllArgsConstructor class Father {@ DateTimeFormat (iso = DateTimeFormat.ISO.DATE) private LocalDate birthday;}

Test the code:

@ Test public void test4 () throws NoSuchFieldException, ParseException {AnnotationFormatterFactory annotationFormatterFactory = new Jsr310DateTimeFormatAnnotationFormatterFactory (); / / find the field Field field = Father.class.getDeclaredField ("birthday"); DateTimeFormat annotation = field.getAnnotation (DateTimeFormat.class); Class type = field.getType (); / / output: System.out.println ("output: LocalDate-> String="); Printer printer = annotationFormatterFactory.getPrinter (annotation, type); Father father = new Father (LocalDate.now ()) System.out.println (printer.print (father.getBirthday (), Locale.US)); / / input: System.out.println ("input: String-> Date="); Parser parser = annotationFormatterFactory.getParser (annotation, type); Object output = parser.parse ("2021-02-07", Locale.US); father = new Father ((LocalDate) output); System.out.println (father);}

Run the program, output:

Output: LocalDate-> String= 2021-02-07 input: String-> Date= Father (birthday=2021-02-07)

Perfect.

Case2: method parameter + LocalDate type. Input

@ Test public void test5 () throws ParseException, NoSuchMethodException {AnnotationFormatterFactory annotationFormatterFactory = new Jsr310DateTimeFormatAnnotationFormatterFactory (); / / get the method input parameter Method method = this.getClass () .getDeclaredMethod ("methodJSR310", LocalDate.class); Parameter parameter = method.getParameters () [0]; DateTimeFormat annotation = parameter.getAnnotation (DateTimeFormat.class); Class type = parameter.getType (); / / input: System.out.println ("input: String-> LocalDate=") Parser parser = annotationFormatterFactory.getParser (annotation, type); Object output = parser.parse ("2021-02-06", Locale.US); / / pass the "converted" parameter to the method, indicating input methodJSR310 ((LocalDate) output);} public void methodJSR310 (@ DateTimeFormat (iso = DateTimeFormat.ISO.DATE) LocalDate localDate) {System.out.println (localDate);}

Run the program, output:

Enter: String-> LocalDate= 2021-02-06

Case3: the method returns a value of type + LocalDate. Input

@ Test public void test6 () throws NoSuchMethodException {AnnotationFormatterFactory annotationFormatterFactory = new Jsr310DateTimeFormatAnnotationFormatterFactory (); / / get the method return value type Method method = this.getClass () .getDeclaredMethod ("method1JSR310"); DateTimeFormat annotation = method.getAnnotation (DateTimeFormat.class); Class type = method.getReturnType (); / / output: System.out.println ("output: LocalDate-> String= in time format"); Printer printer = annotationFormatterFactory.getPrinter (annotation, type) LocalDate returnValue = method1JSR310 (); System.out.println (printer.print (returnValue, Locale.US));} @ DateTimeFormat (iso = DateTimeFormat.ISO.DATE) public LocalDate method1JSR310 () {return LocalDate.now ();}

Perfect.

NumberFormatAnnotationFormatterFactory

The corresponding formatter API is: three subclasses of org.springframework.format.number.AbstractNumberFormatter

@ since 3.0. Go straight to the point and take a look at the source code:

With the above "experience", there is no need to explain this part.

①: @ NumberFormat can be annotated on a subtype of Number and generate the corresponding formatter processing.

Underlying implementation: the actual formatting action Printer/Parser is shown in the following figure. If you delegate it to the formatter that has been introduced earlier, it will not be introduced too much. Those who have knowledge blind areas can take the elevator to check the previous articles in this series.

Use the example

@ NumberFormat supports tagging on many types, such as decimals, percentages, coins, and so on. Since the text has already laid the groundwork, we can only give a simple use case here.

@ Test public void test2 () throws NoSuchMethodException, ParseException {AnnotationFormatterFactory annotationFormatterFactory = new NumberFormatAnnotationFormatterFactory (); / / get the target type to be processed (method parameters, field properties, method return values, etc.) Method method1 = this.getClass () .getMethod ("method2", double.class); Parameter parameter = method1.getParameters () [0]; NumberFormat annotation = parameter.getAnnotation (NumberFormat.class); Class fieldType = parameter.getType () / 1. Generate a parser based on annotations and field types, complete String-> LocalDateTime Parser parser = annotationFormatterFactory.getParser (annotation, fieldType); / / 2, simulate the conversion action, and output the result Object result = parser.parse ("11%", Locale.US); System.out.println (result.getClass ()); System.out.println (result);} public void method2 (@ NumberFormat (style = NumberFormat.Style.PERCENT) double d) {}

Run the program, output:

Class java.math.BigDecimal 0.11

The percentage of 11% is perfectly converted to BigDecimal. As for why it's BigDecimal instead of double, it's all in PercentStyleFormatter.

That's all for "how to use @ DateTimeFormat and @ NumberFormat". Thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report