February 13, 2022

Spring @Conditional Annotation

Using Spring @Conditional annotation you can conditionally register a component. With @Conditional annotation you need to specify a condition and the component is registered only if the condition is true.

For specifying the condition you need to implement org.springframework.context.annotation.Condition interface.

Where to use @Conditional annotation

The @Conditional annotation may be used in one of the following ways:

  • As a type-level annotation on any class directly or indirectly annotated with @Component, including @Configuration classes. If a @Configuration class is marked with @Conditional, all of the @Bean methods, @Import annotations, and @ComponentScan annotations associated with that class will be subject to the conditions.
  • As a meta-annotation, for the purpose of composing custom stereotype annotations. You can see @Profile annotation in Spring framework uses @Conditional annotation.
    @Target(value={TYPE,METHOD})
    @Retention(value=RUNTIME)
    @Documented
    @Conditional(value=org.springframework.context.annotation.ProfileCondition.class)
    public @interface Profile
    
  • As a method-level annotation on any @Bean method

Condition interface

org.springframework.context.annotation.Condition is a functional interface with a single abstract method matches().

matches(ConditionContext context, AnnotatedTypeMetadata metadata)

matches() method has to be implemented and the condition checked with in this method returns true or false. If true is returned then the component is registered otherwise not.

Spring @Conditional annotation examples

With this basic knowledge about @Conditional annotation and Condition interface let’s see an example depicting the usage.

What we need to do is to register either a "dev" datasource or "prod" datasource based on the value in the properties file.

src/main/resources/db.properties
#DB configuration for dev
db.dev.url=jdbc:oracle:thin:@localhost:1521/XEPDB1
db.dev.user=test
db.dev.password=test
db.dev.driver_class_name=oracle.jdbc.driver.OracleDriver

#DB configuration for prod
db.prod.url=jdbc:oracle:thin:@192.156.134.111:1523/XEPDB1
db.prod.user=sysuser
db.prod.password=test
db.prod.driver_class_name=oracle.jdbc.driver.OracleDriver

db.env=prod
Config classes
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;

@Configuration("dbConfig")
@PropertySource(value="classpath:properties/db.properties")
public class DBConfiguration {
  @Autowired
  private Environment env;
  
  @Bean
  @Conditional(DevDBCondition.class)
  public BasicDataSource devDataSource() {
    BasicDataSource ds = new BasicDataSource();
    ds.setDriverClassName(env.getProperty("db.dev.driver_class_name"));
    ds.setUrl(env.getProperty("db.dev.url"));
    ds.setUsername(env.getProperty("db.dev.user"));
    ds.setPassword(env.getProperty("db.dev.password"));
    return ds;
  }
	
  @Bean
  @Conditional(ProdDBCondition.class)
  public BasicDataSource prodDataSource() {
    BasicDataSource ds = new BasicDataSource();
    ds.setDriverClassName(env.getProperty("db.prod.driver_class_name"));
    ds.setUrl(env.getProperty("db.prod.url"));
    ds.setUsername(env.getProperty("db.prod.user"));
    ds.setPassword(env.getProperty("db.prod.password"));
    return ds;
  }
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class UserConfig {
  @Bean
  public UserDAO userDAO() {
    return new UserDAOImpl();
  }
}
Condition class implementations
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;

public class DevDBCondition implements Condition {
  @Override
  public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    Environment env = context.getEnvironment();
    return env.getProperty("db.env").equals("dev");
  }
}
public class ProdDBCondition implements Condition {
  @Override
  public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    Environment env = context.getEnvironment();
    return env.getProperty("db.env").equals("prod");
  }
}
UserDAO interface
public interface UserDAO {
	public void getUsers();
}
UserDAOImpl class
public class UserDAOImpl implements UserDAO {
  @Autowired
  BasicDataSource ds;
  public void getUsers() {
    System.out.println("In getUsers method");
    System.out.println("Driver class name- " + ds.getDriverClassName());
    System.out.println("DB User- " + ds.getUsername());
    System.out.println("DB URL- " + ds.getUrl());
  }
}
Class to run this example.
public class App {
  public static void main(String[] args) {
    AbstractApplicationContext context = new AnnotationConfigApplicationContext(DBConfiguration.class, UserConfig.class);
    UserDAO userDAO = context.getBean("userDAO", UserDAOImpl.class);
    userDAO.getUsers();
    context.close();
  }
}
Output
In getUsers method
Driver class name- oracle.jdbc.driver.OracleDriver
DB User- sysuser
DB URL- jdbc:oracle:thin:@192.156.134.111:1523/XEPDB1

As you can see prodDataSource Bean is registered because db.env=prod is set. In db.properties if you change db.env=dev and run it then devDataSource Bean is registered. With that output is

In getUsers method
Driver class name- oracle.jdbc.driver.OracleDriver
DB User- test
DB URL- jdbc:oracle:thin:@localhost:1521/XEPDB1

That's all for the topic Spring @Conditional Annotation. If something is missing or you have something to share about the topic please write a comment.


You may also like

No comments:

Post a Comment