[spring-projects/spring-boot]提供一种禁用 DataSourceInitializerInvoker 的简单方法

2024-04-12 681 views
7

大家好!

我正在开发一个简单的 Spring Boot 组件,它将一些DataSourcebean 添加到应用程序上下文中。目前,我面临着 Spring Boot 尝试实现数据库初始化的方式的问题,即在第一个bean 初始化期间DataSourceInitializerInvoker尝试检索类型的主 bean 的 bean 。DataSourceDataSource

BeanCurrentlyInCreationException在我的例子中,这会导致@Primary DataSourceBean 依赖于其他DataSourceBean。依赖循环如下:“主DataSource”->“普通DataSource”->“DataSourceInitializerInvoker”->“主DataSource”。

可以在这里找到一个重现问题的简单项目 - https://github.com/galingerv/datasource-issue-re Producer

我想知道是否DataSourceInitializerInvoker可以关闭以避免这个问题,这对我来说是可以接受的。

回答

8

在 2.2.x 中修复循环问题可能很难,但如果能提供一种关闭初始化的简单方法就好了。

6

实际上@snicoll 只是提醒我我们已经拥有了一处房产。 @galingerv 你试过添加spring.datasource.initialization-mode=never到你的吗application.properties

1

@philwebb 感谢您的快速回复!是的,我尝试过这个选项,但没有成功,并且版本 2.2.6 的快速代码分析表明,在bean 检索 PrimaryDataSourceInitializer后立即创建的内部类实例中考虑了该属性。所以循环依赖仍然存在。即使我们可以直接在 中分析该属性,我也必须在我的自动配置中覆盖属性的默认值,这似乎也很棘手。DataSourceinitializerInvokerDataSourceDataSourceInitializerInvokerorg.springframework.boot.autoconfigure.jdbc.DataSourceProperties#initializationModeNEVER

7

@galingerv 感谢您的反馈,很抱歉我们给您发错了方向。我们当然应该改进它,以确保如果属性设置为 ,则 bean 根本不会注册never。我为此创建了#21363。

当前的安排是,无论 Spring Boot 是否配置了数据源,都会发生数据源初始化,我认为此时最合理的操作是排除DataSourceAutoConfiguration并最终应用一些您可能想要的东西(通常是 import on DataSourcePoolMetadataProvidersConfiguration)。

我必须在我的自动配置中将属性 org.springframework.boot.autoconfigure.jdbc.DataSourceProperties#initializationMode 的默认值覆盖为 NEVER,这似乎也很棘手。

恐怕拥有提供多个数据源的自动配置很棘手。这是 Spring Boot 需要改进的一个主题。您能尝试一下排除并报告吗?

9

@snicoll 排除DataSourceAutoConfiguration用户内应用程序肯定会帮助我的自动配置成功启动。但我希望开发一个启动器,无需在用户 SpringBoot 应用程序中排除某些自动配置。

0

我理解这一点,这就是我所说的“恐怕拥有提供多个数据源的自动配置很棘手。”

问题是您试图在自动配置中做一些我们不支持的更“核心”的事情。恐怕目前我没有其他建议可以给你。

即使我们提供了一种轻松禁用它的方法,您仍然必须覆盖 Spring Boot 的默认行为,这在初学者中确实很不寻常,所以我不确定您到底在问我们什么。

4

你好!如果我正确理解 Spring Boot 的概念,几乎所有“开箱即用”的东西都应该是可重写或可定制的。 Spring Boot 开箱即用,可以定义单个DataSourcebean,所以我真的不明白DataSource在自动配置中定义一些额外的 bean 会出现什么问题。我相信,定义额外的 bean 是大多数自动配置实际上所做的事情。您能澄清一下我对概念的理解吗?

9

你是对的。然而,您刚才描述的并不是您面临的问题。我认为你正在自动配置(多个数据源)中做一些目前 Spring Boot 不支持的事情。我的建议是禁用自动配置,但您不愿意这样做。

如果您希望数据源初始化在不禁用初始化模式或不排除自动配置的情况下后退,目前没有计划提供此功能,但如果您有想法,我很乐意重新访问。

2

顺便说一句,我找到了一种禁用 的方法DataSourceInitializerPostProcessor,但相当黑客且不安全。从 2.2.x 开始,此类阻止DataSourceInitializerPostProcessor被注册为BeanPostProcessor

static class Registrar implements ImportBeanDefinitionRegistrar {

        private static final String BEAN_NAME = "dataSourceInitializerPostProcessor";

        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
                                            BeanDefinitionRegistry registry) {
            if (!registry.containsBeanDefinition(BEAN_NAME)) {
                GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
                beanDefinition.setBeanClass(Object.class);
                beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
                beanDefinition.setSynthetic(true);
                registry.registerBeanDefinition(BEAN_NAME, beanDefinition);
            } else {
                registry.removeBeanDefinition(BEAN_NAME);
            }
        }
    }

将其注册到我的自动配置中后ImportBeanDefinitionRegistrar,就不会出现依赖循环。

3

@snicoll 你能解释一下为什么你认为 SpringBoot 自动配置不支持多个数据源吗?我可以想象一个应用程序,其中DataSource由启动器自动配置的多个bean已成功启动,并且可以在应用程序中和另一个自动配置中使用,所以我无法理解不支持状态的原因是什么。

8

这不是我写的吧?我提到 Spring Boot 本身不会自动配置多个数据源,我们只是不支持这一点。在应用程序中执行此操作和在自动配置中执行此操作是完全不同的事情。

回到我的问题,如果您暂时不想排除自动配置或设置属性,我们将不得不基于示例进行调查,因为我们在这里没有取得很大进展。

请分享一个我们可以自己运行来重现问题的示例应用程序。

7

以下是演示自动配置的示例: https://github.com/galingerv/datasource-issue-re Producer /tree/using -autoconfiguration 自动配置:com.example.datasources.autoconfiguration.MultipleDatasourcesAutoConfiguration 使用它的应用程序:com.example.demo.DemoApplication

0

@snicoll 你有机会查看示例吗?我想知道这里是否有一些东西需要我们考虑作为 2.5.x 数据源主题的一部分。

1

@wilkinsona 我可能会,但我不记得了。我同意它应该被视为我们数据源主题的一部分。

3

谢谢。我现在已将其分配给 2.5.x 作为增强功能。我们可以随着主题的进展根据需要进行调整。

3

2.5.0-M2 中对数据源初始化的更改已修复此问题。该示例在 2.5.0-M1 上失败:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::             (v2.5.0-M1)

2021-03-09 15:00:37.772  INFO 84880 --- [           main] com.example.demo.DemoApplication         : Starting DemoApplication using Java 1.8.0_252 on wilkinsona-a01.vmware.com with PID 84880 (/Users/awilkinson/dev/temp/datasource-issue-reproducer/target/classes started by awilkinson in /Users/awilkinson/dev/temp/datasource-issue-reproducer)
2021-03-09 15:00:37.773  INFO 84880 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
2021-03-09 15:00:38.138  WARN 84880 --- [           main] s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'dataSource' defined in com.example.demo.DemoApplication: Unsatisfied dependency expressed through method 'dataSource' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'firstDataSource' defined in com.example.demo.DemoApplication: Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoker': Invocation of init method failed; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'dataSource': Requested bean is currently in creation: Is there an unresolvable circular reference?
2021-03-09 15:00:38.144  INFO 84880 --- [           main] ConditionEvaluationReportLoggingListener : 

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2021-03-09 15:00:38.150 ERROR 84880 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   : 

***************************
APPLICATION FAILED TO START
***************************

Description:

The dependencies of some of the beans in the application context form a cycle:

┌─────┐
|  dataSource defined in com.example.demo.DemoApplication
↑     ↓
|  firstDataSource defined in com.example.demo.DemoApplication
↑     ↓
|  org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoker
└─────┘

由于以下更改,它可以在 2.5.0-M2 上成功启动:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::             (v2.5.0-M2)

2021-03-09 14:59:21.583  INFO 84719 --- [           main] com.example.demo.DemoApplication         : Starting DemoApplication using Java 1.8.0_252 on wilkinsona-a01.vmware.com with PID 84719 (/Users/awilkinson/dev/temp/datasource-issue-reproducer/target/classes started by awilkinson in /Users/awilkinson/dev/temp/datasource-issue-reproducer)
2021-03-09 14:59:21.584  INFO 84719 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
2021-03-09 14:59:21.986  INFO 84719 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 0.587 seconds (JVM running for 0.824)