Skip to content

Configuration properties cannot be bound to DruidDataSource when the environment contains a non-enumerable property source #39840

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
iQiFengLe opened this issue Mar 6, 2024 · 4 comments
Labels
status: duplicate A duplicate of another issue

Comments

@iQiFengLe
Copy link

  • spring boot version: 3.2.3

DbConfig.java

@Configuration
public class DbConfig {


    @Bean
    @ConfigurationProperties(value = "spring.datasource.log")
    public DataSource logDatasource(){
        return new DruidDataSource();
    }

    @Bean
    @ConfigurationProperties(value = "spring.datasource.user")
    public DataSource userDatasource(){
        return new DruidDataSource();
    }

}

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>example</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.3</version>
        <relativePath />
    </parent>
    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>com.zaxxer</groupId>
                    <artifactId>HikariCP</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.20</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jetty</artifactId>
        </dependency>
    </dependencies>

</project>

exception log:

2024-03-06T11:18:41.252+08:00 ERROR 22188 --- [           main] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} init error

java.sql.SQLException: url not set
	at com.alibaba.druid.pool.DruidDataSource.resolveDriver(DruidDataSource.java:1273) ~[druid-1.2.20.jar:na]
	at com.alibaba.druid.pool.DruidDataSource.init(DruidDataSource.java:898) ~[druid-1.2.20.jar:na]
	at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:1462) ~[druid-1.2.20.jar:na]
	at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:1458) ~[druid-1.2.20.jar:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
	at org.springframework.boot.context.properties.bind.JavaBeanBinder$BeanProperty.lambda$getValue$0(JavaBeanBinder.java:383) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.springframework.boot.context.properties.bind.DefaultBindConstructorProvider.getBindConstructor(DefaultBindConstructorProvider.java:46) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.springframework.boot.context.properties.bind.ValueObjectBinder$ValueObject.get(ValueObjectBinder.java:208) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.springframework.boot.context.properties.bind.ValueObjectBinder.bind(ValueObjectBinder.java:75) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.springframework.boot.context.properties.bind.Binder.lambda$bindDataObject$6(Binder.java:480) ~[spring-boot-3.2.3.jar:3.2.3]
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) ~[na:na]
	at java.base/java.util.AbstractList$RandomAccessSpliterator.tryAdvance(AbstractList.java:706) ~[na:na]
	at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:129) ~[na:na]
	at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:527) ~[na:na]
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:513) ~[na:na]
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) ~[na:na]
	at java.base/java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:150) ~[na:na]
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:na]
	at java.base/java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:647) ~[na:na]
	at org.springframework.boot.context.properties.bind.Binder.fromDataObjectBinders(Binder.java:488) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.springframework.boot.context.properties.bind.Binder.lambda$bindDataObject$7(Binder.java:479) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.springframework.boot.context.properties.bind.Binder$Context.withIncreasedDepth(Binder.java:597) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.springframework.boot.context.properties.bind.Binder$Context.withDataObject(Binder.java:583) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.springframework.boot.context.properties.bind.Binder.bindDataObject(Binder.java:479) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.springframework.boot.context.properties.bind.Binder.bindObject(Binder.java:418) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:350) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.springframework.boot.context.properties.bind.Binder.lambda$bindDataObject$5(Binder.java:477) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.springframework.boot.context.properties.bind.JavaBeanBinder.bind(JavaBeanBinder.java:99) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.springframework.boot.context.properties.bind.JavaBeanBinder.bind(JavaBeanBinder.java:87) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.springframework.boot.context.properties.bind.JavaBeanBinder.bind(JavaBeanBinder.java:63) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.springframework.boot.context.properties.bind.Binder.lambda$bindDataObject$6(Binder.java:480) ~[spring-boot-3.2.3.jar:3.2.3]
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) ~[na:na]
	at java.base/java.util.AbstractList$RandomAccessSpliterator.tryAdvance(AbstractList.java:706) ~[na:na]
	at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:129) ~[na:na]
	at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:527) ~[na:na]
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:513) ~[na:na]
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) ~[na:na]
	at java.base/java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:150) ~[na:na]
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:na]
	at java.base/java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:647) ~[na:na]
	at org.springframework.boot.context.properties.bind.Binder.fromDataObjectBinders(Binder.java:488) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.springframework.boot.context.properties.bind.Binder.lambda$bindDataObject$7(Binder.java:479) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.springframework.boot.context.properties.bind.Binder$Context.withIncreasedDepth(Binder.java:597) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.springframework.boot.context.properties.bind.Binder$Context.withDataObject(Binder.java:583) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.springframework.boot.context.properties.bind.Binder.bindDataObject(Binder.java:479) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.springframework.boot.context.properties.bind.Binder.bindObject(Binder.java:418) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:350) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:339) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:269) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:256) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.springframework.boot.context.properties.ConfigurationPropertiesBinder.bind(ConfigurationPropertiesBinder.java:94) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.bind(ConfigurationPropertiesBindingPostProcessor.java:96) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.postProcessBeforeInitialization(ConfigurationPropertiesBindingPostProcessor.java:79) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:422) ~[spring-beans-6.1.4.jar:6.1.4]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1778) ~[spring-beans-6.1.4.jar:6.1.4]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) ~[spring-beans-6.1.4.jar:6.1.4]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) ~[spring-beans-6.1.4.jar:6.1.4]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) ~[spring-beans-6.1.4.jar:6.1.4]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.1.4.jar:6.1.4]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) ~[spring-beans-6.1.4.jar:6.1.4]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-6.1.4.jar:6.1.4]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:975) ~[spring-beans-6.1.4.jar:6.1.4]
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:959) ~[spring-context-6.1.4.jar:6.1.4]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:624) ~[spring-context-6.1.4.jar:6.1.4]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:334) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1354) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1343) ~[spring-boot-3.2.3.jar:3.2.3]
	at org.example.App.main(App.java:20) ~[classes/:na]

After jetty is introduced, the DruidDataSource.getConnection method will be called when the @ConfigurationProperties property is filled.

Causes DruidDataSource.init method initialization exception

After removing jetty, the DruidDataSource.getConnection method is no longer called, which is weird.

 <!-- <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-jetty</artifactId>
  </dependency> -->

Reproduce project address: example

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Mar 6, 2024
@wilkinsona wilkinsona self-assigned this Mar 6, 2024
@wilkinsona
Copy link
Member

wilkinsona commented Mar 6, 2024

This is quite similar to #38514 which the underlying cause being the same non-enumerable property source that comes from JNDI being available. You can work around the problem by adding a spring.properties file to src/main/resources that contains spring.jndi.ignore=true.

The failure's occurring because DruidDataSource isn't entirely compatible with configuration property binding.

When all property sources are enumerable, binding can be optimised to only try to bind properties that are present in the environment. For example, if there are no spring.datasource.log.connection.* properties in the environment, the binder will never need to call getConnection() on the DruidDataSource.

When there's a non-enumerable property source in the environment, the binder does not know all of the properties that are available so it has to try to bind everything. As part of trying to bind everything, the binder's calling getConnection() so that it can then introspect its result for setter methods and determine which properties need to be bound to it. This call fails as it's happening before the URL had been set. Even if the URL had been set, the call would still be unwanted as it would leak a connection.

Hopefully the workaround above is enough to get you past the problem. I'll leave this open for now as I'm not sure what, if anything, we can do to address it. One option would be to provide a way to indicate properties that should be ignored when binding to a third-party class. Another would be for you to bind properties to a safe class of your own and then use Boot's PropertyMapper to apply those to the DruidDataSource instance. Lastly, you could explore binding properties to Boot's DataSourceProperties class and then building the DataSource from those properties:

 properties.initializeDataSourceBuilder().type(DruidDataSource.class).build();

@wilkinsona wilkinsona removed their assignment Mar 6, 2024
@wilkinsona wilkinsona added for: team-attention An issue we'd like other members of the team to review for: team-meeting An issue we'd like to discuss as a team to make progress and removed for: team-attention An issue we'd like other members of the team to review labels Mar 6, 2024
@quaff
Copy link
Contributor

quaff commented Mar 6, 2024

I know it's risky, but can we ignore JndiPropertySource by default since it's vulnerable and not much useful in practice?

@quaff
Copy link
Contributor

quaff commented Mar 7, 2024

I requested Spring Framework team to disable JndiPropertySource by default. see spring-projects/spring-framework#18598 (comment)

@wilkinsona wilkinsona changed the title @ConfigurationProperties Abnormal behavior causes startup failure Configuration properties cannot be bound to DruidDataSource when the environment contains a non-enumerable property source Mar 7, 2024
@philwebb
Copy link
Member

We've opened #39932 to deal with the Jetty JNDI issue. Once that has been done this issue is effectively a duplicate of #34616

@philwebb philwebb closed this as not planned Won't fix, can't repro, duplicate, stale Mar 13, 2024
@philwebb philwebb added status: duplicate A duplicate of another issue and removed status: waiting-for-triage An issue we've not yet triaged for: team-meeting An issue we'd like to discuss as a team to make progress labels Mar 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: duplicate A duplicate of another issue
Projects
None yet
Development

No branches or pull requests

5 participants