3

1. Description of the problem

In actual system application development, I often encounter such a type of demand, and I believe that everyone will often encounter:

  • The same system is deployed in multiple provinces.
  • A business in Beijing is an implementation method based on the needs of users in Beijing.
  • The same business in Shanghai is another way of realizing it, which is similar to that in Beijing.

When encountering such a requirement, we usually define an interface for business implementation, such as:

 public interface IDemoService {
  public void doSomething();
}

This is achieved in the Beijing environment, for example:

 @Component
public class DemoServiceBeijing implements IDemoService {
  @Override
  public void doSomething() {System.out.println("北京的业务实现");}
}

This is achieved in the Shanghai environment, for example:

 @Component
public class DemoServiceShanghai implements IDemoService {
  @Override
  public void doSomething() {System.out.println("上海的业务实现");}
}

Then we write a simulated business test case

 @SpringBootTest
class DemoApplicationTests {
    //这里注入的demoService是DemoServiceShanghai,还是DemoServiceBeijing?
    @Resource
    IDemoService demoService;  
    @Test
    void testDemoService() {
        demoService.doSomething();
    }
}

When we execute this test case, an error will be reported, because Spring has found two implementation classes of IDemoService. It does not know which implementation class to instantiate as the actual business processing bean of IDemoService. Of course our desired state is:

  • When deploying the system in Beijing, use DemoServiceBeijing as the implementation class of IDemoService to complete dependency injection
  • When deploying the system in Shanghai, use DemoServiceShanghai as the implementation class of IDemoService to complete dependency injection

2. Relatively low-level solutions

In the face of the above requirements, let's talk about a few relatively low-level solutions. Although these solutions can achieve the desired state, they are not friendly enough for operation and maintenance.

2.1. Option 1: Use @Primary annotation

If you add @Primary to the class of DemoServiceBeijing when deploying the system in Beijing, the function of this annotation is to force the selection of an implementation class from multiple implementation classes. If Spring does not know which one to choose, we tell its a default.

 @Primary
@Component
public class DemoServiceBeijing implements IDemoService {

2.2. Option 2: Use @Resource Annotation

Because the Resource annotation uses the name for dependency injection by default, the variable name is clearly called demoServiceBeijing (the first letter is lowercase), and the DemoServiceBeijing implementation class is used.

 @Resource
IDemoService demoServiceBeijing;  //这里的变量名称指定了bean名称
//IDemoService demoService;  被替换掉

or

 @Resource(name = "demoServiceBeijing")  //使用resource注解明确指定名称
IDemoService demoService;

2.3. Option 3: Use @Qualifier annotation

In the same way as above, use the @Qualifier annotation to specify the name of the bean for dependency injection

 @Qualifier("demoServiceBeijing")  //使用Qualifier注解明确指定名称
@Resource
IDemoService demoService;

Although the three solutions mentioned above can be solved: the problem of using different interface implementation classes to complete dependency injection in different deployment environments. But this is not good, because once we want to change the deployment environment from beijing (Beijing) to shanghai (Shanghai), we need to modify the location or content of the above annotations (all implementation class codes must be modified).

3. Relatively advanced solutions

We put forward a further expectation: the operation of switching the deployment environment can be completed by only modifying one configuration. for example:

 deploy:
  province: beijing

When we expect to switch the deployment environment from Beijing to Shanghai, we only need to change the beijing in the above configuration to shanghai. How to achieve this?

  • Add the ConditionalOnProperty annotation to the implementation class in Beijing, and the value of havingValue is beijing
 @Component
@ConditionalOnProperty(value="deploy.province",havingValue = "beijing")
public class DemoServiceBeijing implements IDemoService {
  • Add the ConditionalOnProperty annotation to the Shanghai implementation class, and the value of havingValue is shanghai
 @Component
@ConditionalOnProperty(value="deploy.province",havingValue = "shanghai")
public class DemoServiceShanghai implements IDemoService {

The function of the ConditionalOnProperty annotation here is to read the configuration file and find deploy.province , and match the value of the configuration with havingValue, and whichever matches the class will be instantiated as the implementation class bean of the interface and injected into the bean In the Spring container (of course, the injection process needs to cooperate with @Component annotation implementation).

Welcome to pay attention to my announcement number: Antetokounmpo, reply 003 and present the PDF version of the author's column "The Way of Docker Cultivation", more than 30 high-quality docker articles. Antetokounmpo Blog: zimug.com


字母哥博客
933 声望1.5k 粉丝