Solution of invalid automatic injection using @ Qualifier for multiple data sources in springboot

  • 2021-12-12 08:30:39
  • OfStack

Directory @ Qualifier auto-injection invalid troubleshooting root cause of the problem The way to solve the problem @ Qualifier's role and application @ Qualifier's role

Solution of @ Qualifier Auto Injection Invalid

Problem

When using springboot for multiple data sources, there is a problem that a single DataSource corresponds to multiple DataSourceBean.

Specific errors are as follows: XXXXX required a single bean, but 3 were found. It doesn't work to distinguish by @ Qualifier or adding name attribute to @ Bean.

The root cause of the problem

Mainly lies in DataSourceInitializer of SpringBoot, which is used in autoConfigure package to automatically initialize a built-in DataSource instance, and an injection problem occurred when creating this instance.

The process of creating an instance (record 1 is convenient to debug again later):

1. Call the constructor of DataSourceInitializer

2. Call the applyMergedBeanDefinitionPostProcessors method of AbstractAutowireCapableBeanFactory

3. Call the initializeBean method of AbstractAutowireCapableBeanFactory

4. The applyBeanPostProcessorsBeforeInitialization method of AbstractAutowireCapableBeanFactory, in which one loop uses multiple Bean processors to process DataSourceInitializer objects

5. Then jump to the init method of DataSourceInitializer using reflection

6. All DataSource implementation class object instances are obtained through this. applicationContext. getBean (DataSource. class).

7. The resolveNamedBean method of DefaultListableBeanFactory selects the instance object, and obtains all the objects conforming to requireType (that is, DataSource. class) through the getBeanNamesForType method inside.

8. If the number of instances of an object instance is greater than 1, the following two decisions are entered:

It is determined whether there is an instance of the Primary or an instance object with high priority, and if there is, a candidate object name is assigned to the candidateName. If not, it is set to null, and finally an exception of multiple instances is thrown.

The problem lies in

Because only @ Qualifier is used in the project, and DataSourceInitializer of springboot does not handle @ Qualifier, no instances are matched.

Creates multiple data source instances. In the case of multiple data sources, their remedial measure is to add the judgment of whether it is Primary and whether it is HighestPriority in the code.

To deal with which data source to take. Therefore, the solution has come out. Is to mark an instance as Primary or HighestPriority.

A solution to the problem

Add @ Primary annotation to a @ Bean, and you can achieve only one distinction.

In fact, it should be distinguished by priority here, but using @ Order, it is found that it is not this priority, and no relevant data is found, so we will study it later.

Function and application of @ Qualifier

The role of @ Qualifier

This is the official introduction

This annotation may be used on a field or parameter as a qualifier for candidate beans when autowiring. It may also be used to annotate other custom annotations that can then in turn be used as qualifiers.

The simple understanding is:

When using @ Autowire auto injection, add @ Qualifier ("test") to specify which object to inject; It can be used as a qualifier for filtering. When we make custom annotations, we can add @ Qualifier to its definition to filter the required objects. See the following code for this understanding, which is not easy to explain.

Function introduction

The first is the understanding of (1).


// We defined two TestClass Objects, which are testClass1 And testClass2
// If we are in another 1 Objects that are used directly in @Autowire To inject, spring I definitely don't know which object to use 
// Exceptions are excluded  required a single bean, but 2 were found
@Configuration
public class TestConfiguration {
   @Bean("testClass1")
   TestClass testClass1(){
       return new TestClass("TestClass1");
   }
   @Bean("testClass2")
   TestClass testClass2(){
       return new TestClass("TestClass2");
   }
}

The following is a normal reference


@RestController
public class TestController {
 
    // In this case, the combination of these two annotations is similar  @Resource(name="testClass1")
    @Autowired
    @Qualifier("testClass1")
    private TestClass testClass;
 
    @GetMapping("/test")
    public Object test(){
        return testClassList;
    }

}

The use of the @ Autowired and @ Qualifier annotations in this position is similar to @ Resource (name= "testClass1")

Understanding of (2)


@Configuration
public class TestConfiguration {
    // Let's adjust it in testClass1 Increase on @Qualifier Annotation 
    @Qualifier
    @Bean("testClass1")
    TestClass testClass1(){
        return new TestClass("TestClass1");
    }
 
    @Bean("testClass2")
    TestClass testClass2(){
        return new TestClass("TestClass2");
    }
}
@RestController
public class TestController {
    // We use it here 1 A list To receive testClass Object of 
    @Autowired
    List<TestClass> testClassList= Collections.emptyList();
    
    @GetMapping("/test")
    public Object test(){
        return testClassList;
    }
}

The result of our call is

[
{
"name": "TestClass1"
},
{
"name": "TestClass2"
}
]

We can see that all testclass have been obtained. Next, let's modify the code


@RestController
public class TestController {
 
    @Qualifier // We add a note here 
    @Autowired
    List<TestClass> testClassList= Collections.emptyList();
 
    @GetMapping("/test")
    public Object test(){
        return testClassList;
    }
}

Compared with the above code, @ Qualifier annotation is added to the receiving parameter. What is the difference in this way? We call it and the result is as follows:

[
{
"name": "TestClass1"
}
]

Only the TestClass object with the @ Qualifier annotation is returned, so that we can understand what the official tag filtering means.

In addition, @ Qualifier annotation can specify value, so that we can classify and filter the desired objects through values. The code is not listed here, and interested students can try it themselves.


Related articles: