Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Designing Your Own Spring Boot Starter – Part 1

DZone's Guide to

Designing Your Own Spring Boot Starter – Part 1

Calling all Spring Boot DIYers! Learn how to design a Spring Boot starter, with an intro to the spring.factories file, and @conditional annotations.

· Integration Zone
Free Resource

Learn how API management supports better integration in Achieving Enterprise Agility with Microservices and API Management, brought to you in partnership with 3scale

Since its release, Spring Boot has been a huge success: it boosts developers productivity with its convention over configuration philosophy. However, sometimes, it just feels too magical. I have always been an opponent of autowiring for this exact same reason. And when something doesn’t work, it’s hard to get back on track.

This is the reason why I wanted to dig deeper into Spring Boot starter mechanism – to understand every nook and cranny. This post is the first part and will focus on analyzing how it works. The second part will be a case study on creating a starter.

spring.factories

At the root of every Spring Boot starter lies the META-INF/spring.factories file. Let’s check the content of this file in the spring-boot-autoconfigure.jar. Here’s an excerpt of it:

...
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
...

Now let’s have a look at their content. For example, here’s the JpaRepositoriesAutoConfiguration class:

@Configuration
@ConditionalOnBean(DataSource.class)
@ConditionalOnClass(JpaRepository.class)
@ConditionalOnMissingBean({ JpaRepositoryFactoryBean.class,
  JpaRepositoryConfigExtension.class })
@ConditionalOnProperty(prefix = "spring.data.jpa.repositories", 
                       name = "enabled", havingValue = "true",
                       matchIfMissing = true)
@Import(JpaRepositoriesAutoConfigureRegistrar.class)
@AutoConfigureAfter(HibernateJpaAutoConfiguration.class)
public class JpaRepositoriesAutoConfiguration {}

There are a couple of interesting things to note:

  1. It’s a standard Spring @Configuration class
  2. The class contains no “real” code but imports another configuration – JpaRepositoriesAutoConfigureRegistrar, which contains the “real” code
  3. There are a couple of @ConditionalOnXXX annotations used
  4. There seems to be an order dependency management of some sort with @AutoConfigureAfter

Points 1 and 2 are self-explanatory, point 4 is rather straightforward so let’s focus on point 3.

@Conditional Annotations

If you didn’t start to work with Spring yesterday, you might know about the @Profile annotation. Profiles are a way to mark a bean-returning method as being optional. When a profile is activated, the relevant profile-annotated method is called and the returning bean contributed to the bean factory.

Some time ago, @Profile looked like that:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Profile {
    String[] value();
}

Interestingly enough, @Profile has been rewritten to use the new @Conditional annotation:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {
    String[] value();
}

Basically, a @Conditional annotation just points to a Condition. In turn, a condition is a functional interface with a single method that returns a boolean: if true, the @Conditional-annotated method is executed by Spring and its returning object added to the context as a bean.

There are a lot of conditions available out-of-the-box with Spring Boot:

Condition Description
OnBeanCondition Checks if a bean is in the Spring factory
OnClassCondition Checks if a class is on the classpath
OnExpressionCondition Evalutates a SPeL expression
OnJavaCondition Checks the version of Java
OnJndiCondition Checks if a JNDI branch exists
OnPropertyCondition Checks if a property exists
OnResourceCondition Checks if a resource exists
OnWebApplicationCondition Checks if a WebApplicationContext exists

Those can be combined together with boolean conditions:

Condition Description
AllNestedConditions AND operator
AnyNestedConditions OR operator
NoneNestedCondition NOT operator

Dedicated @Conditional annotations point to those annotations. For example, @ConditionalOnMissingBean points to theOnBeanCondition class.

Time to Experiment

Let’s create a configuration class annotated with @Configuration.

The following method will run in all cases:

@Bean
public String string() {
    return "string()";
}

This one won’t, for java.lang.String is part of Java’s API:

@Bean
@ConditionalOnMissingClass("java.lang.String")
public String missingClassString() {
    return "missingClassString()";
}

And this one will, for the same reason:

@Bean
@ConditionalOnClass(String.class)
public String classString() {
    return "classString()";
}

Analysis of the previous configuration

Armed with this new knowledge, let’s analyze the above JpaRepositoriesAutoConfiguration class.

This configuration will be enabled if – and only if all conditions are met:

@ConditionalOnBean(DataSource.class)
There’s a bean of type  DataSource in the Spring context
@ConditionalOnClass(JpaRepository.class)
The  JpaRepository class is on the classpath  i.e. the project has a dependency on Spring Data JPA
@ConditionalOnMissingBean
There are no beans of type  JpaRepositoryFactoryBean nor  JpaRepositoryConfigExtension in the context
@ConditionalOnProperty
The standard application.properties file must contain a property named  spring.data.jpa.repositories.enabled with a value of  true

Additionally, the configuration will run after HibernateJpaAutoConfiguration (if the latter is referenced).

Conclusion

I hope I demonstrated that Spring Boot starters are no magic. Join me next week for a simple case study.

To go further:

Unleash the power of your APIs with future-proof API management - Create your account and start your free trial today, brought to you in partnership with 3scale.

Topics:
spring ,java ,spring boot

Published at DZone with permission of Nicolas Frankel, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}