When working with applications that use the Spring framework, you will need to know how to declare and use Beans in the Spring container. Normally, we just need to declare Bean for a certain class and Spring will automatically create Bean for that class in the Spring container. But in some cases, we will have a need: even though we declare Bean for a class, we only want Spring to create Bean for that class under some specific conditions. To do this, you can use the @Conditional annotation. Beans declared with the @Conditional annotation will only be initialized if a certain condition is satisfied, for example, another Bean has been initialized in the Spring container, for example… How is it in detail? We will learn about this @Conditional annotation together in this tutorial!
For example, I have 2 simple classes as follows:
1 2 3 4 5 |
package com.huongdanjava.spring; public record Clazz(String name) { } |
and:
1 2 3 4 5 |
package com.huongdanjava.spring; public record Student(String name) { } |
To configure beans for the above classes in the Spring container, I will define an annotated class with the @Configuration annotation as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
package com.huongdanjava.spring; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class ApplicationConfiguration { @Bean public Student student() { return new Student("Khanh"); } @Bean public Clazz clazz() { return new Clazz("A"); } } |
Now, if you get the Student information defined in the Spring container:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package com.huongdanjava.spring; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class Application { public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfiguration.class); Student student = (Student) context.getBean("student"); System.out.println(student.name()); } } |
then you will see the following results:
If I now want the Student class bean to only be initialized in the Spring container if a certain condition is met, I can annotate the method to create the Student class bean in the ApplicationConfiguration class with the @Conditional annotation.
This @Conditional annotation has an attribute that is a list of classes implementing the Condition interface. To represent whether a condition is met or not, the Condition interface defines a matches() abstract method with a boolean return type.
Now, I will try adding a new class that implements the Condition interface and simply return false to show that the condition is not met, as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package com.huongdanjava.spring; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; public class ConditionalImpl implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { return false; } } |
Depending on your needs, you can implement the matches() method accordingly!
Declare the @Conditional annotation with this ConditionalImpl class with the student() method in the ApplicationConfiguration class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
package com.huongdanjava.spring; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; @Configuration public class ApplicationConfiguration { @Bean @Conditional(ConditionalImpl.class) public Student student() { return new Student("Khanh"); } @Bean public Clazz clazz() { return new Clazz("A"); } } |
Then run the example again, you will see the error “No bean named ‘student’ available” as follows:
Because the conditions to create the Student bean are not met, the Student bean cannot be created! If you change the return value of the matches() method in the ConditionalImpl class to true:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package com.huongdanjava.spring; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; public class ConditionalImpl implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { return true; } } |
then there will be no more errors!… Try it, guys!
If there are many conditions applied to the initialization of a bean in the Spring container, for example, I have another class implementing the Condition interface as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package com.huongdanjava.spring; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; public class AnotherConditionalImpl implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { return false; } } |
you can declare a list of conditions as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
package com.huongdanjava.spring; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; @Configuration public class ApplicationConfiguration { @Bean @Conditional({ConditionalImpl.class, AnotherConditionalImpl.class}) public Student student() { return new Student("Khanh"); } @Bean public Clazz clazz() { return new Clazz("A"); } } |
In this case, the Student class bean is only initialized if all conditions are met. That means the matches() method in these classes all return true!
For my example, because the matches() method in the AnotherConditionalImpl class returns false, you will also see the error of not finding the bean of the Student class as above.
If you want to apply the condition to all beans in the class declared with the @Configuration annotation, you can annotate the @Conditional annotation at the class level as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
package com.huongdanjava.spring; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; @Configuration @Conditional({ConditionalImpl.class, AnotherConditionalImpl.class}) public class ApplicationConfiguration { @Bean public Student student() { return new Student("Khanh"); } @Bean public Clazz clazz() { return new Clazz("A"); } } |
At this point, you will see that the bean of the Clazz class is not initialized in the Spring container, because one of our two conditions is not satisfied. If I run the code to get the bean of the Clazz class as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
package com.huongdanjava.spring; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class Application { public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfiguration.class); Clazz clazz = (Clazz) context.getBean("clazz"); System.out.println(clazz.name()); Student student = (Student) context.getBean("student"); System.out.println(student.name()); } } |
you will see the error: