There are some basics that usually allow you to handle a bit more complicated abstractions on top of them. I think, in the case of Spring - the type conversions is one of such topics. I tried to structure a bit what Spring has and how we can use it in our applications. The reading may be more useful to the beginning developers with Spring, but I hope the experienced developers might find something interesting in it.

Spring allows you to do a lot of elegant things with your code and types conversion plays a crucial role in this “magic”. I’ll try to take a trivial example of Spring MVC application communicating with the world by means of REST Endpoint, this application will handle types conversion by different means.

Mind map with the main terms, which could be useful:

The application

The application, as mentioned before is just a spring boot project with MVC support. I have simply created it via spring initialzr. I couldn’t resist using my favorite flavor groovy+gradle, this shouldn’t prevent us from investigating the conversions ideas in Spring. I hope. You can easily clone the whole project from GitHub repo.

Assuming that you have created the blank Spring MVC project. Let’s add there some POJO classes, representing the model of our project:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@ToString
class Author {
    String name
}

enum BookStatus {
    ISSUED, FORBIDDEN, REISSUED
}

@ToString
class Book {
    int id
    Date issueDate
    Author author
    BookStatus status
}

Well done, and quick as a flash controller:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@Slf4j
@RestController
class GeneralController {

    @PostMapping(value = "/book")
    @ResponseStatus(HttpStatus.CREATED)
    def createBook(@ModelAttribute Book book) {
        log.info "Created book: $book"
    }

}

groovy-chef

Editors

If we try to call the POST localhost:8080/book we’ll get the error:

1
2
3
4
curl --location --request POST 'localhost:8080/book' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'id=123' \
--data-urlencode 'author=Alex'

result:

1
{"timestamp":"2020-06-14T22:21:24.922+00:00","status":400,"error":"Bad Request","message":"","path":"/book"}

and the same in the app’s log:

1
2
Resolved [org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'book' on field 'author': rejected value [Alex];

Well, this is an expected result, we were trying to set a string 'Alex' to the field of type Author. Spring somehow should know how to convert the String to Author.

Let’s use the concept of PropertyEditor. PropertyEditor is an interface from java bean package, implementing this interface you can configure how the object should be created from the string. PropertyEditors are widely used inside of the Spring core, you can see them in action in spring XML configs.

There are a few implemented editors come with Spring. Let’s create one for our Author class:

1
2
3
4
5
6
7
8
@Slf4j
class AuthorEditor extends PropertyEditorSupport {
    @Override
    void setAsText(String text) throws IllegalArgumentException {
        log.info "Creating author from $text"
        setValue(new Author(name: text))
    }
}

The interface PropertyEditor has a lot of methods, except the String conversion, it has a lot of other bean behavior configuration. You can use PropertyEditorSupport with default implementation of the interface and override only needed methods - in our case, it’s setAsText.

Now, we need to tell Spring somehow to pick up the editor and use it for conversion. The simplest way to do it is to put the editor in the same package with the target class, spring will find it there by himself. By the way the name of editor should follow the convention - name of the target class + Editor. I personally, prefer this way.

But putting all together is not the only option, so let’s make our example a bit more complicated and put editor apart from the target class. To register this standing alone editor you can either use CustomEditorConfigurer or postprocessor or register the editor just right in the controller by means of @IntiBinder annotation.

I’ll try to use the second approach, but before doing it, let’s add one additional abstraction here. Imagine, that we have dozens of different Editors, which most of the time being used together and probably instead of registering them one by one every time, you would like to have one logical unit, which is register as a group of editors. Hopefully, the Spring has this functionality and it’s called PropertyEditorRegistrar. Implementing this interface you need to define one method: registerCustomEditors - just what we needed. Let’s take a look at how it works together:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@Slf4j
@Configuration
class EditorsConfig {

    @Bean
    CustomPropertyEditorRegistrar configurer() {
        new CustomPropertyEditorRegistrar()
    }

}

@CompileStatic
class CustomPropertyEditorRegistrar implements PropertyEditorRegistrar {
    @Override
    void registerCustomEditors(PropertyEditorRegistry registry) {
        registry.registerCustomEditor(Author, new AuthorEditor())
    }
}

and the controller will look like:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Slf4j
@RestController
class GeneralController {

    private CustomPropertyEditorRegistrar customPropertyEditorRegistrar

    GeneralController(CustomPropertyEditorRegistrar customPropertyEditorRegistrar) {
        this.customPropertyEditorRegistrar = customPropertyEditorRegistrar
    }

    @InitBinder
    void initBinder(WebDataBinder binder) {
        customPropertyEditorRegistrar.registerCustomEditors(binder)
    }

    @PostMapping(value = "/book")
    @ResponseStatus(HttpStatus.CREATED)
    def createBook(@ModelAttribute Book book) {
        log.info "Created book: $book"
    }

}

This time calling the same POST localhost:8080/book endpoint returns status 200 and in the log:

1
com.example.demo.editors.AuthorEditor    : Creating author from Alex

Notice, that in the controller we are using registrar, by this approach you could group editors in logical units and register them all together.

Converters

Editors are good if you need to convert from String in rather a low level of beans classes. But there is a bit higher abstraction in Spring, allowing to convert between any type in general, this is a Converter<,> and ConversionService.

It turned out that the basic topic of type conversion in Spring requires more volume to explain as I thought in the beginning. I decided to split this reading in two (I hope) parts, to keep every article simple and easy to read.

The link to the next part I will add later. (Update: the link to the part 2.)

to-be-continue