1. Description of the problem
In the actual system application development I often encounter such a class of requirements, and I believe that you will often encounter in your work.
- The same system is deployed in multiple provinces.
- A business in Beijing is one way of implementation, based on the needs of Beijing users.
- The same business is implemented in Shanghai in another way, much the same way as in Beijing.
When we encounter such a requirement, we usually define an interface for the business implementation, e.g.
Implemented this way in the Beijing environment, e.g.
Implemented this way in the Shanghai environment, e.g.
Then we write a mock business test case.
When we execute this test case an exception must be thrown. This is because Spring has found two IDemoService implementation classes. It does not know which implementation class to instantiate as the actual business processing bean for IDemoService. of course we expect the following state.
- When deploying the system in Beijing, use DemoServiceBeijing as the IDemoService implementation class to complete the dependency injection
- When deploying the system in Shanghai, use DemoServiceShanghai as the implementation class of IDemoService to complete the dependency injection.
2. Relatively low-level solutions
In the face of the above requirements, let’s start with a few relatively low-level solutions, which can achieve our desired state, but are not friendly enough for operation and maintenance.
2.1. Option 1: Use
If you deploy the system in Beijing, add
@Primary to the DemoServiceBeijing class. The purpose of this annotation is to force a single implementation class to be chosen from multiple implementations, and if Spring doesn’t know which one to choose, we tell it the default one.
2.2. Option 2: Use the
Because the Resource annotation uses names for dependency injection by default. So what is used is the DemoServiceBeijing implementation class (the variable name is explicitly called demoServiceBeijing, with lowercase initials).
2.3. Option 3: Use the
For the same reason as above, use the
@Qualifier annotation to specify the name of the bean for dependency injection.
The three solutions mentioned above can solve the problem of using different interface implementation classes for different deployment environments to accomplish dependency injection. But this is not good, because once we change the deployment environment from beijing to shanghai, we need to change the location or content of the above annotations all over again (all the implementation class code should be changed).
3. A relatively advanced solution
We propose a further expectation: that is, the operation of switching deployment environments can be done by modifying only one configuration. For example.
When we want to switch the deployment environment from Beijing to Shanghai, we just need to change beijing to shanghai in the above configuration, how to achieve this?
Add the ConditionalOnProperty annotation to the Beijing implementation class, with the value of havingValue as beijing.
Add the ConditionalOnProperty annotation to the Shanghai implementation class, with the value of havingValue as shanghai.
ConditionalOnProperty annotation does here is: it reads the configuration file and finds
deploy.since, and matches the value of that configuration with
havingValue, and instantiates whichever class matches as the implementation bean of the interface to inject into the Spring container (of course the injection process needs to be with the