Home > database >  @Autowired or constructor inject non-managed classes into Spring context @Service for better testing
@Autowired or constructor inject non-managed classes into Spring context @Service for better testing

Time:12-08

In a Spring Boot project, we access our non managed DAO classes through a static method:

MyDao myDao = DaoProvider.getMyDao();

We use this in Spring managed @Service classes:

@Service
public class MyService {
    
    public void foo() {
        MyDao myDao = DaoProvider.getMyDao();
        myDao.bar();
        ....
    }
}

This makes it hard to write unit tests. Static mocks are no option, because they are slowing down our build pipeline.

We would rather have a solution with @Autowired or constructor dependency and some kind of configuration, that decides which class to inject:

@Service
public class MyService {
    
    @Autowired
    MyDao myDao;
    
    public void foo() {
        myDao.bar();
        ....
    }
}

Obviously, someone has to tell Spring what class to inject, is there a way to do so?:

public class NonManagedSpringInjectionConfiguration {
    
    @Bean
    MyDao getMyDao() {
        return DaoProvider.getMyDao();
    }
}

CodePudding user response:

You just have to specify the profil for example :

@Configuration
public class NonManagedSpringInjectionConfiguration {
    
    @Bean
    @Profile("dev")
    MyDao getMyDao() {
        return DaoProvider.getMyDao();
    }
}

CodePudding user response:

Option 1

If you don't want to turn your MyDao into a Spring-managed Bean, your simpler and probably best option is to instead create the MyService Spring-managed Bean programmatically instead of relying on @Service annotation. First, just remove @Service from MyService and adjust its constructor to accept MyDao:

public class MyService {

    MyDao myDao;

    public MyService(MyDao myDao) {
        this.myDao = myDao;
    }
    
    public void foo() {
        myDao.bar();
        ....
    }
}

And now you just need to register a MyService bean using @Bean in a Configuration class as follows:

@Configuration
public class WhateverConfiguration {
    
    @Bean
    MyService myService() {
        return new MyService(DaoProvider.getMyDao());
    }
}

Option 2

If instead there is a possibility to make MyDao a Spring-managed Bean, then just keep your NonManagedSpringInjectionConfiguration as is:

@Configuration
public class NonManagedSpringInjectionConfiguration {
    
    @Bean
    MyDao getMyDao() {
        return DaoProvider.getMyDao();
    }
}

But then rely on constructor injection instead of on @Autowired. It makes MyService easier to unit test and also explicitly defines that MyDao is mandatory for MyService to work properly. In this case you would keep @Service and rely on Spring to instantiate it:

@Service
public class MyService {

    MyDao myDao;

    public MyService(MyDao myDao) {
        this.myDao = myDao;
    }

    public void foo() {
        myDao.bar();
        ....
    }
}
  • Related