Spring @ConditionalOnClass and @ConditionalOnMissingClass

Introduction

Spring @ConditionalOnClass and @ConditionalOnMissingClass annotations let @Configuration classes be included based on the presence or absence of specific classes. So @ConditionalOnClass loads a bean only if a certain class is on the classpath and @ConditionalOnMissingClass loads a bean only if a certain class is not on the classpath.

This mechanism does not apply the same way to @Bean methods where typically the return type is the target of the condition: before the condition on the method applies, the JVM will have loaded the class and potentially processed method references which will fail if the class is not present.

Prerequisites

Gradle 5.6 – 6.7.1, Java at least 8, Spring Boot 2.1.7 – 2.4.3

Project Setup

Create a gradle or maven based project in your favorite IDE or tool with the project name as spring-conditional-on-class.

The default generated build.gradle script does not include required dependencies, so I will update the build script as follows to include the spring boot dependencies.

buildscript {
	ext {
		springBootVersion = '2.1.7.RELEASE' to 2.4.3
	}
	
    repositories {
    	maven {
    		url 'https://plugins.gradle.org/m2/'
    	}
    }
    
    dependencies {
    	classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

plugins {
    id 'java-library'
    id 'org.springframework.boot' version "${springBootVersion}"
}

sourceCompatibility = 12
targetCompatibility = 12

repositories {
    mavenCentral()
    jcenter()
}

dependencies {
	implementation("org.springframework.boot:spring-boot-starter-web:${springBootVersion}")
}

For maven based project you can use the following pom.xml file:

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.roytuts</groupId>
	<artifactId>spring-conditional-on-class</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<maven.compiler.source>12</maven.compiler.source>
		<maven.compiler.target>12</maven.compiler.target>
	</properties>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.7.RELEASE to 2.4.3</version>
	</parent>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

Examples on @ConditionalOnClass

I will create various ways of using @ConditionalOnClass annotation.

Service Class

I will create simple service class without any methods in it. Ideally this service class will have some methods to perform your project’s business operations.

Notice I have put clazz in the package name because class is not allowed.

package com.roytuts.spring.conditional.on.clazz;
public class SpringService {
}

Main Class

I will create a main class to run our Spring Boot application. I will not modify our main class at all, that’s why I am writing the source code of main class here.

package com.roytuts.spring.conditional.on.clazz;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringConditionalOnClassApp implements CommandLineRunner {
	@Autowired
	private SpringService springService;
	public static void main(String[] args) {
		SpringApplication.run(SpringConditionalOnClassApp.class, args);
	}
	@Override
	public void run(String... args) throws Exception {
		System.out.println("Spring service fully qualified class name: " + springService.getClass());
	}
}

Simple Class

I will create a simple class which will be required to call on @ConditionalOnClass annotation because this class will be executed only when the required class is found.

Class – RequiredClass

Create a simple class as shown below:

package com.roytuts.spring.conditional.on.clazz;
public class RequiredClass {
}

Examples

I will create a Spring Config class and use annotation @ConditionalOnClass in various ways, such as, by namevalue and will see how it works.

I will modify our Spring Config class for each type of example.

@ConditionalOnClass – by name

I will now use the @ConditionalOnClass by passing the class name for the value name.

package com.roytuts.spring.conditional.on.clazz;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnClass(name = "com.roytuts.spring.conditional.on.clazz.RequiredClass")
public class SpringConfig {
	@Bean
	public SpringService springService() {
		return new SpringService();
	}
}

By executing the main class, you will see below output:

Spring service fully qualified class name: class com.roytuts.spring.conditional.on.clazz.SpringService

If your class RequiredClass is not found then you will see following error:

The following candidates were found but could not be injected:
	- Bean method 'springService' in 'SpringConfig' not loaded because @ConditionalOnClass did not find required class <Class Name>

In the above example, I am passing single bean name but you can also pass multiple bean names in the following way:

@ConditionalOnClass(name = { "com.roytuts.spring.conditional.on.clazz.RequiredClass",
			"com.roytuts.spring.conditional.on.clazz.AnotherRequiredClass" })

@ConditionalOnClass – by value

Here I will show @ConditionalOnClass by value in Spring Config class.

I will pass the class for the value value.

package com.roytuts.spring.conditional.on.clazz;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnClass(value = RequiredClass.class)
public class SpringConfig {
	@Bean
	public SpringService springService() {
		return new SpringService();
	}
}

By executing the main class, you will see the same output as you had seen using class name.

If you want to pass multiple classes for the parameter value then you can do in the following way:

@ConditionalOnClass(value = {RequiredClass.class, AnotherRequiredClass.class})

Examples on @ConditionalOnMissingClass

I will create various ways of using @ConditionalOnMissingClass annotation.

This annotation supports by value only.

Service Class

Create a simple service class:

package com.roytuts.spring.conditional.on.missing.clazz;
public class SpringService {
}

Config Class

I will create simple service class without any methods in it. Ideally this service class will have some methods to perform your project’s business operations.

package com.roytuts.spring.conditional.on.missing.clazz;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnMissingClass(value = "com.roytuts.spring.conditional.on.missing.clazz.MissingClass")
public class SpringConfig {
	@Bean
	public SpringService springService() {
		return new SpringService();
	}
}

Main Class

I need this main class to test the application.

package com.roytuts.spring.conditional.on.missing.clazz;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringConditionalOnMissingClassApp implements CommandLineRunner {
	@Autowired
	private SpringService springService;
	public static void main(String[] args) {
		SpringApplication.run(SpringConditionalOnMissingClassApp.class, args);
	}
	@Override
	public void run(String... args) throws Exception {
		System.out.println("Spring service fully qualified class name: " + springService.getClass());
	}
}

Running the above main class will produce below output:

Spring service fully qualified class name: class com.roytuts.spring.conditional.on.missing.clazz.SpringService

If MissingClass exists then you will see the following error in console:

The following candidates were found but could not be injected:
	- Bean method 'springService' in 'SpringConfig' not loaded because @ConditionalOnMissingClass found unwanted class 'com.roytuts.spring.conditional.on.missing.clazz.MissingClass'

Or

Field springService in com.roytuts.spring.conditional.on.missing.clazz.SpringConditionalOnMissingClassApp required a bean of type 'com.roytuts.spring.conditional.on.missing.clazz.SpringService' that could not be found.

That’s all about conditional on class and conditional on missing class in Spring framework.

Source Code

@ConditionalOnClass @ConditionalOnMissingClass

Leave a Comment