0

I'm working on a library that is consumed by some other project. The library offers database access through JDBC and I'd like to add support for R2DBC too in the same library. The consuming project should be able to switch between JDBC and R2DBC based on a configuration property.

The problem that I'm facing is that the R2DBC auto-configuration provided by spring-boot-starter-data-r2dbc (2.5.4) overrides the JDBC configuration and the consuming project can only work with R2DBC.

Further more, when the project is being built, there are certain tasks like documentation or code generation, tests, etc, that depend on the spring context being loaded but do not require database access. These tasks fail because the context cannot be loaded due to the missing R2DBC properties:

BeanCreationException: Error creating bean with name 'connectionFactory' defined in class path resource [org/springframework/boot/autoconfigure/r2dbc/ConnectionFactoryConfigurations$Pool.class]: Bean instantiation via factory method failed;
 nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [io.r2dbc.pool.ConnectionPool]: Factory method 'connectionFactory' threw exception; 
 nested exception is org.springframework.boot.autoconfigure.r2dbc.ConnectionFactoryOptionsInitializer$ConnectionFactoryBeanCreationException: Failed to determine a suitable R2DBC Connection URL

Of course, I could specify the required properties, but it doesn't feel right to load components I don't use. I would like to disable R2DBC completely (much like you can disable Vault support with spring.cloud.vault.enabled=false) and only load it when I need it. Any ideas on how to do that?

Liviu
  • 118
  • 14
  • [Profiles](https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.profiles), bro! (for details, please post more..) – xerx593 Jan 10 '22 at 16:03
  • What does the library do? Because it sounds like https://jooq.org already does what you need... – Lukas Eder Jan 10 '22 at 16:07
  • `@ConditionalOnBean` may be of help in determining which (of your) beans to instantiate. – Kayaman Jan 10 '22 at 16:56
  • @xerx593 I cannot use profiles, the configurations that get loaded belong to spring (see R2dbcAutoConfiguration). – Liviu Jan 10 '22 at 17:38
  • @Kayaman, same thing as with the profiles proposed by xerx593... – Liviu Jan 10 '22 at 17:39
  • 1
    `@SpringBootApplication(exclude=R2dbcAutoConfiguration.class)` ...`@Profile("with")@Configuration@Import(R2dbcAutoConfiguration.class)`;) – xerx593 Jan 10 '22 at 17:39
  • @LukasEder the library offers basic CRUD operations while being agnostic of the entity structure, the entities are defined in the consuming projects. Switching to jooq is not feasible as it would mean altering 100+ projects that depend on this library. – Liviu Jan 10 '22 at 17:41
  • see [Disabling (specific) auto configuration](https://docs.spring.io/spring-boot/docs/current/reference/html/using.html#using.auto-configuration.disabling-specific) – xerx593 Jan 10 '22 at 17:42
  • 1
    @xerx593 I overlooked `@SpringBootApplication(exclude=R2dbcAutoConfiguration.class)` as I needed a configuration inside the library (the library also controls the gradle plugins that are used for building the projects). Reading the documentation I see that the exclusion can also be specified as a property: `spring.autoconfigure.exclude` - which looks promising. Thanks for the tip, I'll test it tomorrow at work and get back with the results. – Liviu Jan 10 '22 at 17:46

1 Answers1

1

My first attempt was to create a configuration class in the library:

@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(name = "r2dbc.enabled", havingValue = "false", matchIfMissing = true)
@AutoConfigureBefore(R2dbcAutoConfiguration.class)
@EnableAutoConfiguration(exclude = R2dbcAutoConfiguration.class)
public class R2dbcConfig {}

That did the trick for some of the tasks but caused problems in other. I'm posting it as it may be sufficient for some folks.

The solution that works in all my cases was a bit more involved, I had to implement an EnvironmentPostProcessor that adds org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration to the spring.autoconfigure.exclude property. Shortly, something like this:

public class R2dbcDisablerEnvironmentPostProcessor implements EnvironmentPostProcessor {

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        Properties props = new Properties();
        props.setProperty("spring.autoconfigure.exclude", "org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration");

        environment.getPropertySources()
                .addLast(new PropertiesPropertySource("r2dbcProperties", props));
    }
}

Thanks @xerx593 for the pointers!

Liviu
  • 118
  • 14