news 2026/8/8 5:27:43

SpringBoot高级进阶

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringBoot高级进阶

数据源自动配置剖析

数据源配置方式

首先我们需要选择数据库驱动的库文件

<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency>

配置数据库连接

spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3307/springboot username: root password: 123456

配置spring-boot-start-jdbc

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency>

编写测试类

@Autowired private DataSource dataSource; @Test public void contextLoads()throws SQLException{ Connection connection =dataSource.getConnection(); System.out.println(connection); }

连接池配置方式

SpringBoot提供了三种连接池:

  • HikariCP
  • Commons DBCP2
  • Tomcat JDBC Connection Pool

其中SpringBoot默认使用HikariCP

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency>

要使用其他两个要改为

<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-dbcp2</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> <exclusions> <exclusion> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> </exclusion> </exclusions> </dependency>
<dependency> <groupId>org.apache.tomcat</groupId> <artifactId>tomcat-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> <exclusions> <exclusion> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> </exclusion> </exclusions> </dependency>

数据库自动配置

为什么前面我们说SpringBoot默认使用HikariCP?这是在哪里指定的?

首先我们需要找到DataSourceAutoConfiguration这个类

@Conditional(PooledDataSourceCondition.class)这个告诉我们指定的配置文件中,必须要有type属性。

现在我们进入DataSourceConfiguration这个里面

里面配置类SpringBoot启动的连接池。

private static final String[] DATA_SOURCE_TYPE_NAMES = new String[] {"com.zaxxer.hikari.HikariDataSource", "org.apache.tomcat.jdbc.pool.DataSource", "org.apache.commons.dbcp2.BasicDataSource"};

所以我们可以看出来在没有指定Type时,默认就是HikariDataSource。

Druid连接池的配置

首先么要整合druid数据源

<dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency>

在application.yml中引入druid的相关配置

spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3307/springboot username: root password: 123456 type: com.alibaba.druid.pool.DruidDataSource

整合的配置类

public class DruidConfig { @ConfigurationProperties(prefix = "spring.datasource") @Bean public DataSource druidDataSource(){ return new DruidDataSource(); } }

SpringBoot整合Mybatis

首先我们要添加maven依赖

<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency>

所以我们需要打开MybatisAutoConfiguration这个类

Mybatis自动配置源码剖析

  1. springBoot项目最核心的就是自动加载配置,该功能依赖的是一个注解@SpringBootApplication中的@EnableAutoConfiguration
  2. @EnableAutoConfiguration主要是通过@Import(AutoConfigurationImportSelector.class)类来加载的;
  3. MybatisAutoConfiguration
    1. 其中有个MybatisProperties,该类是对应mybatis的配置文件
    2. 有个SqlSessionFactory方法,作用是创建SqlSession类,Configuration类(mybatis最主要的类,保存着与mybatis相关的东西)
    3. SqlSessionTemplate,作用是mapperProoxy代理类有关

然后我们要看MapperScan注解

通过进一步分析源码我们可以得出以下结论

@MapperScan这个定义,是扫描指定包下面的mapper接口,然后设置每个mapper接口的beanClass属性为MapperFactoryBean类型并加入到spring的bean容器中。

MapperFactoryBean实现了FactoryBean接口,所以当spring从待实例化的bean容器中变量到这个bean并开始执行实例化使返回的对象实际上就是getObject方法中返回的对象。

最后我们看一下MapperFactoryBean的getObject方法,实际上返回的就是mybatis中通过getMapper拿到的对象,熟悉mybatis源码的就应该清楚,这个就是mybatis通过动态代理生成的mapper接口实现类。

到此,mapper接口现在也通过动态代理生成了实现类,并且注入到spring的bean容器中了,之后使用者就可以通过@Autowired或者getBean等方法,从spring容器中获取到了。

Spring+Mybatis实现动态数据源切换

动态数据源介绍

现在我们的项目中订单模块氛围正向和逆向两个部分;

  • 正向模块中记录了订单的基本信息,包括订单基本信息,订单商品信息,优惠卷信息,发票信息,账期信息,结算信息,订单备注信息,收货人信息等;
  • 逆向模块主要包含了商品退货信息和维修信息;
  • 当我们数据量超过500万时就需要考虑分库分表和读写分类,需要动态切换到对应的数据库中。

解决思路

Spring内置了一个AbstractRoutingDataSource,它可以把多个数据源配置为一个Map,然后根据不同的key返回不同的数据源。

编码实战

首先我们需要准备基础的环境

@Data public class Product { private Integer id ; private String name; private Double price; }
@Mapper public interface ProductMapper { //查询主数据库 @Select("select * from product") public List<Product> getAllProductsM(); //查询从数据库 @Select("select * from product") public List<Product> getAllProductsS(); }
@Service public class ProductServiceImpl implements ProductService { @Autowired private ProductMapper productMapper; @Override public void getAllProductsM() { List<Product> products = productMapper.getAllProductsM(); System.out.println("MyBatis查询结果:"+products); } @Override public void getAllProductsS() { List<Product> products = productMapper.getAllProductsS(); System.out.println("MyBatis查询结果:"+products); } }
@RestController @RequestMapping("/product") public class ProductController { @Autowired private ProductService productService; @GetMapping("/getAllProductsM") public String getAllProductsM() { productService.getAllProductsM(); return "master"; } @GetMapping("/getAllProductsS") public String getAllProductsS() { productService.getAllProductsS(); return "slave"; } }

配置类

@Configuration @Slf4j public class MyDataSourceConfiguration { @Bean(name = "dataSourceM") DataSource dataSourceM(){ log.info("主数据库"); return DataSourceBuilder.create().build(); } @Bean(name = "dataSourceS") DataSource dataSourceS(){ log.info("从数据库"); return DataSourceBuilder.create().build(); } }

配置文件

spring: druid: datasource: master: url: jdbc:mysql://localhost:3307/product_m username: root password: 123456 type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver slave: url: jdbc:mysql://localhost:3307/product_s username: root password: 123456 type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver

其次需要编写RoutingDataSource,把两个真实的数据源代理为一个动态数据源:

@Slf4j public class RoutingDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return "master数据库"; } @Bean @Primary DataSource primaryDataSource(@Autowired @Qualifier("dataSourceM")DataSource dataSourceM, @Autowired @Qualifier("dataSourceS")DataSource dataSourceS){ log.info("正在动态创建数据库"); Map<Object, Object> map = new HashMap<>(); map.put("master数据库", dataSourceM); map.put("slave数据库", dataSourceS); RoutingDataSource routingDataSource = new RoutingDataSource(); routingDataSource.setTargetDataSources(map); routingDataSource.setDefaultTargetDataSource(dataSourceM); return routingDataSource; } }

现在RoutingDataSource配置好了,但是路由的选择是写死的,永远返回master数据库。

现在问题来了,我们该如何存储动态选择的key已经在哪里设置key?

使用ThreadLocal存储key最合适。

public class RoutingDataSourceContext { static final ThreadLocal<String> threadLocalDataSourceKey = new ThreadLocal<>(); public static String getDataSourceKey() { String key = threadLocalDataSourceKey.get(); return key == null ? "master数据库" : key; } public RoutingDataSourceContext(String key) { threadLocalDataSourceKey.set(key); } public void close() { threadLocalDataSourceKey.remove(); } }

现在我们修改RoutingDataSource,获取key的代码

@Override protected Object determineCurrentLookupKey() { return RoutingDataSourceContext.getDataSourceKey() ; }
@RestController @RequestMapping("/product") public class ProductController { @Autowired private ProductService productService; @GetMapping("/getAllProductsM") public String getAllProductsM() { RoutingDataSourceContext routingDataSourceContext = new RoutingDataSourceContext("master数据库"); productService.getAllProductsM(); return "master"; } @GetMapping("/getAllProductsS") public String getAllProductsS() { RoutingDataSourceContext routingDataSourceContext = new RoutingDataSourceContext("salve数据库"); productService.getAllProductsS(); return "slave"; } }

测试结果

2025-12-12 14:07:48.541 [http-nio-8080-exec-3] DEBUG com.guslegend.mapper.ProductMapper.getAllProductsS - ==> Preparing: select * from product 2025-12-12 14:07:48.541 [http-nio-8080-exec-3] DEBUG com.guslegend.mapper.ProductMapper.getAllProductsS - ==> Parameters: 2025-12-12 14:07:48.543 [http-nio-8080-exec-3] DEBUG com.guslegend.mapper.ProductMapper.getAllProductsS - <== Total: 1 MyBatis查询结果:[Product(id=1, name=桌子, price=100.0)] 2025-12-12 14:07:50.442 [http-nio-8080-exec-4] DEBUG com.guslegend.mapper.ProductMapper.getAllProductsM - ==> Preparing: select * from product 2025-12-12 14:07:50.442 [http-nio-8080-exec-4] DEBUG com.guslegend.mapper.ProductMapper.getAllProductsM - ==> Parameters: 2025-12-12 14:07:50.443 [http-nio-8080-exec-4] DEBUG com.guslegend.mapper.ProductMapper.getAllProductsM - <== Total: 1 MyBatis查询结果:[Product(id=1, name=桌子, price=100.0)]

优化

上面需要读取数据库的地方就要加上这样的一段话,是否太过于复杂了?

RoutingDataSourceContext routingDataSourceContext = new RoutingDataSourceContext();

我们可以使用声明式事务管理,或者自定义注解来解决

首先我们要添加aop的依赖

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>

自定义注解

@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RoutingWith { String value() default "master"; }

切面类

@Aspect @Component public class RoutingAspect { @Around("@annotation(routingWith)") public Object routingWithDataSource(ProceedingJoinPoint proceedingJoinPoint,RoutingWith routingWith)throws Throwable{ String key =routingWith.value(); RoutingDataSourceContext routingDataSourceContext = new RoutingDataSourceContext(key); return proceedingJoinPoint.proceed(); } }

改造controller方法

@RestController @RequestMapping("/product") public class ProductController { @Autowired private ProductService productService; @RoutingWith("master") @GetMapping("/getAllProductsM") public String getAllProductsM() { // RoutingDataSourceContext routingDataSourceContext = new RoutingDataSourceContext("master数据库"); productService.getAllProductsM(); return "master"; } @RoutingWith("slave") @GetMapping("/getAllProductsS") public String getAllProductsS() { // RoutingDataSourceContext routingDataSourceContext = new RoutingDataSourceContext("salve数据库"); productService.getAllProductsS(); return "slave"; } }

至此,我们实现了注解动态选择数据源功能。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/8 5:27:28

7步精通Material-UI:构建专业级海洋数据可视化平台的完整教程

7步精通Material-UI&#xff1a;构建专业级海洋数据可视化平台的完整教程 【免费下载链接】material-ui mui/material-ui: 是一个基于 React 的 UI 组件库&#xff0c;它没有使用数据库。适合用于 React 应用程序的开发&#xff0c;特别是对于需要使用 React 组件库的场景。特点…

作者头像 李华
网站建设 2026/8/7 22:07:40

Windows API钩子深度解析:MinHook实战性能对比指南

Windows API钩子深度解析&#xff1a;MinHook实战性能对比指南 【免费下载链接】minhook The Minimalistic x86/x64 API Hooking Library for Windows 项目地址: https://gitcode.com/gh_mirrors/mi/minhook 项目核心价值 MinHook作为Windows平台上最轻量级的API钩子库…

作者头像 李华
网站建设 2026/8/7 14:03:53

Linux 内核中常见地址的设计原理及其API使用

在Linux内核中&#xff0c;地址类型和它们的正确使用是理解内核内存管理的关键。本文将详细解释各种地址类型及其用法。 一、Linux内核中的主要地址类型 物理地址 (Physical Address)phys_addr_t // 平台无关的物理地址类型 定义&#xff1a;CPU总线上的实际内存地址&#xff…

作者头像 李华
网站建设 2026/8/7 6:54:23

养老院信息|基于springboot + vue养老院信息管理系统(源码+数据库+文档)

养老院信息 目录 基于springboot vue养老院信息系统 一、前言 二、系统功能演示 三、技术选型 四、其他项目参考 五、代码参考 六、测试参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 基于springboot vue养老院信息系统 一、前言 博主介绍&#xff1a…

作者头像 李华
网站建设 2026/8/7 15:08:59

优化业务流程的营销智脑创新案例

本文围绕营销智脑在优化业务流程中的应用展开&#xff0c;阐释了其如何通过技术整合实现智能化的企业管理。营销智脑通过数据分析与客户服务的互动&#xff0c;为企业提供实时的数据支持&#xff0c;使决策更加科学化。在众多案例中&#xff0c;不同企业利用这一工具&#xff0…

作者头像 李华
网站建设 2026/8/7 3:11:24

企业级快速开发平台ruoyi-vue-pro:如何用30天完成传统3-6个月的项目

在数字化转型浪潮中&#xff0c;企业面临的最大挑战不是技术本身&#xff0c;而是如何在有限时间和预算内构建稳定、可扩展的业务系统。传统开发模式下&#xff0c;一个完整的企业级系统需要3-6个月才能上线&#xff0c;而基于ruoyi-vue-pro平台&#xff0c;这一周期可缩短至30…

作者头像 李华