Spring – Conditional annotation

Spring 4 has introduced a new annotation @Conditional. It is used to develop an “If-Then-Else” type of conditional checking for bean registration. Let us see how we can use @Conditional annotation to check a property value is dev or prod from environment. For learning we will use Java based configurations in this example.

Create Project Domain class with id, name and duration properties with getters only as shown below.

public class Project {

    private int id;
    private String name;
    private double duration;

    public Project(int id, String name, double duration) {
        this.id = id;
        this.name = name;
        this.duration = duration;
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public double getDuration() {
        return duration;
    }
}

Create Utility classes to represent in-memory Databases. For simplicity, we are not interacting with Databases. DevDatabaseUtil class represents DEV database and ProductionDatabaseUtil class represents PROD database with some data.

public interface DataSource {

    List<Project> getProjectsList();
}
public class DevDatabaseUtil implements DataSource {

    @Override
    public List<Project> getProjectsList() {

        List<Project> projects = new ArrayList<>();
        Project projectWeb = new Project(1, "Web page for Qwerty", 160);
        Project projectWeb2 = new Project(2, "Web page for Asdfgh", 40);
        Project projectWeb3 = new Project(3, "Fix web page for Dfrtghy", 45.50);

        projects.add(projectWeb);
        projects.add(projectWeb2);
        projects.add(projectWeb3);

        return projects;
    }
}
public class ProductionDatabaseUtil implements DataSource {

    @Override
    public List<Project> getProjectsList() {

        List<Project> projects = new ArrayList<>();
        Project projectWeb = new Project(100, "Web page for Client 1", 230);
        Project projectWeb2 = new Project(200, "Web page for Client 2", 35);


        projects.add(projectWeb);
        projects.add(projectWeb2);

        return projects;
    }
}

Create a ProjectDAO class to represent DAO Layer.

public class ProjectService {

    private ProjectDAO projectDAO;

    public ProjectService(ProjectDAO projectDAO) {
        this.projectDAO = projectDAO;
    }

    public List<Project> getProjects() {
        return projectDAO.getProjects();
    }
}

Now it’s time to implement Conditional checking by using@Conditional annotation. Both condition checking classes implements Spring’s Condition interface matches() method. Both Conditional classes checks for “database.name” from environment. If this values is “dev”, then DevDataSourceCondition’s matches() method returns true. Otherwise false. In the same way, if this property value is “prod”, then ProdDataSourceCondition’s matches() method returns true. Otherwise false.

public class DevDataSourceCondition implements Condition {

    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        String dbname = conditionContext.getEnvironment().getProperty("database.name");
        return dbname.equalsIgnoreCase("dev");
    }
}
public class ProductionDataSourceCondition implements Condition {

    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {

        String dbname = conditionContext.getEnvironment().getProperty("database.name");
        return dbname.equalsIgnoreCase("prod");
    }
}

Create Spring’s Java-based configuration classes

@Configuration
public class ProjectConfig {

    @Autowired
    private DataSource dataSource;

    @Bean
    public ProjectService projectService() {
        return new ProjectService(projectDAO());
    }

    @Bean
    public ProjectDAO projectDAO() {
        System.out.println("ProjectDAO projectDAO()" + dataSource);
        return new ProjectDAO(dataSource);
    }
}
@Configuration
public class ProjectDataSourceConfig {

    @Bean(name="dataSource")
    @Conditional(value=DevDataSourceCondition.class)
    public DataSource getDevDataSource() {
        return new DevDatabaseUtil();
    }

    @Bean(name="dataSource")
    @Conditional(ProductionDataSourceCondition.class)
    public DataSource getProdDataSource() {
        return new ProductionDatabaseUtil();
    }

}

Now it’s time to write JUnits to test Spring’s @Conditional classes.

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SpringForBlogDemoApplication.class)
public class ProdConditionalTest {

    @Autowired
    private ApplicationContext applicationContext;

    @Test
    public void testProdDataSource() {
        ProjectService service = (ProjectService) applicationContext.getBean("projectService");
        assertNotNull(service);
        List<Project> projects = service.getProjects();
        assertEquals(2, projects.size());
        assertEquals("Web page for Client 1", projects.get(0).getName());
        assertEquals("Web page for Client 2", projects.get(1).getName());
    }
}
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SpringForBlogDemoApplication.class)
public class DevConditionalTest {

    @Autowired
    private ApplicationContext applicationContext;

    @Test
    public void testDevDataSource() {
        ProjectService service = (ProjectService) applicationContext.getBean("projectService");
        assertNotNull(service);
        List<Project> projects = service.getProjects();
        assertEquals(3, projects.size());
        assertEquals("Web page for Qwerty", projects.get(0).getName());
        assertEquals("Web page for Asdfgh", projects.get(1).getName());
        assertEquals("Fix web page for Dfrtghy", projects.get(2).getName());
    }
}

 

Spring Profiles

A Spring application can be configured to behave differently in different environments using different profiles. For example, you can register beans based on what kind of database you’re running (development, production, test).

You can then configure a deployment to run using zero or more profiles, by providing a spring.profiles.active property. For example, to run the application using production and dev profile, add the following line in application.properties:

spring.profiles.active: dev

To test it, let’s create two classes responsible for database datasource setup and then run our application with datasource configuration setup based on active profile.

@Component
@Profile("dev")
public class DevDatasourceConfig implements DatasourceConfig {

    @Override
    public void setup() {
        System.out.println("Datasource config based on development profile");
    }
}
@Component
@Profile("production")
public class ProductionDatasourceConfig implements DatasourceConfig {

    @Override
    public void setup() {
        System.out.println("Datasource config based on production profile");
    }
}

To inject our DatasourceConfig interface; depending on the active profile we need to create a ‘test’ class. Spring will injects DevDatasourceConfig or ProductionDatasourceConfig bean:

@RestController
public class DatasourceProfileController {

    @Autowired
    DatasourceConfig datasourceConfig;

    @RequestMapping("/profiles")
    public String profiles() {
        datasourceConfig.setup();
        return "Database setup based on Spring profile configuration";
    }
}

When the “dev” profile is active spring injects DevDatasourceConfig object, and on call of setup() method following is the output:

Datasource config based on development profile

To see a list of active profiles you just need to inject Environment and then iterate through a list of active profiles.

@RestController
public class DatasourceProfileController {

    @Autowired
    DatasourceConfig datasourceConfig;

    @Autowired
    Environment environment;

    @RequestMapping("/profiles")
    public String profiles() {
        datasourceConfig.setup();

        for (final String profileName : environment.getActiveProfiles()) {
            System.out.println("Currently active profile - " + profileName);
        }
        
        return "Database setup based on Spring profile configuration";
    }
}
When you’ll run our app, tou should get a list of active profiles:
Datasource config based on development profile
Currently active profile - dev

Any bean that does not specify a profile belongs to “default” profile.

Spring also provides a way to set the default profile when no other profile is active – by using the “spring.profiles.default” property.

For testing purposes, to specify what profile is active you need to specify an active profile by using @ActiveProfiles annotation:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { SpringForBlogDemoApplication.class })
@ActiveProfiles("dev")
public class DatasourceProfileControllerTest {

    @Autowired
    DatasourceConfig datasourceConfig;

    @Test
    public void test_datasourceSetupBasedOnProfile() {
        datasourceConfig.setup();
    }
}

The profile names can also be passed in via a JVM system parameter. The profile names passed as the parameter will be activated during application start-up:

-Dspring.profiles.active=dev

DI – Configuring Beans

Component annotations

Spring creates beans for classes with auto component scanning features by using @Component. @Component annotation is not only one you can use for creating beans, there are others more ‘specialized’ annotations.

  • @Controller – Indicates a controller component in the presentation layer
  • @Service – Indicates a Service component in the business layer
  • @Repository – Indicates DAO component in the persistence layer

So, which one to use? It’s really doesn’t matter. Take a look at the source code one of those annotations:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
    String value() default "";
}

You will notice that all @Repository,@Service or @Controller are annotated with @Component. So, can we use just @Component for all the components for auto scanning? Yes, you can, and Spring will auto scan all your components with @Component annotated.

It’s working fine, but not a good practice, for readability, you should always declare @Repository,@Service or @Controller for a specified layer to make your code more easier to read.

Configuration classes

Let’s assume that our Service classes (SmsService, TweetService, MailService) are part of some third party JAR and we won’t be able to annotate those with @Component. In this case we can use Java configuration classes. To make our application working again we need to create some config class where we will add our service classes to be able to be scanned by Spring.

@Configuration
public class MessageConfig {

    @Bean
    public MessageService emailService() {
        return new EmailService();
    }

    @Bean
    public MessageService smsService() {
        return new SmsService();
    }

    @Bean
    public MessageService tweetService() {
        return new TweetService();
    }
}

Annotating a class with the @Configuration indicates that the class can be used by the Spring IoC container as a source of bean definitions. The @Bean annotation tells Spring that a method annotated with @Bean will return an object that should be registered as a bean in the Spring application context. The name of the methods become the names
of the beans by default.

DI – Two beans implementing same inteface

How to inject the right one?

To see what happens lets annotate with @Component our TweetService class.

@Component
public class TweetService implements MessageService {


    public void sendMessage(String subject, String message) {
        System.out.println("Tweet: " + subject + " - " + message);
    }
}

Trying to run the application you’ll get an error:

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.programmerscuriosity.springforblogdemo.di.example.four.MessageService] is defined: expected single matching bean but found 2: smsService,tweetService

Spring, when trying to find a suitable bean for injecting to the messageService variable, finds two beans – the instances of both TweetService and SmsService.

Let’s try to fix it. One of few ways to do it is, to specify a name in @Resource annotation.

@RestController
public class MessageController {

    @Resource(name = "tweetService")
    private MessageService messageService;

    @RequestMapping("/message")
    public String sendMessage() {
        messageService.sendMessage("Very important:", "Please read this carefully.");
        return "Your message was sent";
    }
}

Spring will then be able to inject the exact bean without any error.

Another alternative will be to give a name to the bean to match the resource name.

@RestController
public class MessageController {

    @Resource
    private MessageService messageService;

    @RequestMapping("/message")
    public String sendMessage() {
        messageService.sendMessage("Very important:", "Please read this carefully.");
        return "Your message was sent";
    }
}
@Component("messageService")
public class SmsService implements MessageService {

    @Override
    public void sendMessage(String subject, String message) {
        System.out.println("SMS: " + subject + " - " + message);
    }
}

Yet another option to tell Spring which bean to inject is to use @Primary annotation.

@Primary
@Component
public class TweetService implements MessageService {


    public void sendMessage(String subject, String message) {
        System.out.println("Tweet: " + subject + " - " + message);
    }
}
@Component
public class SmsService implements MessageService {

    @Override
    public void sendMessage(String subject, String message) {
        System.out.println("SMS: " + subject + " - " + message);
    }
}

The application should still run fine.

A more powerful (it not only works on setters, but on any method, including the constructors) but still very similar to @Resource is @Autowired annotation.

@RestController
public class MessageController {


    private MessageService messageService;

    @Autowired
    public MessageController(MessageService messageService) {
        this.messageService = messageService;
    }

    @RequestMapping("/message")
    public String sendMessage() {
        messageService.sendMessage("Very important:", "Please read this carefully.");
        return "Your message was sent";
    }
}

However, @Autowired does not take the name element. Instead, we need to use @Qualifier along with @Autowired.

@RestController
public class MessageController {


    private MessageService messageService;

    @Autowired
    public MessageController(@Qualifier("sms") MessageService messageService) {
        this.messageService = messageService;
    }

    @RequestMapping("/message")
    public String sendMessage() {
        messageService.sendMessage("Very important:", "Please read this carefully.");
        return "Your message was sent";
    }
}
@Component
@Qualifier("sms")
public class SmsService implements MessageService {

    @Override
    public void sendMessage(String subject, String message) {
        System.out.println("SMS: " + subject + " - " + message);
    }
}

By default, the qualifier of a bean will be same as its name.

@RestController
public class MessageController {


    private MessageService messageService;

    @Autowired
    public MessageController(@Qualifier("tweetService") MessageService messageService) {
        this.messageService = messageService;
    }

    @RequestMapping("/message")
    public String sendMessage() {
        messageService.sendMessage("Very important:", "Please read this carefully.");
        return "Your message was sent";
    }
}
@Component
public class TweetService implements MessageService {


    public void sendMessage(String subject, String message) {
        System.out.println("Tweet: " + subject + " - " + message);
    }
}

 

Spring also has @Inject , which is synonymous to @Autowired. It’s part of the Java CDI JSR-300 standard, and so Spring thought to support it. What is the difference between @Inject and @Autowired?

The short answer: There is no different and can be used interchangeably.

In more detail @Inject is part of the Java CDI (Contexts and Dependency Injection) standard introduced in Java EE 6 (JSR-300). Spring has chosen to support using @Inject synonymously with their own @Autowired annotation.@Autowired is Spring’s own annotation. In a Spring application, the two annotations works the same way as Spring has decided to support some JSR-300 annotations in addition to their own.