SpringBoot + LayUI 实战:从零构建一个简易内容管理后台(附源码)

发布时间:2026/7/15 23:39:30

SpringBoot + LayUI 实战:从零构建一个简易内容管理后台(附源码)
1. 为什么选择SpringBootLayUI搭建CMS后台最近在帮朋友改造一个内部使用的文章管理系统需求很明确要快速上线、维护简单、团队成员都能上手操作。在对比了多种技术方案后最终选择了SpringBootLayUI的组合。这个搭配可能不是最时髦的但绝对是中小型后台管理系统的最实用选择。先说SpringBoot它就像乐高积木的基础底板所有常用功能都给你预制好了。数据库连接一个starter搞定。接口开发注解配置简单到哭。我见过有新手用SpringBoot两天就搭出了可用的API服务这开发效率在传统SSM时代根本不敢想。而LayUI则是后台开发者的福音。作为典型的前端开箱即用框架它把表格、表单、弹窗这些后台高频组件都封装成了傻瓜式API。最让我心动的是它的文档示例——所有组件都有现成的代码片段直接复制粘贴就能跑起来。对于不擅长前端设计的后端开发者比如我来说简直是救命稻草。实测下来用这套技术栈开发一个基础CMS后台核心功能包括文章管理增删改查富文本编辑用户权限控制基于角色的菜单权限数据可视化简单的统计图表文件上传支持本地和云存储全部开发周期不超过3人/周。下面我就把实战中积累的经验和踩过的坑毫无保留地分享给大家。2. 项目初始化与基础配置2.1 创建SpringBoot项目推荐使用阿里云的start.aliyun.com来初始化项目比官方的start.spring.io访问更稳定。关键依赖选择WebSpring Web MVC核心Thymeleaf模板引擎版本选3.0.12以上MyBatis-Plus数据库操作比JPA更符合国人习惯Lombok简化实体类编写这是我的pom.xml核心配置dependencies !-- 基础依赖 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-thymeleaf/artifactId /dependency !-- 数据库相关 -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency !-- 工具类 -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies2.2 前端资源准备LayUI的引入有两种方式直接使用CDN适合快速原型开发下载本地资源推荐生产环境使用我选择下载2.8.0版本最后一个稳定版目录结构这样安排resources/ ├── static/ │ ├── layui/ # LayUI核心文件 │ ├── uploads/ # 上传文件目录 └── templates/ ├── admin/ # 后台页面 └── front/ # 前台页面可选在application.yml中配置关键参数spring: thymeleaf: prefix: classpath:/templates/ suffix: .html mode: HTML cache: false # 开发时关闭缓存 mvc: static-path-pattern: /static/** datasource: url: jdbc:mysql://localhost:3306/cms_db?useSSLfalse username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver server: port: 80803. 核心功能实现3.1 文章管理模块开发先建数据库表MySQL示例CREATE TABLE cms_article ( id bigint NOT NULL AUTO_INCREMENT, title varchar(100) NOT NULL COMMENT 标题, content longtext COMMENT 内容, cover_img varchar(255) DEFAULT NULL COMMENT 封面图, status tinyint DEFAULT 0 COMMENT 状态0-草稿 1-已发布, view_count int DEFAULT 0 COMMENT 浏览量, create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;对应的实体类用Lombok简化Data TableName(cms_article) public class Article { TableId(type IdType.AUTO) private Long id; private String title; private String content; private String coverImg; private Integer status; private Integer viewCount; private LocalDateTime createTime; private LocalDateTime updateTime; }Controller层实现增删改查Controller RequestMapping(/admin/article) public class ArticleController { Autowired private ArticleService articleService; // 文章列表页 GetMapping(/list) public String listPage() { return admin/article_list; } // 获取文章列表数据供LayUI表格渲染 ResponseBody PostMapping(/list) public TableDataInfo list(Article article, Page page) { PageArticle result articleService.selectPage(page, article); return TableDataInfo.build(result); } // 新增/编辑页 GetMapping(/edit/{id}) public String editPage(PathVariable(required false) Long id, Model model) { if (id ! null) { model.addAttribute(article, articleService.getById(id)); } return admin/article_edit; } // 保存操作 ResponseBody PostMapping(/save) public Result save(Valid Article article) { return articleService.saveOrUpdate(article) ? Result.success() : Result.error(保存失败); } }3.2 LayUI表格与表单集成在article_list.html中实现数据表格table classlayui-table idarticleTable lay-filterarticleTable/table script layui.use([table, form], function(){ var table layui.table; // 初始化表格 table.render({ elem: #articleTable, url: /admin/article/list, page: true, cols: [[ {field: id, title: ID, width:80}, {field: title, title: 标题}, {field: viewCount, title: 浏览量, width:100}, {field: status, title: 状态, width:100, templet: #statusTpl}, {field: createTime, title: 创建时间, width:180}, {fixed: right, title: 操作, width:150, align:center, toolbar: #barTpl} ]] }); // 工具条事件 table.on(tool(articleTable), function(obj){ var data obj.data; if(obj.event edit){ layer.open({ type: 2, title: 编辑文章, content: /admin/article/edit/ data.id, area: [800px, 600px] }); } else if(obj.event del){ layer.confirm(确认删除, function(index){ $.post(/admin/article/delete, {id: data.id}, function(res){ if(res.code 0){ table.reload(articleTable); } layer.msg(res.msg); }); }); } }); }); /script !-- 状态模板 -- script typetext/html idstatusTpl {{# if(d.status 1){ }} span classlayui-badge layui-bg-green已发布/span {{# } else { }} span classlayui-badge草稿/span {{# } }} /script !-- 操作按钮模板 -- script typetext/html idbarTpl a classlayui-btn layui-btn-xs lay-eventedit编辑/a a classlayui-btn layui-btn-danger layui-btn-xs lay-eventdel删除/a /script编辑页集成富文本编辑器form classlayui-form input typehidden nameid th:value${article?.id} div classlayui-form-item label classlayui-form-label标题/label div classlayui-input-block input typetext nametitle lay-verifyrequired th:value${article?.title} classlayui-input /div /div div classlayui-form-item label classlayui-form-label封面图/label div classlayui-input-block div classlayui-upload button typebutton classlayui-btn iduploadBtn上传图片/button div classlayui-upload-list img classlayui-upload-img idpreviewImg th:src${article?.coverImg} ?: input typehidden namecoverImg th:value${article?.coverImg} /div /div /div /div div classlayui-form-item label classlayui-form-label内容/label div classlayui-input-block textarea idcontent namecontent styledisplay:none; th:text${article?.content}/textarea /div /div div classlayui-form-item div classlayui-input-block button classlayui-btn lay-submit lay-filterformSubmit保存/button /div /div /form script layui.use([form, layedit, upload], function(){ var form layui.form; var layedit layui.layedit; var upload layui.upload; // 初始化富文本编辑器 var editorIndex layedit.build(content, { height: 300, uploadImage: { url: /admin/upload/image // 图片上传接口 } }); // 图片上传 upload.render({ elem: #uploadBtn, url: /admin/upload/image, done: function(res){ if(res.code 0){ $(#previewImg).attr(src, res.data.src); $(input[namecoverImg]).val(res.data.src); } } }); // 表单提交 form.on(submit(formSubmit), function(data){ data.field.content layedit.getContent(editorIndex); $.post(/admin/article/save, data.field, function(res){ layer.msg(res.msg); if(res.code 0){ setTimeout(function(){ parent.layer.closeAll(); parent.layui.table.reload(articleTable); }, 1500); } }); return false; }); }); /script4. 权限控制与系统优化4.1 基于拦截器的权限控制在CMS后台中简单的权限控制可以通过拦截器实现public class AuthInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String uri request.getRequestURI(); // 放行登录页和静态资源 if(uri.startsWith(/login) || uri.startsWith(/static)){ return true; } // 检查session HttpSession session request.getSession(); if(session.getAttribute(adminUser) null){ response.sendRedirect(/login); return false; } // 更精细的权限控制可以在这里实现 return true; } }注册拦截器Configuration public class WebConfig implements WebMvcConfigurer { Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new AuthInterceptor()) .addPathPatterns(/admin/**) .excludePathPatterns(/admin/login); } }4.2 性能优化技巧静态资源缓存生产环境开启Thymeleaf缓存spring: thymeleaf: cache: trueLayUI组件按需加载layui.config({ base: /static/layui/modules/ }).use([table, form, layedit], function(){ // 按需加载模块 });MyBatis-Plus性能优化Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); // 分页插件 interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 性能分析插件开发环境使用 if(isDev){ interceptor.addInnerInterceptor(new PerformanceInnerInterceptor()); } return interceptor; } }5. 项目部署与源码分享5.1 打包与部署使用SpringBoot的Maven插件打包mvn clean package -DskipTests生成的jar包可以直接运行java -jar cms-backend.jar --spring.profiles.activeprod推荐的生产环境配置# application-prod.yml spring: datasource: url: jdbc:mysql://db-server:3306/cms_prod?useSSLtrue username: prod_user password: ${DB_PASSWORD} # 从环境变量读取 thymeleaf: cache: true server: compression: enabled: true mime-types: text/html,text/css,application/javascript5.2 完整源码获取我把这个CMS后台的完整代码放在了Gitee上包含前后端完整实现数据库SQL脚本部署文档常见问题解答获取方式访问 [Gitee仓库地址] 克隆或下载zip包。项目采用MIT协议你可以自由地用于学习和商业项目。在实际开发中我还扩展了以下功能源码中都已包含七牛云文件上传集成数据导出Excel功能登录验证码支持操作日志记录遇到任何问题都可以在仓库的Issues区提问我会定期查看并回复。也欢迎提交Pull Request一起完善这个项目。

相关新闻

极简架构的缓存分层设计:浏览器、CDN、应用与数据库的全链路缓存策略

极简架构的缓存分层设计:浏览器、CDN、应用与数据库的全链路缓存策略

2026/7/15 23:29:29

极简架构的缓存分层设计:浏览器、CDN、应用与数据库的全链路缓存策略 一、同一条 SQL 被执行了 4700 次 对一个营销落地页做性能分析时发现:页面底部的"热门文章"模块每次请求都查询数据库,SQL 完全一样。日活 2 万的情况下&#x…

独立开发者的成本优化:云资源选型与 AI API 调用的精细化成本控制

独立开发者的成本优化:云资源选型与 AI API 调用的精细化成本控制

2026/7/15 23:29:29

独立开发者的成本优化:云资源选型与 AI API 调用的精细化成本控制 一、$320/月的账单中,71% 花在了"用不到"的资源上 一个独立开发者的 SaaS 工具,AWS 月账单 $320。细看账单明细:RDS t3.medium 每月 $85,EC…

Agent 推理链的性能剖析:从 Token 消耗到工具调用延迟的端到端优化

Agent 推理链的性能剖析:从 Token 消耗到工具调用延迟的端到端优化

2026/7/15 23:29:29

Agent 推理链的性能剖析:从 Token 消耗到工具调用延迟的端到端优化 一、一个 Agent 推理的"隐形账单" 对一个代码生成 Agent 做性能剖析时,发现了几个反直觉的数据: 模型推理耗时 3.2 秒,但端到端延迟 8.7 秒——其中 5…

开源新纪元:深度解析小米 MiMo Code 的技术架构与实战应用

开源新纪元:深度解析小米 MiMo Code 的技术架构与实战应用

2026/7/16 0:59:33

开源新纪元:深度解析小米 MiMo Code 的技术架构与实战应用 在当今的大模型领域,参数量的竞争似乎已不再是唯一的焦点,社区正在将目光转向模型的推理能力与实际落地效果。近期,一个名为 MiMo Code 的项目悄然登上各大技术社区的热榜…

PD端内部的POE电源变压器

PD端内部的POE电源变压器

2026/7/16 0:59:33

PoE电源变压器是PoE受电设备(PD)内部DC-DC电路里的隔离功率变压器:把网线送来的37~57V直流隔离降压成3.3V、5V、12V等低压,同时提供IEEE 802.3强制要求的1500Vrms电气隔离。它只转换能量、不传数据,和网络变…

OpenAI将核心引擎装进对手Claude Code,开启AI编程工作流新竞争模式

OpenAI将核心引擎装进对手Claude Code,开启AI编程工作流新竞争模式

2026/7/16 0:59:33

OpenAI与Claude Code联手:打造双模型编程新工作流今年3月30日,OpenAI做出惊人之举,为竞争对手Anthropic旗下的编程工具Claude Code发布官方插件。开发者安装后,只需一条命令,Claude就能负责管理任务、拆分需求、审核输…

GitHub、Vercel 和 Firestore 技术栈:提升开发速度、消除运维烦恼的“免运维”之选

GitHub、Vercel 和 Firestore 技术栈:提升开发速度、消除运维烦恼的“免运维”之选

2026/7/16 0:59:33

为速度而生的“免运维”技术栈如今,应用开发者可从众多架构解决方案中做选择,云计算革命中涌现的强大折中技术已成熟,这里探讨整合 GitHub、Vercel 和 Firestore 三项技术。它们结合能满足多应用场景需求,缩短开发与部署距离。若追…

免费开源 WhatCable 应用:苹果硅基 Mac 识别 USB - C 线缆规格,还能解锁专业功能!

免费开源 WhatCable 应用:苹果硅基 Mac 识别 USB - C 线缆规格,还能解锁专业功能!

2026/7/16 0:59:33

ZDNET 要点总结USB - C 线缆外观看似相同,规格却可能差异巨大。WhatCable 能在苹果硅基 Mac 上免费读取线缆规格。经测试,有几根线缆实际规格与标注不符。USB - C 的一大问题是,不管是包装盒里仅用于充电的免费线缆,还是花 159 美…

每日 AI 研究简报 · 2026-07-15

每日 AI 研究简报 · 2026-07-15

2026/7/16 0:49:32

(本文借助 AI 大模型及工具辅助整理) 一句话总结:OpenAI 首款硬件设备曝光为无屏可移动智能音箱,苹果智能在中国获批整合阿里百度模型,AI 电力成本开始显现——数据中心将导致美国 13 州电价上涨 63 亿美元。 &#x…

Unity游戏文本翻译架构深度解析:XUnity.AutoTranslator的技术实现与工程实践

Unity游戏文本翻译架构深度解析:XUnity.AutoTranslator的技术实现与工程实践

2026/7/16 0:35:09

Unity游戏文本翻译架构深度解析:XUnity.AutoTranslator的技术实现与工程实践 【免费下载链接】XUnity.AutoTranslator 项目地址: https://gitcode.com/gh_mirrors/xu/XUnity.AutoTranslator XUnity.AutoTranslator作为Unity游戏社区中最成熟的文本翻译解决方…

openEuler Raspberry Pi Kernel设备驱动开发指南:为树莓派硬件添加支持

openEuler Raspberry Pi Kernel设备驱动开发指南:为树莓派硬件添加支持

2026/7/13 20:43:19

openEuler Raspberry Pi Kernel设备驱动开发指南:为树莓派硬件添加支持 【免费下载链接】raspberrypi-kernel It provides openEuler kernel source for Raspberry Pi 项目地址: https://gitcode.com/openeuler/raspberrypi-kernel 前往项目官网免费下载&…

openEuler系统集成测试实战:基于smoke-test套件的环境验证技巧

openEuler系统集成测试实战:基于smoke-test套件的环境验证技巧

2026/7/15 0:26:43

openEuler系统集成测试实战:基于smoke-test套件的环境验证技巧 【免费下载链接】integration-test The repo contains test suits for system integration test 项目地址: https://gitcode.com/openeuler/integration-test 前往项目官网免费下载:…

Python实现跨境电商商品图批量翻译教程

Python实现跨境电商商品图批量翻译教程

2026/7/16 0:09:31

一、问题引入做跨境电商的卖家朋友,你是否遇到过这样的困扰?每次上架新品到亚马逊、Shopee或Lazada等平台,都需要处理大量商品图片的多语言版本。比如上架200款衣服,每款需要翻译成英语、日语、韩语等5种语言,这意味着…

跨境电商多语言商品图翻译方案实现

跨境电商多语言商品图翻译方案实现

2026/7/16 0:09:31

一、问题引入对于做跨境电商的卖家来说,多语言商品图的制作一直是令人头疼的环节。当你准备在亚马逊、Shopee、Lazada等多个平台同步上架新品时,首先遇到的就是图片翻译问题。以一位做家居用品的卖家为例,他需要将200张商品图片中的英文文案全…

Windows系统文件d3dx9_36.dll丢失找不到问题解决

Windows系统文件d3dx9_36.dll丢失找不到问题解决

2026/7/16 0:09:31

在使用电脑系统时经常会出现丢失找不到某些文件的情况,由于很多常用软件都是采用 Microsoft Visual Studio 编写的,所以这类软件的运行需要依赖微软Visual C运行库,比如像 QQ、迅雷、Adobe 软件等等,如果没有安装VC运行库或者安装…