1. 概述
Spring Boot 采用了一套“约定优于配置”的机制来扫描并自动配置 DataSource。这使得我们能够轻松地获得一个默认配置完整的 DataSource 实现。
此外,Spring Boot 还会自动配置一个高性能的连接池,它会按照优先级依次尝试使用 HikariCP、Apache Tomcat 或 Commons DBCP,具体取决于类路径中存在哪个依赖。
✅ 虽然 Spring Boot 的自动 DataSource 配置在大多数情况下表现良好,但有时候我们需要更精细的控制,这时候就需要我们手动创建 DataSource 实现,从而跳过 Spring Boot 的自动配置流程。
本文将带你学习 如何在 Spring Boot 中以编程方式配置 DataSource。
2. Maven 依赖
以编程方式创建 DataSource 实现整体上是比较简单的。
为了演示这一点,我们将实现一个简单的 Repository 层,用于对一些 JPA 实体执行 CRUD 操作。
下面是示例项目的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.4.1</version>
<scope>runtime</scope>
</dependency>
如上所示,我们将使用一个内存中的 H2 数据库实例来测试 Repository 层。这样可以在不产生昂贵数据库操作开销的前提下,验证我们以编程方式配置的 DataSource 是否生效。
另外,请确保检查 Maven Central 上 spring-boot-starter-data-jpa 的最新版本。
3. 编程方式配置 DataSource
如果我们继续使用 Spring Boot 的自动 DataSource 配置并运行当前项目,它会按预期正常工作。
✅ Spring Boot 会自动完成所有底层基础设施的配置工作。这包括创建 H2 的 DataSource 实现,由 HikariCP、Apache Tomcat 或 Commons DBCP 自动处理,并初始化一个内存数据库实例。
此外,我们甚至不需要创建 application.properties 文件,因为 Spring Boot 会提供一些默认的数据库设置。
但正如前面提到的,有时我们需要更高级别的定制化,因此需要以编程方式配置自己的 DataSource 实现。
✅ 最简单的方式是定义一个 DataSource 的工厂方法,并将其放在一个标注了 @Configuration 注解的类中:
@Configuration
public class DataSourceConfig {
@Bean
public DataSource getDataSource() {
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.driverClassName("org.h2.Driver");
dataSourceBuilder.url("jdbc:h2:mem:test");
dataSourceBuilder.username("SA");
dataSourceBuilder.password("");
return dataSourceBuilder.build();
}
}
在这个例子中,我们使用了便捷的 DataSourceBuilder 类,它采用了 Joshua Bloch 的构建者模式(非流式风格),用于以编程方式创建自定义的 DataSource 对象。
这种方式非常友好,因为构建器可以让我们轻松地使用一些常见属性来配置 DataSource,并且底层也会自动使用连接池。
4. 使用 application.properties 外部化 DataSource 配置
当然,我们也可以部分地将 DataSource 配置外部化。例如,我们可以在工厂方法中定义一些基础的 DataSource 属性:
@Bean
public DataSource getDataSource() {
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.username("SA");
dataSourceBuilder.password("");
return dataSourceBuilder.build();
}
然后在 application.properties 文件中指定一些额外的配置:
spring.datasource.url=jdbc:h2:mem:test
spring.datasource.driver-class-name=org.h2.Driver
✅ 定义在外部源(如上面的 application.properties 文件)中的属性,或通过标注 @ConfigurationProperties 的类定义的属性,将会覆盖 Java API 中的配置。
⚠️ 这种方式的缺点是,我们的 DataSource 配置不再集中在一个地方。
✅ 但好处是,它可以将编译时和运行时的配置设置很好地分离,便于我们在不同环境下使用不同的配置,而无需修改 Java 配置代码。
5. 测试 DataSource 配置
测试我们自定义的 DataSource 配置非常简单。整个流程包括创建一个 JPA 实体、定义一个基本的 Repository 接口,并对 Repository 层进行测试。
5.1. 创建一个 JPA 实体
我们先定义一个示例 JPA 实体类,用于建模用户:
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
private String email;
// 标准构造函数 / setter / getter / toString 方法
}
5.2. 简单的 Repository 层
接下来我们需要实现一个基本的 Repository 层,用于对上面定义的 User 实体类执行 CRUD 操作。
由于我们使用了 Spring Data JPA,无需从头实现 DAO。只需扩展 CrudRepository 接口即可:
@Repository
public interface UserRepository extends CrudRepository<User, Long> {}
5.3. 测试 Repository 层
最后,我们需要验证我们以编程方式配置的 DataSource 是否正常工作。可以通过一个集成测试轻松完成:
@RunWith(SpringRunner.class)
@DataJpaTest
public class UserRepositoryIntegrationTest {
@Autowired
private UserRepository userRepository;
@Test
public void whenCalledSave_thenCorrectNumberOfUsers() {
userRepository.save(new User("Bob", "[email protected]"));
List<User> users = (List<User>) userRepository.findAll();
assertThat(users.size()).isEqualTo(1);
}
}
UserRepositoryIntegrationTest 类非常直观,它调用了 Repository 接口的两个 CRUD 方法来持久化和查找实体。
✅ **无论我们选择完全以编程方式配置 DataSource,还是将其拆分为 Java 配置方法和 application.properties,我们都应该始终获得一个可用的数据库连接**。
5.4. 运行示例应用
最后,我们可以使用标准的 main() 方法运行示例应用:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public CommandLineRunner run(UserRepository userRepository) throws Exception {
return (String[] args) -> {
User user1 = new User("John", "[email protected]");
User user2 = new User("Julie", "[email protected]");
userRepository.save(user1);
userRepository.save(user2);
userRepository.findAll().forEach(user -> System.out.println(user));
};
}
}
我们已经测试过 Repository 层,因此可以确定 DataSource 配置成功。运行示例应用后,应该在控制台输出中看到数据库中存储的 User 实体列表。
6. 总结
在本文中,我们学习了如何在 Spring Boot 中以编程方式配置 DataSource 实现。
一如既往,本文中所有代码示例都可以在 GitHub 上找到。