/ SPRING, AUTOWIRING, COMPONENT SCAN, GOOD PRACTICE

A use-case for Spring component scan

Regular readers of this blog know I’m a big proponent of the Spring framework, but I’m quite opinionated in the way it should be used. For example, I favor explicit object instantiation and explicit component wiring over self-annotated classes, component scanning and autowiring.

Concepts

Though those concepts are used by many Spring developers, my experience has taught me they are not always fully understood. Some explanation is in order.

Self-annotated classes

Self-annotated classes are classes which define how they will be instantiated by Spring via annotations on the classes themselves. @Component, @Controller, @Service and @Repository are the usual self-annotated classes found in most Spring projects.

@Component
class MySelfAnnotatedClass {}

The main disadvantages of self-annotated classes is the hard coupling between the class and the bean. For example, it’s not possible to instantiate 2 singleton beans of the same class, as in explicit creation. Worse, it also couples the class to the Spring framework itself.

Note that @Configuration classes are also considered to be self-annotated.

Component scanning

Self-annotated classes need to be listed and registered in the context. This can be done explicitly:

@Configuration
class MyConfig {

    @Bean
    fun myClass() =  MySelfAnnotatedClass()
}

However, the most widespread option is to let the Spring framework search for every self-annotated class on the project classpath and register them according to the annotations.

@Configuration @ComponentScan
class MyConfig

Autowiring

Some beans require dependencies in order to be initialized. Wiring the dependency into the dependent bean can be either:

  • explicit: it’s the developer’s responsibility to tell which beans will fulfill the dependency.
  • implicit (or auto): the Spring framework is responsible to provide the dependency. In order to do that, a single bean must be eligible.

Regarding the second option, please re-read an old post of mine for its related problems.

Sometimes, however, there’s no avoiding autowiring. When bean Bean1 defined in configuration fragment Config1 depends on bean Bean2 defined in fragment Config2, the only possible injection option is autowiring.

@Configuration
class Config2 {

    @Bean
    fun bean2() = Bean2()
}

@Configuration
class Config1 {

    @Bean @Autowired
    fun bean1(bean2: Bean2) = Bean1(bean2)
}

In the above snippet, autowiring is used without self-annotated classes.

Reinventing the wheel

This week, I found myself re-implementing Spring Boot’s actuator in a legacy non-Spring Boot application.

The architecture is dead simple: the HTTP endpoint returns a Java object (or a list of them) serialized through the Jackson library. Every endpoint might return a different object, and each can be serialized using a custom serializer.

I’ve organized the project in a package-per-endpoint way (as opposed to package-per-layer), and have already provided several endpoints. I’d like people to contribute other endpoints, and I want it to be as simple as possible. In particular, they should only:

  1. Declare controllers
  2. Declare configuration classes
  3. Instantiate Jackson serializers

The rest should taken care of by generic code written by me.

The right usage of autowire

Controllers and configuration classes are easily taken care of by using @ComponentScan on the main configuration class located in the project’s main package. But what about serializers?

Spring is able to collect all beans of a specific class registed into a context into a list. That means that every package will declare its serializers independently, and common code can take care of the registration:

@Configuration @EnableWebMvc @ComponentScan
class WebConfiguration : WebMvcConfigurerAdapter() {

    @Autowired
    private lateinit var serializers: List<StdSerializer<*>> (1)

    override fun configureMessageConverters(converters: MutableList<HttpMessageConverter<*>>) {
        converters.add(MappingJackson2HttpMessageConverter().apply {
            objectMapper.registerModule(SimpleModule().apply {
                serializers.forEach { addSerializer(it) }
            })
        })
    }
}
1 Magic happens here. This configuration class has already been written, and new packages don’t need to do anything, serializers will be part of the list.

Here’s an example of such a configuration class, declaring a serializer bean:

@Configuration
class FooConfiguration {

    @Bean
    fun fooSerializer()  = FooSerializer()
}

class FooSerializer : StdSerializer<Foo>(Foo::class.java) {
    ...
}

Even better, if packages need to be modularized further into full-fledged JARs, this setup will work in the exact same way without any change.

Conclusion

A better understanding of self-annotated classes, component-scanning and autowiring is beneficial to all Spring developers.

Moreover, while they have a lot of drawbacks in "standard" beans classe, it’s not only perfectly acceptable but even an advantage to do so within the scope of configuration classes. In projects designed in a package-by-feature way, it improves modularization and decoupling.

Nicolas Fränkel

Nicolas Fränkel

Developer Advocate with 15+ years experience consulting for many different customers, in a wide range of contexts (such as telecoms, banking, insurances, large retail and public sector). Usually working on Java/Java EE and Spring technologies, but with focused interests like Rich Internet Applications, Testing, CI/CD and DevOps. Also double as a trainer and triples as a book author.

Read More
A use-case for Spring component scan
Share this