如何在集成测试期间配置Springboot以包装数据源



我的目标是进行一个集成测试,以确保在查找过程中不会发生太多数据库查询。(这有助于我们由于不正确的JPA配置而捕获n 1个查询(

我知道数据库连接是正确的,因为每当MyDataSourceWrapperConfiguration不包括在测试中时,在测试运行期间都没有配置问题。但是,一旦添加,循环依赖性就会发生。(请参阅下面的错误(我认为@Primary是必要的,以使JPA/JDBC代码使用正确的DataSource实例。

MyDataSourceWrapper是一个自定义类,可跟踪给定事务发生的查询数量,但它将实际数据库工作委派给了通过构造函数传递的DataSource

错误:

The dependencies of some of the beans in the application context form a cycle:
   org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
┌─────┐
|  databaseQueryCounterProxyDataSource defined in me.testsupport.database.MyDataSourceWrapperConfiguration 
↑     ↓
|  dataSource defined in org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Tomcat
↑     ↓
|  dataSourceInitializer
└─────┘

我的配置:

@Configuration
public class MyDataSourceWrapperConfiguration {
    @Primary
    @Bean
    DataSource databaseQueryCounterProxyDataSource(final DataSource delegate) {
        return MyDataSourceWrapper(delegate);
    }
}

我的测试:

@ActiveProfiles({ "it" })
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration({ DatabaseConnectionConfiguration.class, DatabaseQueryCounterConfiguration.class })
@EnableAutoConfiguration
public class EngApplicationRepositoryIT {
    @Rule
    public MyDatabaseQueryCounter databaseQueryCounter = new MyDatabaseQueryCounter ();
    @Rule
    public ErrorCollector errorCollector = new ErrorCollector();
    @Autowired
    MyRepository repository;
    @Test
    public void test() {
        this.repository.loadData();
        this.errorCollector.checkThat(this.databaseQueryCounter.getSelectCounts(), is(lessThan(10)));
    }
}

更新:这个原始问题是针对Springboot 1.5的。接受的答案反映出,@rajadilipkolli的答案适用于Springboot 2.x

在您的情况下,您将获得2个DataSource实例,这可能不是您想要的。而是使用BeanPostProcessor,它是为此实际设计的组件。另请参阅春季参考指南。

创建并注册进行包装的BeanPostProcessor

public class DataSourceWrapper implements BeanPostProcessor {
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        if (bean instanceof DataSource) {
             return new MyDataSourceWrapper((DataSource)bean);
        }
        return bean;
    }
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

然后将其注册为@Bean而不是您的MyDataSourceWrapper

提示:,而不是滚动您自己的包装DataSource,您可能对DataSource-Proxy感兴趣,并结合了具有计数器等的DataSource-Assert。

从Spring Boot 2.0.0.m3使用BeanPostProcessor无法工作。

作为围绕自己的豆类创建自己的豆子,如下

  @Bean
    public DataSource customDataSource(DataSourceProperties properties) {
        log.info("Inside Proxy Creation");
        final HikariDataSource dataSource = (HikariDataSource) properties
                .initializeDataSourceBuilder().type(HikariDataSource.class).build();
        if (properties.getName() != null) {
            dataSource.setPoolName(properties.getName());
        }
        return ProxyDataSourceBuilder.create(dataSource).countQuery().name("MyDS")
                .logSlowQueryToSysOut(1, TimeUnit.MINUTES).build();
    }

另一种方法是使用dataSource-proxy版本的数据源启动器

以下解决方案使用Spring Boot 2.0.6。

它使用显式绑定而不是注释@ConfigurationProperties(prefix = "spring.datasource.hikari")

@Configuration
public class DataSourceConfig {
    private final Environment env;
    @Autowired
    public DataSourceConfig(Environment env) {
        this.env = env;
    }
    @Primary
    @Bean
    public MyDataSourceWrapper primaryDataSource(DataSourceProperties properties) {
        DataSource dataSource = properties.initializeDataSourceBuilder().build();
        Binder binder = Binder.get(env);
        binder.bind("spring.datasource.hikari", Bindable.ofInstance(dataSource).withExistingValue(dataSource));
        return new MyDataSourceWrapper(dataSource);
    }
}

您实际上仍然可以在Spring Boot 2中使用BeanPostProcessor,但需要返回正确的类型(已声明的Bean的实际类型(。为此,您需要创建一个正确类型的代理,该类型将数据源方法重定向到拦截器以及所有其他方法。

示例代码请参阅https://github.com/spring-projects/spring-boot/issues/12592。

最新更新