spring之如何在集成测试期间配置 Spring Boot 以包装 DataSource

linjiqin 阅读:70 2025-06-02 22:19:02 评论:0

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

我知道数据库连接是正确的,因为无论何时 MyDataSourceWrapperConfiguration 在测试运行期间都没有配置问题不包括在测试中。但是,一旦添加,就会发生循环依赖。 (见下面的错误)我相信 @Primary为了让 JPA/JDBC 代码使用正确的 DataSource 是必需的实例。
MyDataSourceWrapper是一个自定义类,它跟踪给定事务发生的查询数量,但它将真实的数据库工作委托(delegate)给 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 的。然而,接受的答案反射(reflect)了@rajadilipkolli 的答案适用于springboot 2.x

请您参考如下方法:

在您的情况下,您将获得 2 DataSource可能不是您想要的实例。而是使用 BeanPostProcessor这是实际为此设计的组件。另见Spring Reference Guide .

创建并注册 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
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

关注我们

一个IT知识分享的公众号