Spring Boot's @Conditional Annotations

One of the outstanding features of Spring Boot is without doubt its auto-configuration capability. However, auto-configuration is implemented upon another great Spring feature: conditional annotations.

@Conditional Annotations

Spring 3.1 came with support for the environment abstraction. Along came the @Profile annotation. We talked about it in one of the last blog posts.

However, Spring 4.0 introduced a new mechanism on which since then @Profile is based: the @Conditional annotation and the Condition interface.

Conditional can be used as (meta-)annotation on components in order to define rules that determine wether the annotated component/bean/factory method is eligible for registration in the DI container. The rule part is done via implementing the Condition interface.

When we have a look at @Profile again, we can see that it is nothing more then an @Conditional annotation (when putting the usual retention annotation et. al. aside):

...
@Conditional(ProfileCondition.class)
public @interface Profile { .. }

The ProfileCondition implementation is rather easy, so we can show the entire source code:

class ProfileCondition implements Condition {

	@Override
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		if (context.getEnvironment() != null) {
			MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
			if (attrs != null) {
				for (Object value : attrs.get("value")) {
					if (context.getEnvironment().acceptsProfiles(((String[]) value))) {
						return true;
					}
				}
				return false;
			}
		}
		return true;
	}
}

As you can see, it basically has a look at the current environment and sees if the environment names specified in the annotation are accepted. If so, the bean will be registered.

The AnnotatedTypeMetadata argument holds meta-data about the annotated type. This includes information about all the annotations. Interesting part here is that Spring retrieves this information without necessarily requiring class-loading.

Conditional Annotations in Spring Boot

Spring Boot takes conditional annotations to a whole new level. It comes with various @Conditional* annotations being very useful for library/framework but also application developers. Let’s go through our favourite three annotations, keep in mind that the list is by far not complete. So whenever you are looking for some conditional exclusion or inclusion of beans, have a look in the org.springframework.boot.autoconfigure.condition package:

@ConditionalOnClass

ConditionalOnClass can be used to register a bean only when the given class(es) can be found on the class-path. You can define the fully qualified class name either via a String or via a Class<?> reference. Sounds a bit like a chicken and egg problem because you are defining and importing classes that might not be available at compile-time. But as Spring uses ASM to gather the annotation meta-data it is possible to refer to classes not being on the current class-path without running into linkage errors. Especially when providing library/framework classes to other developers, this annotation can be used to active certain beans only based on the existing of specific classes, e.g. driver-classes etc.

@ConditionalOnWebApplication

ConditionalOnWebApplication can be used to define beans that only exist if the application is executed in a JEE-based servlet-context/web application context. For example, when you are developing a Spring Boot based JSF application, you could think about defining a custom annotation @Web which can use @ConditionalOnWebApplication as meta-annotation, which works without problems by the way:

...
@ConditionalOnWebApplication
public @interface Web { .. }

@ConditionalOnProperty

ConditionalOnProperty can be used to register beans only if certain configuration properties are being found with the specified values. This is useful if you have the case where you want to activate certain beans based on a value in the application.properties file(s). In that case, it is even enough to simply specify the configuration property, if it is false or not given at all, it will not register the affected beans:

@Configuration
@ConditionalOnProperty("my.feature.active")
public class MyFeatureConfiguration { ... }

Summary

Spring Boot has a very cool set of conditional annotations. Conditional annotations are based upon Spring 4.0‘s Condition interface and its @Conditional annotation. If you are using Spring Boot, there is a very high chance one of the @Conditional* annotations are becoming interesting at some point or another.