news 2026/3/16 17:05:56

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/3/15 21:37:45

终极GUI自动化指南:UI-TARS如何彻底改变人机交互模式

在当今数字化时代&#xff0c;图形用户界面&#xff08;GUI&#xff09;已成为我们与计算机交互的主要方式。然而&#xff0c;传统的自动化工具在面对日益复杂的界面时显得力不从心。UI-TARS作为字节跳动最新开源的单一体视觉语言模型&#xff0c;正在重新定义GUI自动化的未来。…

作者头像 李华
网站建设 2026/3/15 15:07:56

ERNIE 4.5:3000亿参数MoE模型如何重塑企业AI效率边界

ERNIE 4.5&#xff1a;3000亿参数MoE模型如何重塑企业AI效率边界 【免费下载链接】ERNIE-4.5-300B-A47B-W4A8C8-TP4-Paddle 项目地址: https://ai.gitcode.com/hf_mirrors/baidu/ERNIE-4.5-300B-A47B-W4A8C8-TP4-Paddle 导语 百度ERNIE 4.5系列大模型以异构混合专家架…

作者头像 李华
网站建设 2026/3/16 4:21:08

物理信息神经网络的终极指南:5个免费工具快速入门科学计算新范式

物理信息神经网络&#xff08;PINN&#xff09;正在彻底改变科学计算的格局&#xff0c;这种融合物理学原理与深度学习的新方法让复杂的微分方程求解变得前所未有的简单。作为新手&#xff0c;你可能还在为传统的数值方法头疼不已&#xff0c;但现在有了PINNpapers这个完整资源…

作者头像 李华
网站建设 2026/3/14 10:59:24

用ComfyUI打造专属AI滤镜:定制化图像风格生成方案

用ComfyUI打造专属AI滤镜&#xff1a;定制化图像风格生成方案 在广告公司做视觉设计的第三年&#xff0c;我终于不再被“上次那个色调怎么调的&#xff1f;”这种问题困扰了。过去每次客户说“就那种感觉&#xff0c;但再明亮一点”&#xff0c;团队就得翻聊天记录、试十几组参…

作者头像 李华
网站建设 2026/3/15 8:56:44

免费PCB设计查看神器:Altium文件浏览器完整使用指南

免费PCB设计查看神器&#xff1a;Altium文件浏览器完整使用指南 【免费下载链接】AltiumDesignerViewer Altium Designer Viewer是一款高效且易于使用的查看工具&#xff0c;专为设计工程师和团队成员打造&#xff0c;旨在无需进行任何注册或激活的情况下&#xff0c;轻松浏览和…

作者头像 李华
网站建设 2026/3/14 9:41:55

ImageSharp色彩变换:揭秘数字图像调色的数学魔法

ImageSharp色彩变换&#xff1a;揭秘数字图像调色的数学魔法 【免费下载链接】ImageSharp :camera: A modern, cross-platform, 2D Graphics library for .NET 项目地址: https://gitcode.com/gh_mirrors/im/ImageSharp 你是否曾好奇&#xff0c;那些令人惊艳的滤镜效果…

作者头像 李华