Conditional autowiring in Spring

In the last article we have seen that how we can configure beans based on certain environments.This is quite useful features spring have provided.Sometimes we need more granular control over the bean autowiring in spring, such as we need certain beans that needs to be autowired or injected on some complex condition (which is something more than certain environment).

for e.g. What if we need certain bean needs to be injected if some other bean have specific property value.
or inject an bean only if some other class is found on class-path.




Here Spring will help you to make such a configuration using @Conditional annotation.
Suppose we want the bean OperationUser to be created if the property "department= Operations" is set in your properties file.
@Bean
@Conditional(OpsDepartmentCondition.class)
public OperationsUser operationsUser(){
  OpeartionsUser opsUser = new OperationsUser();
  return opsUser;
}
We will take a look at how OpsDepartmentCondition will look like:
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 OpsDepartmentCondition implements Condition {

 @Override
 public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
  Environment env = context.getEnvironment();
  return env.getProperty("department").equals("Operations");
 }

}
The above code checks the presence of property department having value "Operations".

We will take a look at what resources are available when we implement condition interface.

ConditionContext : If we take a look at the the ConditionContext interface it has access to pretty low level resources in the spring container. 

    public interface ConditionContext {
    
     BeanDefinitionRegistry getRegistry();
    
     ConfigurableListableBeanFactory getBeanFactory();
    
     Environment getEnvironment();
    
     ResourceLoader getResourceLoader();
    
     ClassLoader getClassLoader();
    
    } 
    • getRegistry(): we get access to BeanDefinitionRegistry which has all the bean definitions, on basis of that you can access other beans to make certain autowiring decision based on that.
    • getEnvironment(): This method will give access to all the env properties, System properties which will help you in Autowiring of bean.
    • getResourceLoader(): This method can be helpful to check presence of certain file, read file and then make autowiring decision. /li>
    • getClassLoader(): This method can be helpful to check presence of certain classes, read class metadata and then make autowiring decision.

AnnotatedTypeMetadata 

    public interface AnnotatedTypeMetadata {
    
     boolean isAnnotated(String annotationName);
    
     Map<String, Object> getAnnotationAttributes(String annotationName);
    
     Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString);
    
     MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName);
    
     MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString);
    
    }
    
    • isAnnotated(String annotationName): This method can check whether any class bean is annotated with certain type of annotation (eg. @Qualifier, @NotNull or any of your custom made annotation and then make certain autowiring decision based on that.
    • getAnnotationAttributes(String annotationName): This method will give the access to annotationAttributes which will help you in autowiring of bean.
    • getAnnotationAttributes(String annotationName, boolean classValuesAsString): This method will give the access to annotationAttributes of specific class which will help you in autowiring of bean.
    • Other two methods are self explanatory which gives access to all anntoation attributes by annoationName and className respectively.

So do I have to implement the Condition interface every time for simple check as well ?

 Then the answer is  NO !

Spring framework is popular for its usage of annotation, be it in autowiring or any other tasks. So spring framework does provide certain annotations which have similar functionality exposed by above interface.

  • @ConditionalOnClass: This annotation will inject the bean if specified class is present on classpath. Lets say you want to inject an Configuration bean which is using certain datasource class, then you can use this annotaton. 
@ConditionalOnClass(DataSource.class)
      public class MySQLconfiguration {
   
      }
  • @ConditionalOnProperty: The @ConditionalOnProperty annotation is used to specify if a configuration will be loaded based on the presence and value of a Spring Environment property. 
@Bean
 @ConditionalOnProperty(
   name = "db", 
   havingValue = "local")
 @ConditionalOnMissingBean
 public DataSource dataSource() {
     DriverManagerDataSource dataSource = new DriverManagerDataSource();
   
     dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
     dataSource.setUrl("jdbc:mysql://localhost:3306/localDB?createDatabaseIfNotExist=true");
     dataSource.setUsername("username");
     dataSource.setPassword("password");
  
     return dataSource;
 }

// The bean that we have defined at the start of article using condition interface can use this annotation as below
@ConditionalOnProperty(name = "department", 
   havingValue = "Operations")
public OperationsUser operationsUser(){
  OpeartionsUser opsUser = new OperationsUser();
  return opsUser;
}
  • @ConditionalOnResource: The @ConditionalOnResource annotation help you to inject certain bean or load your configuration if certain resources are present. In following example we will configure hibernate properties to set on entityManager based on presence of resource mysql.properties file.
@ConditionalOnResource(
    resources = "classpath:mysql.properties")
  @Conditional(HibernateCondition.class)
  Properties hibernateProperties() {
      Properties properties = new Properties();
   
      properties.setProperty("hibernate.hbm2ddl.auto", 
        env.getProperty("mysql-hibernate.hbm2ddl.auto"));
      properties.setProperty("hibernate.dialect", 
        env.getProperty("mysql-hibernate.dialect"));
      properties.setProperty("hibernate.show_sql", 
        env.getProperty("mysql-hibernate.show_sql") != null
        ? env.getProperty("mysql-hibernate.show_sql") : "false");
      return properties;
  }

  • @ConditionalOnBean and @ConditionalOnMissingBean: The @ConditionalOnBean and @ConditionalOnMissingBean annotations let a bean be included based on the presence or absence of specific beans. You can use the value attribute to specify beans by type or name to specify beans by name.
@Configuration
 public class MyAutoConfiguration {

   // The UserService bean will be injected if accountService bean is present in container.
  @Bean
  @ConditionalOnBean(name="accountService")
  public UserService userService() { ... }

   // The MyService bean will be injected if accountService bean is not present in container.
  @Bean
  @ConditionalOnMissingBean(name="accountService")
  public MyService myService() { ... }

 }
  • @ConditionalOnWebApplication and @ConditionalOnNotWebApplication : The @ConditionalOnWebApplication and @ConditionalOnNotWebApplication annotations let a bean be included if applicationContext is webApplicationContext or not.
@Configuration
 public class MyAutoConfiguration {

        // The FileUploaderServlet bean will be injected if context is WebApplicationContext.
  @Bean
  @ConditionalOnWebApplication
  public FileUploaderServlet fileUploaderServlet() { ... }

        // The DesktopApplicationService bean will be injected if applicationContext is not webapplicationContext
  @Bean
  @ConditionalOnNotWebApplication
  public DesktopApplicationService desktopApplicationService() { ... }

 }

Cookies Consent

This website uses cookies to offer you a better Browsing Experience. By using our website, You agree to the use of Cookies

Privacy Policy