0.序言
很荣幸能够参与到 2024年博客之星评选活动。在此,请允许我简单地介绍一下自己:
我的成长历程
我是 2025届 的一名计算机专业学生,从小学到高中,我一直是那个努力学习、听话的孩子。周围的人都在埋头苦读,似乎学习和考试就是我们人生的全部意义。
进入大学后,我依然保持着高中的学习状态。即使室友们在深夜 12:30 熄灯,我也坚持早起学习,坐在课堂的第一排,每一堂课都全神贯注。大一时,我的专业成绩排名 第四,连续两年获得 奖学金。虽然在外人眼中,我是一个“出类拔萃”的学生,但内心深处的迷茫和压力始终伴随着我。
某一天,我开始思考:人生的意义究竟是什么? 难道就是不停地学习和考试吗?于是,我开始阅读各类哲学书籍,试图从中找到人生的答案。《幸福之路》、 《苏菲的世界》、《苏格拉底的申辩》…… 这些书让我渐渐意识到,人生并不只是学习和考试,更重要的是找到属于自己的方向和热爱。那时,加入了鱼皮的知识星球,看到许多伙伴分享 Java学习心得,心中悄然燃起了对编程的渴望。从那一刻起,成为 Java后端工程师成为了我的第一个梦想。
于是,我开始了漫长的自学之旅。从 韩顺平老师的600小时Java课程 到手写 Tomcat、Servlet 和 Spring、SpringMVC 、 MyBatis 的底层实现,我每天学习 8到11小时,甚至连春节都只休息了一天。用八个月时间,完成了正常一年才能学完的内容。通过这段时间的努力,我掌握了完整的 Java体系,并且反复研究 设计模式,通过视频、书籍和总结的方式学了四遍。这一年的时间,我的语雀文档笔记已经到达了一百万字。
不仅如此,我还跟随鸡翅老哥的 Club项目,完成了 八万行后端代码,完成了五期的全部内容,并独自将项目部署到 8台服务器 上。看着群里连做这个项目到处问问题的人都进了字节,而我却能独自解决所有问题,我就更有信心了,也让我无比坚定了自己的选择。
为了秋招,我根据简历整理出了 八万字的面试题,每天按计划背诵,虽然枯燥无味,但我依然坚持了下来。
遇到挫折,陷入低谷
然而,秋招的经历让我第一次真正体会到现实的压力。第一次大厂面试,我因为算法准备的不充分而被刷掉,面试官的一句话更让我深受打击:“你们这些应届生项目我看不上,很多人连项目都没有,你基础太差了。”这一刻,我感受到前所未有的打击,难道只有算法才算基础?我辛辛苦苦做的 Java底层 和 项目经验 就不值一提吗?
在秋招的最后,我获得了一个小厂自研的工作。然后因为正在等待中厂的意向,我选择去小厂试一试,在 10月末,中厂发了意向,考虑到这个公司在网上风评不错,并且在这个小厂感觉每天技术都在退步,只是做一些业务的处理,完全用不到我学的那么多技术,并且小厂在一个月试用期后离职就要付公司一个月的培训费用,就先离职了。
然而,11月末,中厂的意向突然被毁掉,那一刻的巨大落差让我瞬间陷入了迷茫。那些曾经不眠不休、全力以赴的日子,似乎在一瞬间都变得毫无意义。这种挫败感让我深陷痛苦,彻夜难眠,反复思考:我究竟错在哪里?
从小到大,我几乎没有真正休息过。大三时,我更是将全部的精力都投入到 Java 学习中。别人在午休时,我在埋头钻研;别人在享受娱乐、玩游戏时,我却依然坚持学习。我几乎舍弃了生活中的一切,只为了让自己在未来能够更加出色。那段时间,我用辛勤的努力为自己构筑了一座信念之塔,然而却在这一刻轰然倒塌。
重拾信心,迎接未来
失意的阴影笼罩着我,质疑与痛苦一度让我无法自拔。过去的每一份坚持与牺牲,都像是一场徒劳的幻梦,让人怀疑努力的意义。然而,也正是在这一刻,我开始意识到——或许,这只是前行路上的一块绊脚石,而不是终点。我必须学会站起来,重新出发。
经历了这段低谷期,我重新审视自己,发现其实我并没有尽全力。如果每天 8到11小时 不够,那就努力把时间延长到 12小时;如果算法不行,那就每天刷 10道算法题;如果我的项目不够好,那就自研框架并反复优化。
虽然在小厂没有待多久,而且离职资料完全被删除,但是因为当时用小厂自研的框架写过几个接口,即使没有看过具体框架的代码,我也凭借着扎实的 Java基础,以及 设计模式 的基础,用了几天时间经过逐步的思考、优化,最终的实现效果竟然与原框架不谋而合。
通过参与企业项目,我了解了一些企业开发中的痛点,并不断优化和增加框架功能,这些经验不仅丰富了我的技术积累,也让我更加坚定了继续前行的决心。
开源与分享
加入 CSDN 后,我秉承 开源精神,并专注于技术分享与输出。在半年多的时间里,发布275篇高质量文章,平均质量分90分,得到了12000多粉丝的关注,铁粉数量900多。
此次,我决定将自己半年多来精心编写的框架 Sunrays-Framework 开源到 GitCode,并在CSDN附上详细的文档和教程,希望能帮助更多正在学习的朋友,也希望我的经验能够激励更多人相信,只要 坚持不懈,终将迎来希望。
我为何如此看重这次评选
这次评选不仅仅是对我过去半年努力的肯定,更是我 春招路上最后的底气。作为一名即将毕业的学生,在 Java行业竞争异常激烈的现状 下,这可能是我最后一次证明自己实力的机会。对于我而言,这不仅仅是一次比赛,而是关乎未来职业发展的一次重大转折点。
但我的目标并不仅仅是为自己争取一次机会,更希望借此评选让更多人关注 Sunrays-Framework。这个框架凝聚了我半年多的心血,它不仅是我技术能力的体现,更是我对开源精神的理解与实践。我希望它能帮助更多开发者,无论是用来 学习 还是作为 毕业设计,都能成为大家提升技能和效率的得力助手。
我并不奢求任何回报,只希望在这次评选中,能够获得你们宝贵的一票!每一票,都是对我努力的认可,对开源精神的支持。这不仅是对过去半年辛苦付出的最好褒奖,更是帮助我在 春招最后关头 再拼一次的机会。
最后的心声
我深知 Java行业 的竞争有多么激烈,尤其是对应届毕业生而言。每一份工作机会都来之不易,而这次评选,可能是我 改变命运的最后机会。无论最终成绩如何,我都会继续完善框架,不断优化,只为帮助更多像我一样热爱技术、追求成长的年轻人!
如果你也曾在学习编程时迷茫过,曾在技术深坑中挣扎过,我真诚希望我的经历和这个框架,能够为你带来一丝启发和帮助。你的每一票,都是对我的最大支持和鼓励!
衷心感谢每一位愿意投票的朋友。你们的支持,不仅仅是对我努力的肯定,更是推动我不断前行的动力!让我们共同期待 Sunrays-Framework 帮助更多开发者取得进步,焕发更大的光彩!
1.概述
Sunrays-Framework 是一款基于 Spring Boot 构建的高效微服务开发框架,深度融合了 Spring Cloud 生态中的核心技术组件,涵盖了以下关键功能:
- MyBatis-Plus:简化数据访问层的开发。
- Minio:提供稳定、高效的分布式文件存储支持。
- Redis:实现缓存、分布式锁等高性能存储功能。
- RabbitMQ:可靠的消息队列支持,适用于异步任务和消息通知。
- Log4j2:提供灵活、性能卓越的日志管理。
- Nacos:负责服务发现与配置管理,确保系统动态可扩展。
- Spring Cloud Gateway:高性能的 API 网关,支持路由与负载均衡。
- OpenFeign:声明式 HTTP 客户端,简化服务间通信。
- OpenAI:为智能化应用提供接入支持。
- Mail:内置邮件服务功能,支持多场景通知需求。
- 微信支付与登录:完整集成微信支付功能和微信授权登录,提升用户体验。
框架注重 高效性、可扩展性和易维护性,为开发者提供开箱即用的解决方案,极大地简化了微服务架构的搭建过程。无论是构建企业级分布式系统还是完成毕设项目,Sunrays-Framework 都能以其强大的模块化设计与全面的技术支持,帮助开发者快速实现目标、专注于业务逻辑的创新与优化。
1.主要功能
本人在此承诺:只要我做Java一天,框架就会不断的升级!
永久开源,永久免费!
2.相关链接
- CSDN:Sunrays-Framework 开发笔记
- GitCode:Sunrays-Framework 开源地址
- 官方文档:Sunrays-Framework 官方文档 必读部分提供了七万字的完整语雀开发文档
2.系统要求
为确保系统正常运行,以下是本项目所需的技术栈及版本要求。
构建工具
工具 | 版本 |
---|---|
Maven | 3.6.3 或更高版本 |
框架和语言
技术 | 版本 |
---|---|
Spring Boot | 2.4.2 |
Spring Cloud | 2020.0.1 |
Spring Cloud Alibaba | 2021.1 |
JDK | 1.8 |
数据库与缓存
技术 | 版本 |
---|---|
MySQL | 5.7 |
Redis | 6.2.6 |
消息队列与对象存储
技术 | 版本 |
---|---|
RabbitMQ | 3.8.8 |
MinIO | 2024-12-19 |
3.快速入门
0.配置Maven中央仓库
1.打开settings.xml
2.不要配置阿里云,切换为Maven中央仓库,否则下不了依赖
<mirrors>
<mirror>
<id>central</id>
<mirrorOf>central</mirrorOf> <!-- 直接指定中央仓库 -->
<name>Maven Central</name>
<url>https://repo.maven.apache.org/maven2</url>
</mirror>
</mirrors>
1.创建项目 combinations-quickstart-starter-demo
2.基本目录结构
3.代码
1.pom.xml 引入quickstart依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.sunxiansheng</groupId>
<artifactId>sunrays-framework-demo</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>combinations-quickstart-starter-demo</artifactId>
<!-- 通过properties来指定版本号 -->
<properties>
<!-- 指定编译版本 -->
<java.version>1.8</java.version>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- 指定Sunrays-Framework的版本 -->
<sunrays.version>1.0.0</sunrays.version>
</properties>
<dependencyManagement>
<!-- 使用sunrays-dependencies来管理依赖,则依赖无需加版本号 -->
<dependencies>
<dependency>
<groupId>cn.sunxiansheng</groupId>
<artifactId>sunrays-dependencies</artifactId>
<version>${sunrays.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- 快速入门的一个starter -->
<dependency>
<groupId>cn.sunxiansheng</groupId>
<artifactId>combinations-quickstart-starter</artifactId>
<!-- 无需指定版本 -->
</dependency>
</dependencies>
</project>
2.application.yml 配置日志存储根目录
sun-rays:
log4j2:
home: /Users/sunxiansheng/IdeaProjects/sunrays-framework-demo/combinations-quickstart-starter-demo/logs # 日志存储的根目录
3.QuickStartController.java 测试的Controller
package cn.sunxiansheng.quickstart.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Description: QuickStartController
*
* @Author sun
* @Create 2025/1/18 19:17
* @Version 1.0
*/
@RestController
public class QuickStartController {
/**
* A test endpoint.
*
* @return A sample response.
*/
@RequestMapping("/test")
public String test() {
return "This is a test response from QuickStartController";
}
}
4.QuickStartApplication.java 启动类
package cn.sunxiansheng.quickstart;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Description: QuickStartApplication
*
* @Author sun
* @Create 2025/1/18 18:52
* @Version 1.0
*/
@SpringBootApplication
public class QuickStartApplication {
public static void main(String[] args) {
SpringApplication.run(QuickStartApplication.class, args);
}
}
5.测试运行
6.请求测试
1.Controller的返回结果是String,默认自动被包装了
2.自带链路追踪以及全链路的日志输出
4.基础知识(必读)
1.项目架构
1.图示
2.说明
1. sunrays-dependencies
- 职责:管理项目的依赖版本及配置。
- 描述:独立模块,不继承其他模块。
2. sunrays-framework
- 职责:统一管理整个项目,继承
sunrays-dependencies
。 - 描述:作为项目的核心框架模块,负责整体项目的基础配置和管理。
3. sunrays-common
- 职责:封装通用组件,继承
sunrays-framework
。 - 描述:包括一些通用的工具类、公共模块等。
4. sunrays-common-cloud
- 职责:封装 Cloud 相关的通用组件,继承
sunrays-framework
。 - 描述:包括 Cloud 相关的基础设施和服务封装。
5. sunrays-common-demo
- 职责:提供
sunrays-common
和sunrays-common-cloud
的测试 demo,继承sunrays-framework
。 - 描述:用于验证
sunrays-common
和sunrays-common-cloud
子模块的功能和集成。
6. sunrays-combinations
- 职责:管理业务依赖,或者作为中台,继承
sunrays-framework
。 - 描述:通过组合
sunrays-common
和sunrays-common-cloud
完成具体的业务依赖管理。
7. sunrays-combinations-demo
- 职责:提供
sunrays-combinations
模块的测试 demo,继承sunrays-framework
。 - 描述:用于验证
sunrays-combinations
模块的功能和集成。
2.common-log4j2-starter说明
1.这个模块是必须被引入的!
2.对于sunrays-combinations
如果引入了sunrays-combinations模块的依赖,就不需要额外引入common-log4j2-starter,因为已经默认包含了。
3.对于sunrays-common或者sunrays-common-cloud
如果引入的是sunrays-common或者sunrays-common-cloud,那么就需要额外引入common-log4j2-starter。
3.隐私数据保护的问题
1.引入common-env-starter
这个依赖一旦引入,就可以在application.yml配置文件中配置.env文件的路径,然后通过$占位符来从.env文件中读取隐私数据,我在后面的介绍中都会采用这种方式。
2.不引入common-env-starter
在实际开发中,如果不需要进行隐私数据的保护,就可以不引入这个依赖:
- 不需要在application.yml配置文件中配置.env文件的路径,也不需要创建.env文件了
- 将我示例中的${xxx}的部分直接替换为真实的数据即可,比如:
5.核心模块介绍
1.common-log4j2-starter
1.功能介绍
ApplicationReadyListener
- 程序启动时,动态获取并打印以下信息:
- 日志存储的根目录的绝对路径
- 应用的访问地址(包括端口)
- 前端请求时可用的日期格式
- 程序启动时,动态获取并打印以下信息:
ApplicationEnvironmentPreparedListener
- 获取日志根目录及模块名,并将其设置为环境变量供
log4j2-spring.xml
使用。
- 获取日志根目录及模块名,并将其设置为环境变量供
TraceIdLoggingAspect
- 每次请求开始时打印一行日志,包含请求的
traceId
,以便于日志追踪。
- 每次请求开始时打印一行日志,包含请求的
LogAspect
- 日志切面(AOP),匹配所有
controller
和service
包中的方法,记录以下信息:- 方法的执行时间
- 请求参数
- 返回结果
- 日志切面(AOP),匹配所有
- 使用自定义的
banner.txt
- 读取项目的版本号,并在启动时展示自定义的 Banner。
2.配置示例
sun-rays:
log4j2:
home: /Users/sunxiansheng/IdeaProjects/sunrays-framework/sunrays-demo/common-log4j2-starter-demo/logs # 日志根目录 (默认: ./logs)
log-aspect-enable: true # 是否启用日志切面 (默认开启)
3.案例演示
1.创建模块
2.目录结构
3.pom.xml
1.基本配置
<!-- 通过properties来指定版本号 -->
<properties>
<!-- 指定编译版本 -->
<java.version>1.8</java.version>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- 指定Sunrays-Framework的版本 -->
<sunrays.version>1.0.0</sunrays.version>
</properties>
<dependencyManagement>
<!-- 使用sunrays-dependencies来管理依赖,则依赖无需加版本号 -->
<dependencies>
<dependency>
<groupId>cn.sunxiansheng</groupId>
<artifactId>sunrays-dependencies</artifactId>
<version>${sunrays.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2.引入依赖
<dependencies>
<!-- common-log4j2-starter -->
<dependency>
<groupId>cn.sunxiansheng</groupId>
<artifactId>common-log4j2-starter</artifactId>
</dependency>
<!-- 引入web模块作为测试! -->
<dependency>
<groupId>cn.sunxiansheng</groupId>
<artifactId>common-web-starter</artifactId>
</dependency>
</dependencies>
4.application.yml 配置日志输出根目录
sun-rays:
log4j2:
home: /Users/sunxiansheng/IdeaProjects/sunrays-framework-demo/common-log4j2-starter-demo/logs # 日志根目录 (默认: ./logs)
5.Log4j2Controller.java 测试Controller
package cn.sunxiansheng.log4j2.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Description: Log4j2Controller
*
* @Author sun
* @Create 2025/1/19 13:07
* @Version 1.0
*/
@RestController
public class Log4j2Controller {
/**
* A test endpoint.
*
* @return A sample response.
*/
@RequestMapping("/test")
public String test() {
return "This is a test response from Log4j2Controller";
}
}
6.Log4j2Application.java 启动类
package cn.sunxiansheng.log4j2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Description: Log4j2Application
*
* @Author sun
* @Create 2025/1/19 13:07
* @Version 1.0
*/
@SpringBootApplication
public class Log4j2Application {
public static void main(String[] args) {
SpringApplication.run(Log4j2Application.class, args);
}
}
7.测试
1.启动
2.请求
2.common-web-starter
1.功能介绍
1.JacksonConfig 提供对日期类型的序列化支持
- 配置 Jackson 的 ObjectMapper 来为 Date 类型指定自定义的序列化和反序列化格式。这样,前后端可以统一日期格式。
2.修饰SpringMVC 中的请求处理适配器
- 通过自定义注解 IgnoredResultWrapper 和装饰器模式对 SpringMVC 中的请求处理适配器进行扩展,控制响应结果的包装及处理方式。
3.将前台传进的多种日期格式自动反序列化为 Date 类型
- 使用
@InitBinder
来解析来自前端的多种日期格式,确保它们能正确反序列化为 Date 类型。
2.配置示例
配置 Maven 来打包项目时,指定 .jar
文件的名称为项目的 artifactId
和 version
,并使用 spring-boot-maven-plugin
插件来打包所有依赖。
<!-- Maven 打包常规配置 -->
<build>
<!-- 打包成 jar 包时的名字为项目的 artifactId + version -->
<finalName>${project.artifactId}-${project.version}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.4.2</version>
<executions>
<execution>
<goals>
<!-- 将所有的依赖包都打到这个模块中 -->
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
3.案例演示
1.创建模块
2.目录结构
3.pom.xml
1.基本配置
<!-- 通过properties来指定版本号 -->
<properties>
<!-- 指定编译版本 -->
<java.version>1.8</java.version>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- 指定Sunrays-Framework的版本 -->
<sunrays.version>1.0.0</sunrays.version>
</properties>
<dependencyManagement>
<!-- 使用sunrays-dependencies来管理依赖,则依赖无需加版本号 -->
<dependencies>
<dependency>
<groupId>cn.sunxiansheng</groupId>
<artifactId>sunrays-dependencies</artifactId>
<version>${sunrays.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2.引入依赖
<dependencies>
<!-- common-web-starter -->
<dependency>
<groupId>cn.sunxiansheng</groupId>
<artifactId>common-web-starter</artifactId>
</dependency>
<!-- common-log4j2-starter 是必须引入的!!! -->
<dependency>
<groupId>cn.sunxiansheng</groupId>
<artifactId>common-log4j2-starter</artifactId>
</dependency>
</dependencies>
3.打包配置
<!-- maven 打包常规配置 -->
<build>
<!-- 打包成 jar 包时的名字为项目的artifactId + version -->
<finalName>${project.artifactId}-${project.version}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.4.2</version>
<executions>
<execution>
<goals>
<!-- 将所有的依赖包都打到这个模块中 -->
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
4.application.yml 配置日志根目录
sun-rays:
log4j2:
home: /Users/sunxiansheng/IdeaProjects/sunrays-framework-demo/common-web-starter-demo/logs # 日志根目录(默认./logs)
5.WebController.java 测试三种Web响应方式
package cn.sunxiansheng.web.controller;
import cn.sunxiansheng.tool.response.ResultWrapper;
import cn.sunxiansheng.web.annotation.IgnoredResultWrapper;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Description: WebController
*
* @Author sun
* @Create 2025/1/19 13:39
* @Version 1.0
*/
@RestController
public class WebController {
/**
* 第一种方式:直接使用自动包装成功结果
*
* @return
*/
@RequestMapping("/method1")
public String method1() {
return "method1";
}
/**
* 第二种方式:使用 @IgnoredResultWrapper注解忽略掉自动包装
*
* @return
*/
@IgnoredResultWrapper
@RequestMapping("/method2")
public String method2() {
return "method2";
}
/**
* 第三种方式:直接使用ResultWrapper来自己封装结果
*
* @return
*/
@RequestMapping("/method3")
public ResultWrapper<String> method3() {
return ResultWrapper.fail("method3");
}
}
6.WebApplication.java 启动类
package cn.sunxiansheng.web;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Description: WebApplication
*
* @Author sun
* @Create 2025/1/19 13:39
* @Version 1.0
*/
@SpringBootApplication
public class WebApplication {
public static void main(String[] args) {
SpringApplication.run(WebApplication.class, args);
}
}
7.测试
1.第一种方式:直接使用自动包装成功结果
2.第二种方式:使用 @IgnoredResultWrapper注解忽略掉自动包装
3.第三种方式:直接使用ResultWrapper来自己封装结果
4.测试打包功能 clean install
3.common-mybatis-plus-starter
1.功能介绍
1.自定义分页封装
- 只需传递
pageNum
,pageSize
,total
,data
即可自动完成分页封装,简化分页逻辑。
2.对 MyBatis-Plus 框架进行二次封装
- 隐藏
QueryWrapper
等复杂实现细节,保留动态生成 CRUD 特性。 - 结合 EasyCode 自定义模板优化代码生成,实现无感封装,统一数据访问规范,大幅提升开发效率和规范性,打造高可用、高扩展性、低入门成本的企业级数据访问层解决方案。
3.定义 MyBatis 拦截器
- 对 SQL 进行格式话输出,提高 SQL 可读性,便于排查问题。
2.配置示例
1.开启 SQL 美化 配置是否启用 SQL 美化功能,使 SQL 更加可读。
sun-rays:
mybatis-plus:
sql-beauty-enabled: true # 是否开启 SQL 美化(默认 true)
2.配置数据源 配置数据库连接信息,使用 Spring 的数据源配置项。
spring:
datasource:
username: ${
MYSQL_USERNAME} # 用户名
password: ${
MYSQL_PASSWORD} # 密码
url: jdbc:mysql://${
MYSQL_IP}:${
MYSQL_PORT}/${
MYSQL_DATABASE}?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false # 数据库连接 URL
3.配置 EasyCode 的宏定义
# 定义配置宏
# 指定逻辑删除字段名(驼峰命名)
set($logicDeleteField = "isDeleted")
# 指定逻辑删除字段名(下划线命名)
set($logicDeleteFieldUnder = "is_deleted")
# 定义全局逻辑删除字段的删除值和未删除值
set($deleteColumnValue = 1)
set($notDeletedColumnValue = 0)
# 配置排除的字段列表(驼峰命名)
set($excludedFields = ["createBy", "createTime", "updateBy", "updateTime"])
4.配置 common-mybatis-plus-starter
模块的 BaseEntity
与通用字段一致
5.最佳实践(无需任何配置)
通用字段为:
create_by
create_time
update_by
update_time
is_deleted
3.案例演示
1.EasyCode插件配置
1.下载 EasyCodeConfig.json
2.在插件设置中选择从本地导入配置
3.在设置的Global Config中创建sunrays-framework.vm并粘贴配置
# 定义配置宏
# 指定逻辑删除字段名(驼峰命名)
set($logicDeleteField = "isDeleted")
# 指定逻辑删除字段名(下划线命名)
set($logicDeleteFieldUnder = "is_deleted")
#定义全局逻辑删除字段的删除值和未删除值
set($deleteColumnValue = 1)
set($notDeletedColumnValue = 0)
# 配置排除的字段列表(驼峰命名)
set($excludedFields = ["createBy", "createTime", "updateBy", "updateTime"])
# 最佳实践(必要字段):create_by,create_time,update_by,update_time,is_deleted
2.IDEA连接数据库
1.点击右上角的数据库标志,然后点击加号,选择要连接的数据库
2.填写数据库信息进行连接
3.创建并使用数据库 sunrays_framework
create database sunrays_framework;
use sunrays_framework;
4.创建示例表
CREATE TABLE example_table
(
id INT PRIMARY KEY COMMENT '主键ID',
user_name VARCHAR(255) NULL COMMENT '用户名称',
user_email VARCHAR(255) NULL COMMENT '用户邮箱',
phone_number VARCHAR(20) DEFAULT NULL COMMENT '联系电话',
home_address VARCHAR(255) DEFAULT NULL COMMENT '家庭住址',
account_status TINYINT(1) DEFAULT 0 COMMENT '账户状态(0-禁用,1-启用)',
create_by VARCHAR(50) DEFAULT NULL COMMENT '创建人',
create_time DATETIME COMMENT '创建时间',
update_by VARCHAR(50) DEFAULT NULL COMMENT '更新人',
update_time DATETIME COMMENT '更新时间',
is_deleted TINYINT(1) DEFAULT 0 COMMENT '逻辑删除标记(0-未删除,1-已删除)'
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 COMMENT ='示例表';
INSERT INTO example_table (id, user_name, user_email, phone_number, home_address, account_status, create_by)
VALUES (1, '张三', 'zhangsan@example.com', '13800138000', '北京市朝阳区', 1, 'admin'),
(2, '李四', 'lisi@example.com', '13800138001', '上海市浦东新区', 1, 'admin'),
(3, '王五', 'wangwu@example.com', '13800138002', '广州市天河区', 0, 'admin'),
(4, '赵六', 'zhaoliu@example.com', '13800138003', '深圳市福田区', 1, 'admin'),
(5, '孙七', 'sunqi@example.com', '13800138004', '成都市武侯区', 0, 'admin'),
(6, '周八', 'zhouba@example.com', '13800138005', '杭州市西湖区', 1, 'admin'),
(7, '吴九', 'wujia@example.com', '13800138006', '重庆市渝中区', 0, 'admin'),
(8, '郑十', 'zhengshi@example.com', '13800138007', '南京市鼓楼区', 1, 'admin'),
(9, '冯十一', 'fengshiyi@example.com', '13800138008', '武汉市武昌区', 1, 'admin'),
(10, '褚十二', 'chushier@example.com', '13800138009', '长沙市岳麓区', 0, 'admin');
5.让IDEA连接刚才创建的数据库
3.项目环境搭建
1.创建模块 common-mybatis-plus-starter-demo
2.目录结构
3.pom.xml
基本配置
<!-- 通过properties来指定版本号 --> <properties> <!-- 指定编译版本 --> <java.version>1.8</java.version> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <!-- 指定Sunrays-Framework的版本 --> <sunrays.version>1.0.0</sunrays.version> </properties> <dependencyManagement> <!-- 使用sunrays-dependencies来管理依赖,则依赖无需加版本号 --> <dependencies> <dependency> <groupId>cn.sunxiansheng</groupId> <artifactId>sunrays-dependencies</artifactId> <version>${sunrays.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
引入依赖
<dependencies> <!-- common-mybatis-plus-starter --> <dependency> <groupId>cn.sunxiansheng</groupId> <artifactId>common-mybatis-plus-starter</artifactId> </dependency> <!-- common-log4j2-starter 是必须引入的!!! --> <dependency> <groupId>cn.sunxiansheng</groupId> <artifactId>common-log4j2-starter</artifactId> </dependency> <!-- EasyCode 生成的代码依赖Web模块--> <dependency> <groupId>cn.sunxiansheng</groupId> <artifactId>common-web-starter</artifactId> </dependency> <!-- env模块确保数据安全,可以不引入 --> <dependency> <groupId>cn.sunxiansheng</groupId> <artifactId>common-env-starter</artifactId> </dependency> </dependencies>
4.application.yml 配置日志根目录、.env文件的绝对路径、数据源
sun-rays:
log4j2:
home: /Users/sunxiansheng/IdeaProjects/sunrays-framework/sunrays-common-demo/common-mybatis-plus-starter-demo/logs # 日志根目录(默认./logs)
env:
path: /Users/sunxiansheng/IdeaProjects/sunrays-framework/sunrays-common-demo/common-mybatis-plus-starter-demo # .env文件的绝对路径
spring:
datasource:
username: ${
MYSQL_USERNAME} # 用户名
password: ${
MYSQL_PASSWORD} # 密码
url: jdbc:mysql://${
MYSQL_IP}:${
MYSQL_PORT}/${
MYSQL_DATABASE}?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false # url
5..env文件配置数据库信息
MYSQL_USERNAME= 用户名
MYSQL_PASSWORD= 密码
MYSQL_IP= ip
MYSQL_PORT= 端口
MYSQL_DATABASE= 数据库名字
6.MyBatisPlusController.java 测试Controller
package cn.sunxiansheng.mybatis.plus.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Description: MyBatisPlusController
*
* @Author sun
* @Create 2025/1/19 15:01
* @Version 1.0
*/
@RestController
public class MyBatisPlusController {
/**
* A test endpoint.
*
* @return A sample response.
*/
@RequestMapping("/test")
public String test() {
return "This is a test response from MyBatisPlusController";
}
}
7.MyBatisPlusApplication.java 启动类
package cn.sunxiansheng.mybatis.plus;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Description: MyBatisPlusApplication
*
* @Author sun
* @Create 2025/1/19 15:00
* @Version 1.0
*/
@SpringBootApplication
public class MyBatisPlusApplication {
public static void main(String[] args) {
SpringApplication.run(MyBatisPlusApplication.class, args);
}
}
8.启动测试
4.使用EasyCode生成代码
1.选中指定的表,右键生成代码
2.选择Module和Package以及之前导入的模板,点击OK
3.生成的代码
5.测试分页查询
1.不加任何条件查询第一页,页面大小为三的数据
2.响应结果
{
"success": true,
"code": 200,
"message": "操作成功",
"data": {
"pageNo": 1,
"pageSize": 3,
"total": 10,
"totalPages": 4,
"result": [
{
"id": 1,
"userName": "张三",
"userEmail": "zhangsan@example.com",
"phoneNumber": "13800138000",
"homeAddress": "北京市朝阳区",
"accountStatus": 1,
"isDeleted": 0
},
{
"id": 2,
"userName": "李四",
"userEmail": "lisi@example.com",
"phoneNumber": "13800138001",
"homeAddress": "上海市浦东新区",
"accountStatus": 1,
"isDeleted": 0
},
{
"id": 3,
"userName": "王五",
"userEmail": "wangwu@example.com",
"phoneNumber": "13800138002",
"homeAddress": "广州市天河区",
"accountStatus": 0,
"isDeleted": 0
}
],
"start": 1,
"end": 3,
"hasNextPage": true,
"hasPreviousPage": false
}
}
6.全链路日志展示
1.controller和service方法的入参格式化打印
2.统计数量的sql格式化打印
3.查询数据的sql格式化打印
4.controller和service方法的出参格式化打印
4.部分生成代码说明
1.ExampleTableController.java
1.展示
2.介绍
- 该文件是Controller
- 继承了BaseController,提供了Date类型的自动转换,和一些快捷的响应方法
- 提供了一些基础的接口实现
- 如果想要自定义接口,直接在下面调用Service即可,跟平常的使用方式是一样的
2.ExampleTableService.java
1.展示
2.介绍
- 该文件是Service接口
- 两个泛型分别是与数据库对应的entity的类型以及id类型
- 使用的时候也是跟平常一样,在接口内加一个方法
3.ExampleTableServiceImpl.java
1.展示
2.介绍
- 该文件是Service的实现类
- 三个泛型分别是调用MyBatis Plus方法的Mapper类型,与数据库对应的entity的类型以及id类型
- 构造器是为了在运行时动态给父类设置具体的操作Mybatis Plus的Mapper
- 调用框架自动生成的方法使用super.xxx,调用自己实现的方法用注入的Mapper
- 使用的时候,就不需要管那么多,直接实现Service方法即可
4.ExampleTableMapper.java
1.展示
2.介绍
- 该文件是Mapper接口
- 提供了三个基础的方法
- 使用起来也是跟平常一样,加方法
5.ExampleTableMapper.xml
1.展示
2.介绍
- 该文件是Mapper的实现类
- 提供了三个基础方法的实现
- 使用起来就直接实现Mapper接口的方法去写sql就行
5.示例:使用框架编写一个需求
1.需求
分页查询id大于5的用户
2.目录结构
3.ExampleTableMapper.java 新增求数量和求数据的方法
/**
* 查询出id大于5的用户数量
*
* @return
*/
Long countById();
/**
* 分页查询出id大于5的所有用户
*
* @param offset
* @param limit
* @return
*/
List<ExampleTable> queryByPageAboutId(@Param("offset") Long offset,
@Param("limit") Long limit);
4.ExampleTableMapper.xml 实现方法
<select id="countById" resultType="java.lang.Long">
select count(*)
from example_table
where id > 5
</select>
<select id="queryByPageAboutId" resultMap="ExampleTableMap">
select id, user_name, user_email, phone_number, home_address, account_status, create_by, create_time, update_by,
update_time, is_deleted
from example_table
where id > 5
and is_deleted = 0
limit #{offset}, #{limit}
</select>
5.ExampleTableService.java 新增分页查询的方法
/**
* 分页查询id大于5的用户
*
* @return
*/
PageResult<ExampleTableVo> queryByPageAboutId(ExampleTableController.QueryByPageAboutIdReq req);
6.ExampleTableServiceImpl.java 实现方法
@Override
@Transactional(rollbackFor = Exception.class) // 开启事务
public PageResult<ExampleTableVo> queryByPageAboutId(ExampleTableController.QueryByPageAboutIdReq req) {
// 分页查询
PageResult<ExampleTableVo> paginate = SunPageHelper.paginate(
req.getPageNo(),
req.getPageSize(),
() -> exampleTableMapper.countById(),
(offset, limit) -> {
// 查询数据,并转换为vo
List<ExampleTable> exampleTableList =
exampleTableMapper.queryByPageAboutId(offset, limit);
List<ExampleTableVo> exampleTableVos = ExampleTableConverter.INSTANCE.convertPoList2VoList(exampleTableList);
return exampleTableVos;
}
);
return paginate;
}
7.ExampleTableController.java 新增req和分页查询方法
@Data
public static class QueryByPageAboutIdReq {
private Long pageNo;
private Long pageSize;
}
/**
* 分页查询id大于5的用户
*
* @return
*/
@GetMapping("/queryByPageAboutId")
public ResultWrapper<PageResult<ExampleTableVo>> queryByPageAboutId(QueryByPageAboutIdReq req) {
// ============================== Preconditions 参数校验 ==============================
// ============================== 调用Service层 ==============================
// 调用service查询id大于5的用户
PageResult<ExampleTableVo> pageResult = exampleTableService.queryByPageAboutId(req);
return ResultWrapper.ok(pageResult);
}
8.查询结果
4.common-minio-starter
1.功能介绍
1.快捷上传文件
- 使用
putObject(MultipartFile file, String bucketName)
一步完成文件上传,并返回预览链接和下载链接。 - 文件自动归类到按日期生成的文件夹中,方便管理。
2.文件名不重复
- 上传文件时,结合日期路径和
UUID
自动生成唯一文件名,避免命名冲突。
3.便捷管理
- 支持文件上传、下载、删除及存储桶管理,满足日常文件操作需求。
- 此工具类极大简化了文件操作流程,让上传和链接生成更加高效可靠。
此工具类极大简化了文件操作流程,让上传和链接生成更加高效可靠!
2.配置示例
sun-rays:
minio:
endpoint: ${
MINIO_ENDPOINT} # minio服务地址 http://ip:端口
accessKey: ${
MINIO_ACCESS_KEY} # minio服务的accessKey
secretKey: ${
MINIO_SECRET_KEY} # minio服务的secretKey
3.案例演示
1.创建模块
2.目录结构
3.pom.xml
1.基本配置
<!-- 通过properties来指定版本号 -->
<properties>
<!-- 指定编译版本 -->
<java.version>1.8</java.version>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- 指定Sunrays-Framework的版本 -->
<sunrays.version>1.0.0</sunrays.version>
</properties>
<dependencyManagement>
<!-- 使用sunrays-dependencies来管理依赖,则依赖无需加版本号 -->
<dependencies>
<dependency>
<groupId>cn.sunxiansheng</groupId>
<artifactId>sunrays-dependencies</artifactId>
<version>${sunrays.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2.引入依赖
<dependencies>
<!-- common-minio-starter -->
<dependency>
<groupId>cn.sunxiansheng</groupId>
<artifactId>common-minio-starter</artifactId>
</dependency>
<!-- common-log4j2-starter 是必须引入的!!! -->
<dependency>
<groupId>cn.sunxiansheng</groupId>
<artifactId>common-log4j2-starter</artifactId>
</dependency>
<!-- 引入web模块作为测试! -->
<dependency>
<groupId>cn.sunxiansheng</groupId>
<artifactId>common-web-starter</artifactId>
</dependency>
<!-- env模块确保数据安全,可以不引入 -->
<dependency>
<groupId>cn.sunxiansheng</groupId>
<artifactId>common-env-starter</artifactId>
</dependency>
</dependencies>
4.application.yml 配置日志根目录、.env文件的绝对路径以及minio
sun-rays:
log4j2:
home: /Users/sunxiansheng/IdeaProjects/sunrays-framework-demo/common-minio-starter-demo/logs # 日志根目录(默认./logs)
env:
path: /Users/sunxiansheng/IdeaProjects/sunrays-framework-demo/common-minio-starter-demo # .env文件的绝对路径
minio:
endpoint: ${
MINIO_ENDPOINT} # minio服务地址 http://ip:端口
accessKey: ${
MINIO_ACCESS_KEY} # minio服务的accessKey
secretKey: ${
MINIO_SECRET_KEY} # minio服务的secretKey
5..env 配置minio的信息
MINIO_ENDPOINT= minio服务地址 http://ip:端口
MINIO_ACCESS_KEY= minio服务的accessKey
MINIO_SECRET_KEY= minio服务的secretKey
6.MinioController.java Minio测试Controller
package cn.sunxiansheng.minio.controller;
import cn.sunxiansheng.minio.utils.MinioUtil;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.util.List;
/**
* Description: MinioController
*
* @Author sun
* @Create 2025/1/19 17:11
* @Version 1.0
*/
@RestController
public class MinioController {
/**
* A test endpoint.
*
* @return A sample response.
*/
@RequestMapping("/test")
public String test() {
return "This is a test response from MinioController";
}
@Resource
private MinioUtil minioUtil;
/**
* 检查存储桶是否存在
*/
@GetMapping("/bucketExists")
public boolean bucketExists(@RequestParam String bucketName) {
return minioUtil.bucketExists(bucketName);
}
/**
* 列出所有存储桶的名字
*/
@GetMapping("/listBucketNames")
public List<String> listBucketNames() {
return minioUtil.listBucketNames();
}
/**
* 创建存储桶
*/
@PostMapping("/makeBucket")
public String makeBucket(@RequestParam String bucketName) {
minioUtil.makeBucket(bucketName);
return "Bucket " + bucketName + " created successfully!";
}
/**
* 删除空的存储桶
*/
@DeleteMapping("/removeBucket")
public String removeBucket(@RequestParam String bucketName) {
minioUtil.removeBucket(bucketName);
return "Bucket " + bucketName + " removed successfully!";
}
/**
* 上传文件并返回预览和下载链接
*/
@PostMapping("/upload")
public List<String> uploadFile(@RequestParam MultipartFile file, @RequestParam String bucketName) {
return minioUtil.putObject(file, bucketName);
}
/**
* 下载文件到指定路径
*/
@GetMapping("/download")
public String downloadFile(@RequestParam String bucketName,
@RequestParam String objectName,
@RequestParam String fileName) {
minioUtil.downloadObject(bucketName, objectName, fileName);
return "File " + objectName + " downloaded to " + fileName;
}
/**
* 删除文件或文件夹
*/
@DeleteMapping("/removeObject")
public String removeObjectOrFolder(@RequestParam String bucketName, @RequestParam String prefix) {
return minioUtil.removeObjectOrFolder(bucketName, prefix);
}
}
7.MinioApplication.java 启动类
package cn.sunxiansheng.minio;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Description: MinioApplication
*
* @Author sun
* @Create 2025/1/19 17:09
* @Version 1.0
*/
@SpringBootApplication
public class MinioApplication {
public static void main(String[] args) {
SpringApplication.run(MinioApplication.class, args);
}
}
4.测试
1.创建一个存储桶
2.检查刚才创建的桶是否存在
3.上传文件并返回预览和下载链接
1.上传到test桶(已经使用mc开放了读权限)
2.预览和下载
3.如果没有开放读权限预览就是下面的报错
5.common-redis-starter
1.功能介绍
1. 快捷操作:
- 支持五大基础数据类型(String、Hash、List、Set、SortedSet)的全面操作。
- 常用功能包括新增、删除、修改、查询及批量操作,使用便捷。
2. 灵活性:
- 提供自定义缓存键名生成方法,支持默认分隔符(
:)
及自定义分隔符的拼接。 - 通过 Lua 脚本支持原子操作,提升操作性能。
3. 数据安全性:
- 缓存数据采用 JSON 序列化存储,自动保留类型信息,确保数据在反序列化时的准确性。
- 完善的类型转换支持,提供类型安全的 Redis 操作。
4. 高效的管理:
- 支持对键设置过期时间、延长有效期、批量删除等操作。
- 针对不同场景,提供丰富的类型专用方法,如:批量获取、排序操作、分数范围查询等。
6.丰富的操作功能:
String 类型:
- 快速设置键值。
- 设置带过期时间的缓存。
- 支持条件操作(如键不存在时才设置值)。
Hash 类型:
- 单值、多值的添加与删除。
- 获取单个值、多个值及完整键值对。
- 支持键值对的批量操作。
List 类型:
- 左/右添加与弹出。
- 根据索引操作列表元素。
- 支持获取指定范围及完整列表内容。
Set 类型:
- 元素的添加、删除及批量操作。
- 集合长度查询及随机元素获取。
- 检查集合是否包含某个元素。
SortedSet 类型:
- 添加元素及分数更新。
- 获取指定分数范围的元素及排序结果。
- 支持获取元素排名及分数。
2.配置示例
spring:
redis:
host: ${
REDIS_HOST} # Redis服务器地址 ip
port: ${
REDIS_PORT} # Redis服务器端口
password: ${
REDIS_PASSWORD} # Redis服务器密码
3.案例演示
1.创建模块
2.目录结构
3.pom.xml
1.基本配置
<!-- 通过properties来指定版本号 -->
<properties>
<!-- 指定编译版本 -->
<java.version>1.8</java.version>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- 指定Sunrays-Framework的版本 -->
<sunrays.version>1.0.0</sunrays.version>
</properties>
<dependencyManagement>
<!-- 使用sunrays-dependencies来管理依赖,则依赖无需加版本号 -->
<dependencies>
<dependency>
<groupId>cn.sunxiansheng</groupId>
<artifactId>sunrays-dependencies</artifactId>
<version>${sunrays.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2.引入依赖
<dependencies>
<dependency>
<groupId>cn.sunxiansheng</groupId>
<artifactId>common-redis-starter</artifactId>
</dependency>
<!-- common-log4j2-starter 是必须引入的!!! -->
<dependency>
<groupId>cn.sunxiansheng</groupId>
<artifactId>common-log4j2-starter</artifactId>
</dependency>
<!-- 用来测试的Web模块 -->
<dependency>
<groupId>cn.sunxiansheng</groupId>
<artifactId>common-web-starter</artifactId>
</dependency>
<!-- env模块确保数据安全,可以不引入 -->
<dependency>
<groupId>cn.sunxiansheng</groupId>
<artifactId>common-env-starter</artifactId>
</dependency>
</dependencies>
4.application.yml 配置日志根目录、.env文件的绝对路径以及Redis
sun-rays:
log4j2:
home: /Users/sunxiansheng/IdeaProjects/sunrays-framework-demo/common-redis-starter-demo/logs # 日志根目录(默认./logs)
env:
path: /Users/sunxiansheng/IdeaProjects/sunrays-framework-demo/common-redis-starter-demo # .env文件的绝对路径
spring:
redis:
host: ${
REDIS_HOST} # Redis服务器地址 ip
port: ${
REDIS_PORT} # Redis服务器端口
password: ${
REDIS_PASSWORD} # Redis服务器密码
5..env 配置Redis的信息
REDIS_HOST= Redis服务器地址 ip
REDIS_PORT= Redis服务器端口
REDIS_PASSWORD= Redis服务器密码
6.测试的Controller
1.RHashController.java
package cn.sunxiansheng.redis.controller;
import cn.sunxiansheng.redis.utils.RHash;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/rhash")
public class RHashController {
private final RHash rHash;
public RHashController(RHash rHash) {
this.rHash = rHash;
}
@NoArgsConstructor
@AllArgsConstructor
@Data
public static class User {
private Long id;
private String name;
private Integer age;
}
/**
* 设置单个hash值
*/
@PostMapping("/put")
public void put(@RequestParam String key, @RequestParam String hashKey, @RequestBody User user) {
rHash.put(key, hashKey, user);
}
/**
* 设置多个hash值
*/
@PostMapping("/putAll")
public void putAll(@RequestParam String key, @RequestBody Map<String, User> userMap) {
rHash.putAll(key, userMap);
}
/**
* 删除hash中的多个值
*/
@DeleteMapping("/deleteMultiple")
public Long deleteMultiple(@RequestParam String key, @RequestBody List<String> hashKeys) {
return rHash.delete(key, hashKeys);
}
/**
* 删除hash中的单个值
*/
@DeleteMapping("/deleteSingle")
public Long deleteSingle(@RequestParam String key, @RequestParam String hashKey) {
return rHash.delete(key, hashKey);
}
/**
* 获取单个hash值并转换为指定类型
*/
@GetMapping("/getOne")
public User getOneMapValue(@RequestParam String key, @RequestParam String hashKey) {
return rHash.getOneMapValue(key, hashKey, User.class);
}
/**
* 获取多个hash值并转换为指定类型
*/
@GetMapping("/getMulti")
public List<User> getMultiMapValue(@RequestParam String key, @RequestBody List<String> hashKeys) {
return rHash.getMultiMapValue(key, hashKeys, User.class);
}
/**
* 获取所有键值对并转换为指定类型
*/
@GetMapping("/getAll")
public Map<String, User> getAll(@RequestParam String key) {
return rHash.getAll(key, User.class);
}
/**
* 判断hash中是否存在某个键
*/
@GetMapping("/hasHashKey")
public Boolean hasHashKey(@RequestParam String key, @RequestParam String hashKey) {
return rHash.hasHashKey(key, hashKey);
}
}
2.RSetController.java
package cn.sunxiansheng.redis.controller;
import cn.sunxiansheng.redis.utils.RSet;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.io.Serializable;
import java.util.Set;
/**
* Description: RSet的测试Controller
*
* @Author sun
* @Create 2024/11/15
* @Version 1.0
*/
@RestController
@RequestMapping("/rset")
public class RSetController {
@Autowired
private RSet rSet;
/**
* 静态内部类表示简单的自定义对象
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class Element implements Serializable {
private Long id;
private String name;
}
/**
* 添加一个元素
*/
@PostMapping("/add")
public Long add(@RequestParam String key, @RequestBody Element element) {
return rSet.add(key, element);
}
/**
* 批量添加元素
*/
@PostMapping("/addBatch")
public Long addBatch(@RequestParam String key, @RequestBody Set<Element> elements) {
return rSet.addBatch(key, elements);
}
/**
* 移除元素
*/
@PostMapping("/remove")
public Long remove(@RequestParam String key, @RequestBody Set<Element> elements) {
return rSet.remove(key, elements);
}
/**
* 获取集合
*/
@GetMapping("/members")
public Set<Element> members(@RequestParam String key) {
return rSet.members(key, Element.class);
}
/**
* 判断是否包含某元素
*/
@GetMapping("/isMember")
public Boolean isMember(@RequestParam String key, @RequestBody Element element) {
return rSet.isMember(key, element);
}
/**
* 获取集合大小
*/
@GetMapping("/size")
public Long size(@RequestParam String key) {
return rSet.size(key);
}
}
3.RSortedSetController.java
package cn.sunxiansheng.redis.controller;
import cn.sunxiansheng.redis.utils.RSortedSet;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.web.bind.annotation.*;
import java.util.Set;
/**
* Description: 测试 RSortedSet 工具类
*
* @Author sun
* @Create 2024/11/16
* @Version 1.0
*/
@RestController
@RequestMapping("/rsortedset")
public class RSortedSetController {
@Autowired
private RSortedSet rSortedSet;
// 自定义类型
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class CustomValue {
private Long id;
private String name;
}
@PostMapping("/add")
public Boolean add(@RequestParam String key, @RequestBody CustomValue value, @RequestParam double score) {
return rSortedSet.add(key, value, score);
}
@PostMapping("/addBatch")
public Long addBatch(@RequestParam String key, @RequestBody Set<RSortedSet.DataScore<CustomValue>> dataScores) {
return rSortedSet.addBatch(key, dataScores);
}
@PostMapping("/remove")
public Long remove(@RequestParam String key, @RequestBody Set<CustomValue> values) {
return rSortedSet.remove(key, values);
}
@PostMapping("/removeRangeByScore")
public Long removeRangeByScore(@RequestParam String key, @RequestParam double min, @RequestParam double max) {
return rSortedSet.removeRangeByScore(key, min, max);
}
@PostMapping("/changeByDelta")
public Double changeByDelta(@RequestParam String key, @RequestBody CustomValue value, @RequestParam double delta) {
return rSortedSet.changeByDelta(key, value, delta);
}
@GetMapping("/reverseRangeWithScores")
public Set<ZSetOperations.TypedTuple<CustomValue>> reverseRangeWithScores(@RequestParam String key) {
return rSortedSet.reverseRangeWithScores(key, CustomValue.class);
}
@GetMapping("/score")
public Double score(@RequestParam String key, @RequestBody CustomValue value) {
return rSortedSet.score(key, value);
}
@GetMapping("/reverseRank")
public Long reverseRank(@RequestParam String key, @RequestBody CustomValue value) {
return rSortedSet.reverseRank(key, value);
}
@GetMapping("/zCard")
public Long zCard(@RequestParam String key) {
return rSortedSet.zCard(key);
}
@GetMapping("/count")
public Long count(@RequestParam String key, @RequestParam double min, @RequestParam double max) {
return rSortedSet.count(key, min, max);
}
}
4.RStringController.java
package cn.sunxiansheng.redis.controller;
import cn.sunxiansheng.redis.utils.RString;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
/**
* Description: 测试 RString 工具类的 Controller
*
* @Author sun
* @Create 2024/11/14 15:20
* @Version 1.0
*/
@RestController
@RequestMapping("/rstring")
public class RStringController {
@Resource
private RString rString;
/**
* 设置缓存值
*/
@PostMapping("/set")
public String set(@RequestParam String key, @RequestParam Long value) {
rString.set(key, value);
return "Value set successfully!";
}
/**
* 设置缓存值并设置过期时间
*/
@PostMapping("/setWithExpire")
public String setWithExpire(@RequestParam String key,
@RequestParam Long value,
@RequestParam long timeout) {
rString.setWithExpire(key, value, timeout);
return "Value set with expiration successfully!";
}
/**
* 设置缓存值并设置过期时间(自定义时间单位)
*/
@PostMapping("/setWithExpireUnit")
public String setWithExpireUnit(@RequestParam String key,
@RequestParam Long value,
@RequestParam long timeout) {
rString.setWithExpire(key, value, timeout, TimeUnit.DAYS);
return "Value set with custom expiration successfully!";
}
/**
* 设置缓存值,如果key不存在,则设置成功
*/
@PostMapping("/setIfAbsent")
public String setIfAbsent(@RequestParam String key,
@RequestParam Long value,
@RequestParam long timeout) {
boolean result = rString.setIfAbsent(key, value, timeout);
return result ? "Value set successfully!" : "Key already exists!";
}
/**
* 获取缓存值
*/
@GetMapping("/get")
public Long get(@RequestParam String key) {
return rString.get(key, Long.class);
}
/**
* 获取缓存值并设置新值
*/
@PostMapping("/getAndSet")
public Long getAndSet(@RequestParam String key, @RequestParam Long value) {
return rString.getAndSet(key, value, Long.class);
}
/**
* 根据 delta 值调整缓存中的数值
*/
@PostMapping("/changeByDelta")
public Long changeByDelta(@RequestParam String key, @RequestParam long delta) {
return rString.changeByDelta(key, delta);
}
}
5.RListController.java
package cn.sunxiansheng.redis.controller;
import cn.sunxiansheng.redis.utils.RList;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 测试 RList 工具类的 Controller
*
* @Author sun
* @Create 2024/11/14 16:00
* @Version 1.0
*/
@RestController
@RequestMapping("/rlist")
public class RListController {
@Autowired
private RList rList;
/**
* 左侧添加单个值
*/
@PostMapping("/leftPush")
public String leftPush(@RequestParam String key, @RequestBody User user) {
rList.leftPush(key, user);
return "User added to the left of the list.";
}
/**
* 左侧批量添加多个值
*/
@PostMapping("/leftPushAll")
public String leftPushAll(@RequestParam String key, @RequestBody List<User> users) {
rList.leftPushAll(key, users);
return "Users added to the left of the list.";
}
/**
* 右侧添加单个值
*/
@PostMapping("/rightPush")
public String rightPush(@RequestParam String key, @RequestBody User user) {
rList.rightPush(key, user);
return "User added to the right of the list.";
}
/**
* 右侧批量添加多个值
*/
@PostMapping("/rightPushAll")
public String rightPushAll(@RequestParam String key, @RequestBody List<User> users) {
rList.rightPushAll(key, users);
return "Users added to the right of the list.";
}
/**
* 根据索引设置List中的值
*/
@PutMapping("/set")
public String set(@RequestParam String key, @RequestParam int index, @RequestBody User user) {
rList.set(key, index, user);
return "User updated at index " + index;
}
/**
* 获取List中的所有数据
*/
@GetMapping("/rangeAll")
public List<User> rangeAll(@RequestParam String key) {
return rList.rangeAll(key, User.class);
}
/**
* 根据索引获取List中的元素
*/
@GetMapping("/getElementByIndex")
public User getElementByIndex(@RequestParam String key, @RequestParam int index) {
return rList.getElementByIndex(key, index, User.class);
}
/**
* 获取List的长度
*/
@GetMapping("/size")
public Long size(@RequestParam String key) {
return rList.size(key);
}
/**
* 从右侧弹出元素
*/
@DeleteMapping("/popRight")
public User popRight(@RequestParam String key) {
return rList.popRight(key, User.class);
}
/**
* 从左侧弹出元素
*/
@DeleteMapping("/popLeft")
public User popLeft(@RequestParam String key) {
return rList.popLeft(key, User.class);
}
/**
* 根据值移除指定数量的匹配元素
*/
@DeleteMapping("/removeByValue")
public String removeByValue(@RequestParam String key, @RequestParam int count, @RequestBody User user) {
Long removedCount = rList.removeByValue(key, count, user);
return removedCount + " matching users removed.";
}
}
/**
* 简单的自定义类型 User
*
* @Author sun
* @Create 2024/11/14 15:50
* @Version 1.0
*/
@NoArgsConstructor
@Data
@AllArgsConstructor
class User implements java.io.Serializable {
private String id;
private String name;
}
7.RedisApplication.java 启动类
package cn.sunxiansheng.redis;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Description: RedisApplication
*
* @Author sun
* @Create 2025/1/19 19:33
* @Version 1.0
*/
@SpringBootApplication
public class RedisApplication {
public static void main(String[] args) {
SpringApplication.run(RedisApplication.class, args);
}
}
8.测试RString
1.设置缓存值
2.获取缓存值
6.common-rabbitmq-starter
1. 功能介绍
1.自定义消息转换器
- 发送消息时根据数据类型动态设置
Content-Type
(application/json
或text/plain
)。 - 接收消息时根据
Content-Type
自动反序列化为 Java 对象或字符串。
2.增强的可扩展性
- 自定义消息转换器覆盖了默认的 RabbitMQ 消息转换器,实现了更加灵活的消息处理逻辑。
- 自动处理不支持的消息类型,抛出清晰的异常提示。
3.与 Spring Boot 高度集成
- 在
RabbitAutoConfiguration
之前加载配置,确保自定义逻辑优先生效。 - 将自定义消息转换器应用到
RabbitTemplate
,方便开发者直接使用。
2. 配置示例
spring:
rabbitmq:
host: ${
RABBITMQ_HOST} # RabbitMQ 服务地址
username: ${
RABBITMQ_USERNAME} # 登录用户名
password: ${
RABBITMQ_PASSWORD} # 登录密码
virtual-host: / # 虚拟主机路径
port: ${
RABBITMQ_PORT} # RabbitMQ 服务端口
3.案例演示
1.创建模块
1.父模块
2.生产者 publisher
3.消费者 consumer
2.目录结构
3.父pom.xml
1.统一管理子模块
<!-- 统一管理子模块 -->
<packaging>pom</packaging>
<modules>
<module>publisher</module>
<module>consumer</module>
</modules>
2.基本配置
<!-- 通过properties来指定版本号 -->
<properties>
<!-- 指定编译版本 -->
<java.version>1.8</java.version>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- 指定Sunrays-Framework的版本 -->
<sunrays.version>1.0.0</sunrays.version>
</properties>
<dependencyManagement>
<!-- 使用sunrays-dependencies来管理依赖,则依赖无需加版本号 -->
<dependencies>
<dependency>
<groupId>cn.sunxiansheng</groupId>
<artifactId>sunrays-dependencies</artifactId>
<version>${sunrays.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
3.引入依赖
<dependencies>
<!-- common-rabbitmq-starter -->
<dependency>
<groupId>cn.sunxiansheng</groupId>
<artifactId>common-rabbitmq-starter</artifactId>
</dependency>
<!-- common-log4j2-starter 是必须引入的!!! -->
<dependency>
<groupId>cn.sunxiansheng</groupId>
<artifactId>common-log4j2-starter</artifactId>
</dependency>
<!-- 用来测试的Web模块 -->
<dependency>
<groupId>cn.sunxiansheng</groupId>
<artifactId>common-web-starter</artifactId>
</dependency>
<!-- env模块确保数据安全,可以不引入 -->
<dependency>
<groupId>cn.sunxiansheng</groupId>
<artifactId>common-env-starter</artifactId>
</dependency>
</dependencies>
4.publisher
1.application.yml 配置日志根目录、.env文件的绝对路径以及RabbitMQ
sun-rays:
log4j2:
home: /Users/sunxiansheng/IdeaProjects/sunrays-framework-demo/common-rabbitmq-starter-demo/publisher/logs # 日志根目录(默认./logs)
env:
path: /Users/sunxiansheng/IdeaProjects/sunrays-framework-demo/common-rabbitmq-starter-demo/publisher # .env文件的绝对路径
spring:
# RabbitMQ 配置
rabbitmq:
# 服务器地址 ip
host: ${
RABBITMQ_HOST}
# 用户名
username: ${
RABBITMQ_USERNAME}
# 密码
password: ${
RABBITMQ_PASSWORD}
# 虚拟主机
virtual-host: /
# 端口
port: ${
RABBITMQ_PORT}
server:
port: 8081
2..env 填写RabbitMQ的配置
RABBITMQ_HOST=host
RABBITMQ_USERNAME=用户名
RABBITMQ_PASSWORD=密码
RABBITMQ_PORT=端口
3.TestConfig.java 创建fanout类型的交换机和队列
package cn.sunxiansheng.publisher.config;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Description: 测试配置类
*
* @Author sun
* @Create 2024/12/31 19:00
* @Version 1.0
*/
@Configuration
public class TestConfig {
/**
* 创建一个fanout类型的交换机
*
* @return
*/
@Bean
public FanoutExchange fanoutExchange() {
return new FanoutExchange("fanout.exchange.testExchange");
}
/**
* 创建一个队列
*
* @return
*/
@Bean
public Queue fanoutQueue() {
return QueueBuilder.durable("testQueue") // 持久化队列
.lazy() // 惰性队列
.build();
}
/**
* 交换机和队列绑定
*/
@Bean
public Binding binding() {
return BindingBuilder.bind(fanoutQueue()).to(fanoutExchange());
}
}
4.TestConfigPublisher.java 发布对象类型的消息
package cn.sunxiansheng.publisher.pub;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* Description: 测试发布者
*
* @Author sun
* @Create 2024/12/31 19:05
* @Version 1.0
*/
@RestController
@Slf4j
public class TestConfigPublisher {
@Resource
private RabbitTemplate rabbitTemplate;
/**
* bean对象
*/
@Data
static class Person {
private String name;
private int age;
}
/**
* 发布对象类型的消息
*/
@RequestMapping("/send")
public void send() {
log.info("发送消息");
Person person = new Person();
person.setName("sun");
person.setAge(18);
rabbitTemplate.convertAndSend("fanout.exchange.testExchange", "", person);
}
}
5.PublisherApplication.java 启动类
package cn.sunxiansheng.publisher;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Description: PublisherApplication
*
* @Author sun
* @Create 2025/1/20 14:55
* @Version 1.0
*/
@SpringBootApplication
public class PublisherApplication {
public static void main(String[] args) {
SpringApplication.run(PublisherApplication.class, args);
}
}
5.consumer
1.application.yml 配置日志根目录、.env文件的绝对路径以及RabbitMQ
sun-rays:
log4j2:
home: /Users/sunxiansheng/IdeaProjects/sunrays-framework-demo/common-rabbitmq-starter-demo/consumer/logs # 日志根目录(默认./logs)
env:
path: /Users/sunxiansheng/IdeaProjects/sunrays-framework-demo/common-rabbitmq-starter-demo/consumer # .env文件的绝对路径
spring:
# RabbitMQ 配置
rabbitmq:
# 主机
host: ${
RABBITMQ_HOST}
# 用户名
username: ${
RABBITMQ_USERNAME}
# 密码
password: ${
RABBITMQ_PASSWORD}
# 虚拟主机
virtual-host: /
# 端口
port: ${
RABBITMQ_PORT}
server:
port: 8082
2..env 填写RabbitMQ的配置
RABBITMQ_HOST=host
RABBITMQ_USERNAME=用户名
RABBITMQ_PASSWORD=密码
RABBITMQ_PORT=端口
3.TestConfigConsumer.java 监听队列中的消息
package cn.sunxiansheng.consumer.con;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* Description: 测试消费者
*
* @Author sun
* @Create 2024/12/31 19:03
* @Version 1.0
*/
@Component
@Slf4j
public class TestConfigConsumer {
/**
* bean对象
*/
@Data
static class Person {
private String name;
private int age;
}
@RabbitListener(queues = "testQueue")
public void receive(Person person) {
String name = person.getName();
int age = person.getAge();
log.info("name:{}, age:{}", name, age);
}
}
4.ConsumerApplication.java 启动类
package cn.sunxiansheng.consumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Description: ConsumerApplication
*
* @Author sun
* @Create 2025/1/20 14:55
* @Version 1.0
*/
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
6.测试
1.发送消息
2.接受消息
7.总结
半年多的创作历程,每一行代码、每一个功能模块,都凝结了我无数个日日夜夜的努力与付出。从最初的构想到如今逐步完善的框架,我经历了无数的挑战,攻克了一个又一个技术难题,每一步都让我更加坚定要为开发者们带来更好、更高效的开发工具。
Sunrays-Framework 是我心血的结晶,它不仅包含了我对技术的探索,更承载了我对开发者的责任与承诺。在过去的半年里,我深入研究、精心打磨,将 MyBatis-Plus、Redis、RabbitMQ、Minio 等技术组件集成在其中,力求让框架既强大又易用,帮助开发者节省时间、提升效率,快速构建高质量的分布式系统。
尽管教程展示了框架的一个小部分,但框架的功能远远不止这些。它提供了 丰富的模块化设计、优雅的日志处理、高效的配置管理 等一系列功能,能在不同场景下满足开发者的需求。想要深入了解更多功能和最佳实践,官方文档是你必不可少的学习资源,它将带领你全面掌握框架的各个方面。
而这一切的实现离不开你们的支持与鼓励!我知道,每一个投票背后都是你们对我创作的认可和对开源精神的支持。你们的每一票,都是我继续前行的动力,是我不断优化、改进框架的不竭源泉。我会继续不断努力,让 Sunrays-Framework 成为每位开发者不可或缺的工具,为更多的开发者带来便利和提升。
最后,衷心感谢各位在百忙之中抽出宝贵时间阅读这篇文章,支持我,投票给我!你们的支持对我意义非凡,它不仅是对我过去半年来努力的肯定,更将成为我继续坚持下去的动力源泉。我相信,在大家的支持下,未来的 Sunrays-Framework 会更加完善和强大,帮助更多的开发者走得更远,做得更好。
每一份支持,都是我前进的动力,每一次反馈,都是我成长的机会。谢谢大家,真的感谢你们的每一票!