Search code examples
javaspringpojo

Interface to concrete class conditional instantiation in Spring


I have a Spring based Java application where a lot of classes use the following autowired interface.. they work off this interface at all places.

@Autowired
private IOperatingSystemManager m_operatingSystemManager;

Right now, there is only one implementation of the interface as follows:

@Component
public class WindowsManager implements IOperatingSystemManager 
{
 // Windows based shenanigans
}

And the application works as expected. Spring is happy. Everybody is happy. Alright, not everybody...

So, I want to add another concrete implementation of IOperatingSystemManager ..

@Component
public class LinuxManager implements IOperatingSystemManager 
{
 // Linux based shenanigans
}

What we want is the auto wiring of IOperatingSystemManager conditionally based on a properties file setting. (say.. os=windows.. basically something that is an arbitrary string and cannot be derived from system properties etc. simply because this is a dummy example. the actual managers are not OS related.)

I don't want to change any of the classes who have autowired to the interface and are working off the interface. All I need is for Spring to look at some logic that will dictate the Autowiring of the variables and wire up the right concrete instance for:

@Autowired
IOperatingSystemManager m_operatingSystemManager 

at all the gazillion places.

The documentation & web search talk about profiles, condition, bean factory, qualifiers etc.. but we don't want to use Profiles; and Qualifiers seem to be needing changes to all the interface variable annotations.

Factory methods look promising, but being new to Spring, couldn't find a crisp answer.

What is a simple and recommended way to achieve this?


Solution

  • Instead of scanning the WindowsManager class, create one concrete instance that implements the IOperatingSystemManager interface or another one, depending on the your logical conditions.

    First, remove the @Component annotation from the WindowsManager class.

    Then, create and scan this @Configuration class, which will act as a factory for your beans:

    @Configuration
    public class OperatingSystemManagerFactory {
    
        @Bean
        public IOperatingSystemManager getOperatingSystemManager() {
            if ( /* some logic that evaluates to true if windows */ ) {
                return new WindowsManager();
            } else {
                // Linux default option ;)
                return new LinuxManager();
            }
        }
    }
    

    With this solution, you shouldn't need to update anyone of your classes that reference the IOperatingSystemManager interface.