Spring and springboot multiple data source configuration and problem solving

Let me talk about the problems encountered first.

No qualifying bean of type [javax.sql.DataSource] is defined: expected single matching bean but found 2: pgDataSource, DataSource
1
This is the problem that occurred when I configured dual data sources in the springboot project , seeing this problem, the first reaction is that @Autowired used somewhere in the code injects DataSource, but I did not find such code in the project.
Until I read the exception information in the following part

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSourceInitializer': Invocation of init method failed; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.sql.DataSource] is defined: expected single matching bean but found 2: postgresqlDataSource,bpmDataSource
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:136) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:408) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1570) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:545) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:220) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:351) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:332) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerPostProcessor.postProcessAfterInitialization(DataSourceInitializerPostProcessor.java:62) ~[spring-boot-autoconfigure-1.3.5.RELEASE.jar:1.3.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:422) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1583) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:545) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    ... 40 common frames omitted
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
才发现是dataSourceInitializer这个类导致的

@PostConstruct
    public void init() {
        if (!this.properties.isInitialize()) {
            logger.debug("Initialization disabled (not running DDL scripts)");
            return;
        }
        if (this.applicationContext.getBeanNamesForType(DataSource.class, false,
                false).length > 0) {
            this.dataSource = this.applicationContext.getBean(DataSource.class);
        }
        if (this.dataSource == null) {
            logger.debug("No DataSource found so not initializing");
            return;
        }
        runSchemaScripts();
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
This is the abnormal code. this.applicationContext.getBean(DataSource.class); In this position, the data source is obtained by the type, and then the above exception occurs.

The following describes the method of springboot's annotation to configure multiple data sources


@Configuration
public class GlobalDataConfiguration {
    @Bean(name = "bpmDataSource") @Primary
    @ConfigurationProperties
    (prefix = "bpm.datasource")
    public DataSource primaryDataSource() {
        return new com .alibaba.druid.pool.DruidDataSource();
    }

    @Bean(name = "postgresqlDataSource")
    @ConfigurationProperties(prefix = "workbench.pgsql")
    public DataSource secondaryDataSource() {
        DataSource dataSource = new com.alibaba.druid.pool. DruidDataSource();
        return dataSource;
    }

    @Bean(name = "mysqlNamedParameterJdbcTemplate")
    public NamedParameterJdbcTemplate primaryJdbcTemplate(@Qualifier("bpmDataSource") DataSource dataSource) {
        return new NamedParameterJdbcTemplate(dataSource);
    }

    @Bean(name = "postgresqlNamedParameterJdbcTemplate")
    public NamedParameterJdbcTemplate secondaryJdbcTemplate(@Qualifier("postgresqlDataSource") DataSource dataSource) {
        return new NamedParameterJdbcTemplate(dataSource);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
bpm.datasource.url=
bpm.datasource.username=
bpm.datasource.password=

workbench.pgsql.url=
workbench.pgsql.username=
workbench.pgsql.password=
1
2
3
4
5
6
7
After switching to this configuration the above problem is solved.

    if (this.applicationContext.getBeanNamesForType(DataSource.class, false,
                false).length > 0) {
            this.dataSource = this.applicationContext.getBean(DataSource.class);
        }
1
2
3
4
But there are also two data sources in this code, I don't understand it very much, and continue to track the code for analysis. The length judged by the subordinate is the total amount of the data source, which is no problem.
Then the problem occurs in the getBean method.
The next step is to enter the class DefaultListableBeanFactory

@Override
    public <T> T getBean(Class<T> requiredType) throws BeansException {
        return getBean(requiredType, (Object[]) null);
    }

    @Override
    public <T> T getBean(Class< T> requiredType, Object... args) throws BeansException {
        Assert.notNull(requiredType, "Required type must not be null");
        String[] beanNames = getBeanNamesForType(requiredType);
        if (beanNames.length > 1) {
            ArrayList< String>
            for (String beanName : beanNames) {
                if (!containsBeanDefinition(beanName) || getBeanDefinition(beanName).isAutowireCandidate()) {
                    autowireCandidates.add(beanName);
                }
            }
            if (autowireCandidates.size() > 0) {
                beanNames = autowireCandidates.toArray(new String[autowireCandidates.size()]);
            }
        }
        if (beanNames.length == 1) {
            return getBean(beanNames[0], requiredType, args);
        }
        else if (beanNames.length > 1) {
            Map<String, Object> candidates = new HashMap<String, Object>();
            for (String beanName : beanNames) {
                candidates.put(beanName, getBean(beanName, requiredType, args));
            }
            String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
            if (primaryCandidate != null) {
                return getBean(primaryCandidate, requiredType, args);
            }
            String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
            if (priorityCandidate != null) {
                return getBean(priorityCandidate, requiredType, args);
            }
            throw new NoUniqueBeanDefinitionException(requiredType, candidates.keySet());
        }
        else if (getParentBeanFactory() != null) {
            return getParentBeanFactory().getBean(requiredType, args);
        }
        else {
            throw new NoSuchBeanDefinitionException(requiredType);
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
Through the above code, we can find that when there are multiple implementations of the same type, beanNames.length > 1, and then go down to the determinePrimaryCandidate method, we can see To

isPrimary(candidateBeanName, beanInstance)
1
this verification code

protected boolean isPrimary(String beanName, Object beanInstance) {
        if (containsBeanDefinition(beanName)) {
            return getMergedLocalBeanDefinition(beanName).isPrimary();
        }
        BeanFactory parentFactory = getParentBeanFactory();
        return (parentFactory instanceof DefaultListableBeanFactory &&
                ((DefaultListableBeanFactory) parentFactory).isPrimary(beanName, beanInstance));
    }
1
2
3
4
5
6
7
8
At this position we found the root cause of the problem, we used it when configuring with springboot @Primary annotation, then spring will load the object with this annotation by default. The problem in this article occurs when there are multiple objects and it is impossible to distinguish which object is loaded by default. Of course, based on the principle of configurable and programmable, vice versa is also universal. We can add

primary="true"
1 to the configuration xml of the data source, and
the problem is solved happily.

This is the final configuration file for multiple database connections

<?xml version="1.0" encoding="UTF-8 "?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd   
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
    default-autowire="byName">

    <bean id="bpmDataSource" class="com.alibaba.druid.pool.DruidDataSource"
        init-method="init"destroy-method="close"  primary="true" >
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="${datasource.url}" />
        <property name="username" value="${datasource.username}" />
        <property name="password" value="${datasource.password}" />
        <!-- 配置初始化大小、最小、最大 -->
        <property name="initialSize" value="1" />
        <property name="minIdle" value="1" />
        <property name="maxActive" value="40" />
        <!-- 配置获取连接等待超时的时间 -->
        <property name="maxWait" value="60000" />         <property name="timeBetweenEvictionRunsMillis" value="60000" />
        <!-- How long does it take to configure the interval to detect idle connections that need to be closed, in milliseconds-->

        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="25200000" />
        <property name="validationQuery" value="SELECT 'x'" />
        <property name="testWhileIdle" value="true" />
        <property name="testOnBorrow" value="false" />
        <property name="testOnReturn" value="false" />
    </bean>

    <bean id="mysqlTransactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource">
            <ref local="bpmDataSource" />
        </property>
    </bean>

    <bean id="mysqlNamedParameterJdbcTemplate"
        class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
        <constructor-arg index="0" ref="bpmDataSource" />
    </bean>

    <context:component-scan base-package="com.demo.sop.service" />

    <!-- pgsql -->
    <bean id="postgresqlDataSource" class="com.alibaba.druid.pool.DruidDataSource"
        init-method="init" destroy-method="close">
        <property name="driverClassName" value="org.postgresql.Driver" />
        <property name="url" value="${pgsql.url}" />
        <property name="username" value="${pgsql.username}" />         <!-- configure initialization size, min, max -->
        <property name="password" value="${pgsql.password}" />

        <property name="initialSize" value="1" />
        <property name="minIdle" value="1" />
        <property name="maxActive" value="40" />
        <!-- Configure to get connection waiting Timeout time-->
        <property name="maxWait" value="60000" />
        <!-- How long is the configuration interval to perform detection to detect idle connections that need to be closed, in milliseconds-->
        <property name=" timeBetweenEvictionRunsMillis" value="60000" />
        <!-- Configure the minimum lifetime of a connection in the pool, in milliseconds-->
        <property name="minEvictableIdleTimeMillis" value="25200000" />
        <property name="validationQuery " value="SELECT 'x'" />
        <property name="testWhileIdle" value="true" />
        <property name="testOnBorrow" value="false" />
        <property name="testOnReturn" value="false" />
        <!-- 配置监控统计拦截的filters -->
        <property name="filters" value="stat,log4j" />
    </bean>
    <bean id="postgresqlTransactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource">
            <ref local="postgresqlDataSource" />
        </property>
    </bean>
    <tx:annotation-driven transaction-manager="postgresqlTransactionManager" />

    <bean id="postgresqlNamedParameterJdbcTemplate"
        class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
        <constructor-arg index="0" ref="postgresqlDataSource" />
    </bean>

</beans>


转子:
     http://blog.csdn.net/xupeng874395012/article/details/69419791

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326428187&siteId=291194637