简介:这份文档为基于Java的校园博客系统设计与实现的完整毕业设计论文,面向计算机相关专业学生及需要开发博客类项目的开发者。内容覆盖系统分析、系统设计、功能实现与测试等完整流程,涉及Java、MySQL、Tomcat、Spring Boot等关键技术,包含可行性分析、性能分析、数据库E-R图与数据表字段设计、功能模块实现及测试结论,可作为课程设计或毕业设计的直接参考模板。资源为单个docx文件,压缩包大小4.91MB,已有62人学习下载。文档目录结构完整,从摘要、前言、开发技术介绍到系统测试、结论与致谢,便于快速定位各章节内容;结合具体功能模块与数据库设计,能够帮助读者快速理解校园博客系统的整体架构和开发思路,缩短设计开发周期。
1. 校园博客系统到底在做什么:一个Java课设背后的真实需求
“基于java的校园博客系统”是每届计算机专业学生都会撞上的课题,它听着像一个大作业模板,实际上踩中了Java Web开发的全套核心:前后端交互、数据库设计、权限控制、富文本处理、并发安全。如果你正在对着这个标题查资料,大概率是要做毕设或课程设计,也可能是在准备面试时遇到类似的“手写博客系统”题目——它几乎就是Java后端入门的标准试金石。这篇笔记不打算给你贴一份万能源码,而是把系统从建表到上线的完整路径拆开讲清楚,包括每段代码为什么这么写、参数怎么调、哪些地方会翻车。适合能跑通Hello World但没独立做过完整项目的新手,也适合想把这套课设改成简历项目的熟手。
2. 技术栈与架构设计:为什么Spring Boot + MySQL能撑起校园博客这套组合
2.1 从零选型:单体应用、Spring Boot与MyBatis的取舍
校园博客系统的典型用户量是几百到几千人,一台服务器加一个MySQL就绰绰有余,不需要分布式、不需要微服务。所以选型的核心逻辑是“用最成熟的方案解决最明确的问题”,而不是追新技术。常见做法是Spring Boot 2.x + MyBatis + MySQL + Thymeleaf(或Vue前后端分离),这个组合在校园课设里出现率最高,原因是资料多、面试常问、部署简单。
Spring Boot的价值在于它帮你把复杂的Spring配置收进了自动装配:内嵌Tomcat、约定好扫描路径、依赖版本统一管理。相比传统SSH(Spring + Struts + Hibernate)时代,现在的启动成本低了一个量级。MyBatis则比JPA更贴近SQL,学生手写SQL能清晰看到查询执行的每一步,调试时也容易定位问题。如果选JPA,Hibernate的懒加载和N+1问题会让新手一头扎进黑匣子;MyBatis至少翻车时你能看到SQL长什么样。
这里有个常见争论:要不要用Spring Cloud?我一般会劝退。校园博客系统的业务复杂度撑不起微服务的成本,引入Nacos、Feign、Sentinel后反而会让课设答辩变成事故现场——你不光要解释业务,还要解释为什么用这些重组件。同样的道理,Redis可以做,但不是必须;如果为了给简历加分,可以加一个简单的缓存层,但务必将缓存失效和穿透问题处理干净,否则面试官追问会露馅。
2.2 核心功能模块划分与数据流向
校园博客系统按角色拆,一般有游客、注册用户、管理员三类。游客只能查看文章列表和详情;注册用户能发博客、评论、点赞、管理自己的文章;管理员能删帖、封号、查看文章审核。数据流向是典型的单体分模块:Controller接收请求 → Service处理业务 → Mapper操作数据库 → 返回视图或JSON。
| 模块 | 核心功能 | 对应数据表 |
|---|---|---|
| 用户模块 | 注册、登录、密码重置 | user |
| 文章模块 | 发表、编辑、删除、分页列表 | article, category |
| 评论模块 | 发表评论、删除评论、嵌套回复 | comment |
| 互动模块 | 点赞/取消点赞、阅读量统计 | like_record, article.read_count |
| 管理后台 | 用户管理、文章审核、数据统计 | user.role, audit_log |
注意数据流里的“阅读量统计”是个容易被忽略的坑。如果每次请求都UPDATE article SET read_count = read_count + 1,高并发下会锁行;如果先用Redis计数再定时回写,又增加了部署难度。课设阶段我建议直接UPDATE,只要在MySQL连接串上加上乐观锁参数,并把文章表调整为InnoDB,就足够应付几百并发。真正的压力问题等你上线后再优化,这属于“值不值得做”的边界——为课设投入Redis维护时间,性价比不高。
模块间的依赖方向要控制住:Service层只依赖Mapper,Controller层只依赖Service,禁止跨层调用MyBatis的Session。很多新手图省事,在Controller里直接注入Mapper,初期开发快,后面一旦要加日志、权限就很痛苦,重构时想哭的心都有。
3. 搭建项目骨架与数据库:从建库SQL到MyBatis映射的完整起步
3.1 用Spring Initializr生成项目:目录结构与依赖说明
不要手动新建Maven项目然后一个个填依赖,直接去Spring Initializr(start.spring.io)生成一个zip包。选择Java 8或Java 11、Spring Boot 2.7.x(选择主流稳定版,不要用3.x,因为3.x要求Java 17并且javax包名变成jakarta,很多旧教程会失效)。依赖勾选以下四个:Spring Web、MyBatis Framework、MySQL Driver、Thymeleaf。如果你想做前后端分离,就加Spring Web + Validation,前端用Vue单独建工程,但课设答辩时单体的Thymeleaf更省事——直接跑一个包就是完整站点。
生成后目录结构默认如下:
src/main/java/com/example/campusblog ├── CampusBlogApplication.java ├── controller/ // 控制器层 ├── service/ // 业务层(接口+实现) ├── mapper/ // MyBatis Mapper接口 ├── entity/ // 数据库实体类 ├── config/ // 配置类(拦截器、WebMvc配置) └── utils/ // 工具类(MD5、JWT等) src/main/resources ├── static/ // 静态资源 ├── templates/ // Thymeleaf模板页面 └── application.yml // 配置文件 src/test/java // 测试目录启动类默认就有,不用改。关键在于application.yml里的配置,这里直接决定你后续能不能连上数据库:
server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/campus_blog?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false username: root password: your_password thymeleaf: cache: false mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.campusblog.entity configuration: map-underscore-to-camel-case: true logging: level: com.example.campusblog.mapper: debug参数说明:serverTimezone=Asia/Shanghai必须加,否则连接MySQL 8.x会报时区错误;map-underscore-to-camel-case设为true后,数据库字段read_count能自动映射到实体的readCount,省去大量@Results注解;logging.level.mapper: debug会在控制台打印所有SQL,开发期务必打开,这是你排查“查不到数据”最直接的手段。注意MyBatis的mapper-locations路径,如果你把XML放在resources/mapper下就用这个配置;如果你改成注解SQL,这个配置就不需要,但我建议项目用XML——复杂动态SQL只有XML才写得清楚。
3.2 建表SQL:用户、文章、评论、点赞四张核心表
数据库名建议用campus_blog,字符集统一utf8mb4。utf8mb4比utf8多支持emoji和生僻字,校园博客里用户昵称千奇百怪,用utf8会在插入某些字符时直接报错。以下是四张核心表的建设脚本:
CREATE TABLE `user` ( `id` bigint NOT NULL AUTO_INCREMENT, `username` varchar(50) NOT NULL COMMENT '登录名', `password` varchar(64) NOT NULL COMMENT 'MD5加盐后的密码', `nickname` varchar(50) DEFAULT NULL, `avatar_url` varchar(255) DEFAULT NULL, `role` tinyint NOT NULL DEFAULT '1' COMMENT '0-管理员 1-普通用户', `status` tinyint NOT NULL DEFAULT '1' COMMENT '1-正常 0-禁用', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_username` (`username`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='用户表'; CREATE TABLE `article` ( `id` bigint NOT NULL AUTO_INCREMENT, `user_id` bigint NOT NULL, `category_id` int DEFAULT NULL, `title` varchar(200) NOT NULL, `content` longtext NOT NULL COMMENT '富文本HTML', `summary` varchar(500) DEFAULT NULL, `read_count` int NOT NULL DEFAULT '0', `like_count` int NOT NULL DEFAULT '0', `status` tinyint NOT NULL DEFAULT '1' COMMENT '0-草稿 1-发布 2-审核中', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_user_id` (`user_id`), KEY `idx_category_id` (`category_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章表'; CREATE TABLE `comment` ( `id` bigint NOT NULL AUTO_INCREMENT, `article_id` bigint NOT NULL, `user_id` bigint NOT NULL, `parent_id` bigint DEFAULT '0' COMMENT '0-顶级评论,非0为回复某条评论', `content` varchar(1000) NOT NULL, `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_article_id` (`article_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='评论表'; CREATE TABLE `like_record` ( `id` bigint NOT NULL AUTO_INCREMENT, `article_id` bigint NOT NULL, `user_id` bigint NOT NULL, `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_article_user` (`article_id`,`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='点赞记录表,防止重复点赞';设计说明:user.password字段存的是加盐后的MD5值,不是明文;article.content用longtext,因为富文本的HTML可能很大,text最多64KB,写一篇带代码高亮的文章很容易超;like_record的唯一键uk_article_user从数据库层面保证同一用户不能对同一文章重复点赞,这比在Service层先查询再插入更可靠——并发下查询再插入会产生两条记录,唯一键会直接让第二条插入报错,你捕获异常即可。comment.parent_id支持嵌套回复,但深度不要超过两层,校园博客场景两层足够。
另外,记得给所有表的create_time都加上DEFAULT CURRENT_TIMESTAMP,这样插入数据时不显式赋值也能自动写入时间,避免Java代码里到处System.currentTimeMillis()。
3.3 MyBatis Generator与手写Mapper:什么时候用代码生成器
项目里entity和mapper可以直接手写,毕竟只有四张表,代码量不大;但如果你习惯用MyBatis Generator,也可以用它生成基础CRUD,然后再手工补充复杂查询。用Generator的最大好处是实体类字段不用手敲,但生成的XML会包含大量用不上的resultMap和Example类,这些代码会让项目显得冗余。课设答辩时老师会问“这个ResultMap里为什么有version字段”,你要是答不上来会被质疑代码是否自己写的。
我一般建议:实体类手写,Mapper接口手写,XML手写,但基础的单表查询用@Select注解写在接口上,只有动态条件查询才放到XML里。这样避免了一开始就被Generator生成的几百行XML淹没。比如用户登录查询,一行注解就能解决:
@Mapper public interface UserMapper { @Select("SELECT * FROM user WHERE username = #{username} AND status = 1") User findActiveByUsername(String username); }当你后续需要“按关键词分页搜索文章,同时过滤状态和分类”时,再在XML里写动态SQL。原则是:简单查询用注解,复杂查询用XML。这个边界能让你保持代码整洁,也方便面试时讲解每个方法的SQL逻辑。
4. 核心业务实现:注册登录、发布博客、评论点赞的代码级拆解
4.1 注册登录:MD5加盐与Spring Security的轻量替代方案
网上很多校园博客项目用的是Spring Security + JWT,但Spring Security的过滤器链配置对新手极不友好,一个SecurityConfig能调一整天。我做课设时更推荐轻量方案:用拦截器校验登录状态,密码用MD5加盐存储,登录成功后把User对象存在Session里。这套方案代码量少、逻辑透明,面试时也能讲清楚。
注册接口的Service代码如下:
@Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Override public Result register(UserRegisterDTO dto) { // 1. 校验用户名唯一 if (userMapper.findByUsername(dto.getUsername()) != null) { return Result.error("用户名已存在"); } // 2. 密码加盐后MD5,盐用固定字符串+用户名的组合 String salt = "campus_blog_" + dto.getUsername(); String encrypted = DigestUtils.md5DigestAsHex((salt + dto.getPassword()).getBytes()); // 3. 保存用户 User user = new User(); user.setUsername(dto.getUsername()); user.setPassword(encrypted); user.setNickname(dto.getNickname()); userMapper.insert(user); return Result.success("注册成功"); } @Override public User login(String username, String password) { User user = userMapper.findByUsername(username); if (user == null) { throw new BusinessException("用户不存在"); } String salt = "campus_blog_" + username; String encrypted = DigestUtils.md5DigestAsHex((salt + password).getBytes()); if (!encrypted.equals(user.getPassword())) { throw new BusinessException("密码错误"); } if (user.getStatus() == 0) { throw new BusinessException("账号已被禁用"); } return user; } }逻辑说明:加盐方式很多种,这里用“固定前缀+用户名”当盐,避免两次注册相同用户名产生相同哈希(虽然用户名唯一,但加盐后更安全)。真正的生产系统会用BCrypt,且盐是随机生成的,但MD5加盐对课设足够,答辩时如果你主动说“我知道MD5不够强,生产上应该用BCrypt”,会加分。注册时没有对密码做二次确认,DTO里用@NotBlank注解让他完成前端和Controller验证。
登录成功后的会话处理,写一个拦截器最直观:
public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (request.getSession().getAttribute("loginUser") == null) { // 判断是否为AJAX请求,是则返回401状态码,否则重定向到登录页 if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) { response.setStatus(401); } else { response.sendRedirect("/login"); } return false; } return true; } }参数说明:这里用了Session而不是Token,好处是登录状态由服务端控制,退出时直接session.invalidate()就行,不用处理Token过期。键名loginUser是约定的,后续在Thymeleaf模板中通过${session.loginUser.nickname}取昵称。AJAX判断很关键,否则前端发ajax请求被拦截后返回的是整个登录页HTML,前端解析不了。
4.2 发布博客与富文本处理:XSS过滤与HTML白名单
发布博客是文章模块的核心。前端我用的是一套开源的富文本编辑器,传递过来的content是一段带样式的HTML。存入数据库前必须做XSS过滤——否则用户在内容里写<script>alert('xss')</script>,每次有人看这篇文章就会执行他的脚本,轻则弹窗,重则窃取登录态。这里推荐使用Jsoup这个Java库来清洗HTML。
public String cleanContent(String content) { // 使用Jsoup的Safelist配置白名单,只允许基本文本和排版标签 Safelist safelist = Safelist.relaxed() .addTags("code", "pre", "blockquote") .addAttributes("p", "style"); return Jsoup.clean(content, safelist); }逻辑说明:Safelist.relaxed()默认允许a、b、i、img等常见标签,但会去掉class、id、style等属性。校园博客经常要贴代码,所以手动加上code和pre标签;如果你允许用户自定义文字颜色,就给p标签增加style属性。注意,iframe和script标签默认不在白名单里,会被Jsoup自动移除,这就能挡住大部分恶意脚本。这个clean方法在Service层调用,在article插入前对content执行一次。
发布文章时还有两个参数需要关注。一个是summary摘要,如果没有手动填写,就从content中提取纯文本前200个字符。用Jsoup.parse(content).text()可以轻松去除HTML标签拿纯文本。另一个是status状态位:默认存为1(发布),如果你加了管理员审核机制,就先存为2(审核中)。课设阶段我建议直接发布,不要给自己增加审核需求——审核意味着后台页面、操作日志、状态流转,复杂度直线上升,除非你的课题明确要求。
文章分页查询是另一个高频点,常见做法是用PageHelper插件:
PageHelper.startPage(pageNum, pageSize); List<ArticleVO> list = articleMapper.selectByCondition(title, categoryId, status); PageInfo<ArticleVO> pageInfo = new PageInfo<>(list);参数说明:PageHelper.startPage(pageNum, pageSize)必须在查询方法的第一行调用,它只作用于下一条SQL。注意返回的list是Page类型,直接用PageInfo包装就能得到总页数、当前页、是否首页等字段。常见翻车是:在startPage之前执行了其他Mapper查询,导致PageHelper拦截错了SQL,所以Service里要把分页放在该方法的第一个Mapper调用之前。PageHelper分页后返回的集合必须new PageInfo<>(list)获取总数,直接用list.size()会是当前页条数而不是总记录数。
4.3 评论与点赞:事务边界与并发控制
评论和点赞是典型的“写多读少”交互,涉及两张表的状态同步,最容易出现数据不一致。先看评论插入的Service:
@Transactional(rollbackFor = Exception.class) public void addComment(CommentDTO dto, Long userId) { Article article = articleMapper.selectById(dto.getArticleId()); if (article == null) { throw new BusinessException("文章不存在"); } Comment comment = new Comment(); comment.setArticleId(dto.getArticleId()); comment.setUserId(userId); comment.setContent(dto.getContent()); comment.setParentId(dto.getParentId() == null ? 0 : dto.getParentId()); commentMapper.insert(comment); // 更新文章的评论数量,如果评论表有comment_count字段则更新,否则可省略 }逻辑说明:这里的@Transactional保证“插入评论”和“更新文章评论数”要么都成功,要么都失败。rollbackFor = Exception.class很重要——Spring事务默认只回滚RuntimeException和Error,如果你在业务里抛的是自定义BusinessException(继承自RuntimeException)没问题,但万一抛的是受检异常,不加这个参数事务不会回滚,数据库就插入了一条评论但文章计数没变。这条血泪经验在面试和答辩中经常被问到。
点赞功能则需要处理“重复点赞”和“取消点赞”两种操作。由于数据库有like_record的唯一键约束,可以直接用插入结果判断:
@Transactional(rollbackFor = Exception.class) public boolean toggleLike(Long articleId, Long userId) { try { LikeRecord record = new LikeRecord(); record.setArticleId(articleId); record.setUserId(userId); likeRecordMapper.insert(record); articleMapper.incrementLikeCount(articleId); return true; // 点赞成功 } catch (DuplicateKeyException e) { // 记录已存在,说明要取消点赞 likeRecordMapper.deleteByArticleIdAndUserId(articleId, userId); articleMapper.decrementLikeCount(articleId); return false; } }逻辑说明:用try-catch捕获唯一键冲突,把“是否已点赞”的判断交给数据库,而不是先select再insert。先查再插在并发下会产生两条记录,虽然唯一键最终会阻止,但会多一次无效的数据库往返。DuplicateKeyException是Spring翻译过的异常,能被@Transactional捕获并继续执行后续操作,这要求articleMapper.incrementLikeCount和delete在同一个事务里,否则取消点赞时更新计数失败会导致脏数据。注意高并发下like_count字段的更新会有线程竞争,课设阶段靠MySQL行锁足够,不需要Redis分布式锁——那属于过度设计。
评论列表的查询性能也需要优化。如果文章有几千条评论,一次全查出来再在Java内存里组装树形结构会很卡。常见做法是分两级查询:先查顶级评论(parent_id = 0)用LIMIT分页,再按顶评论id批量查子评论,在内存里组装。SQL用WHERE parent_id IN (...)来避免N+1查询。
5. 避坑指南:校园博客系统从开发到上线的5个高频翻车点
5.1 翻车点一:MySQL连接时区报错,一启动就红字
现象:Spring Boot启动时报The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized,数据库连接不上。原因:MySQL 8.x驱动的时区识别与系统默认时区不一致。解决:在JDBC连接串里加serverTimezone=Asia/Shanghai,同时确保数据库端的全局时区也设置了,执行SET GLOBAL time_zone = '+8:00'。这个坑几乎每个搭环境的人都会遇到,网上教程往往没写这条,属于“环境配置教程”遗漏的典型细节。
5.2 翻车点二:Thymeleaf页面端口下找不到静态资源
现象:页面能打开,但CSS、JS全都404。原因:Spring Boot默认把resources/static下的资源映射到根路径,但当Controller的请求路径是/admin/**时,Thymeleaf模板里用相对路径../static/css/style.css引错了位置。解决:模板中统一使用Thymeleaf的@{}表达式:th:href="@{/css/style.css}",这样不管Controller在哪层都能正确解析。另外,检查application.yml中spring.mvc.static-path-pattern是否被改成自定义路径,如果改了就换回来。
5.3 翻车点三:MyBatis查询结果全是null,但SQL控制台能查到数据
现象:控制台打印SQL返回一条记录,但Java对象里所有字段都null。原因:数据库字段使用下划线命名(read_count),实体类用驼峰(readCount),没有开启map-underscore-to-camel-case。解决:在application.yml的mybatis.configuration里配置map-underscore-to-camel-case: true。如果你是用MyBatis Generator生成的XML,那resultMap已经手动映射过了,不会出现这个问题;但手写@Select注解查询时最容易漏这个开关。另一个小坑:实体类中字段是Integer readCount,数据库read_count为NULL时,MyBatis会直接赋值null,Java里不会自动变成0,所以你需要在实体类定义时用private Integer readCount = 0;初始化,或者在SQL用IFNULL(read_count, 0)。
5.4 翻车点四:发布文章后内容里的图片不显示
现象:文章列表能显示标题,点进详情页后图片裂开。原因:富文本编辑器上传图片时返回的是相对路径(比如/upload/2024/01/xx.png),文章详情页是/article/detail/12这个URL,浏览器会把相对路径解析成/article/upload/...,自然访问不到。解决:发布文章时,在Service层把内容里的src="/upload替换成src="http://你的域名/upload,或者给图片加上完整的上下文路径。更稳妥的做法是上传图片时直接返回完整URL,这样无论文章被导出或分享到哪,图片都不会丢。这个坑用编辑器自带的图片上传接口调试时特别常见,你需要在后端文件上传接口返回数据里加上serverAddress + "/upload/" + relativePath。
5.5 翻车点五:点赞计数与记录表不一致,用户取消点赞后数字不更新
现象:用户点击取消点赞,like_record记录删了,但文章上的like_count没减。原因:toggleLike方法里likeRecordMapper.delete和articleMapper.decrementLikeCount没有在同一个事务中,因为类上没有@Transactional,或者异常被吞掉了。解决:确保类级别或方法级别有@Transactional(rollbackFor = Exception.class),同时检查articleMapper.decrementLikeCount的SQL是否写了UPDATE article SET like_count = like_count - 1 WHERE id = ?,如果之前写过like_count - 1但忘了加WHERE,所有文章都会被减一遍。这个翻车现场我见过好几个组员踩过,还是那句话——事务边界是你自己控制的,别依赖运气。
6. 上线前的进阶验证:日志、压测与部署,让课设真正能跑起来
如果你的目标是让这个校园博客系统成为简历上的“真实项目”,只跑通本地还不够。至少要完成三项验证:第一,统一日志记录,推荐在Service层用@Slf4j打印关键操作日志,比如用户注册、文章发布、异常堆栈;第二,用JMeter或Postman的Runner做一轮简单的并发压测,重点接口就是登录和文章分页,观察QPS和错误率,这个数据放到简历上比“功能完成”更有说服力;第三,部署到云服务器,用java -jar运行打包后的jar包,配一个Nginx做反向代理和静态资源缓存,再把数据库连接串改成服务器的内网地址。
我的习惯是最后检查一遍这三个地方:Session是否设置了过期时间(建议30分钟),文件上传目录是否隔离开了,还有没有在代码里硬编码数据库密码。记住Spring Boot的默认配置文件中如果含有真实密码,打包后也会被解压看到,最好通过环境变量传入,比如password: ${DB_PASSWORD},部署时在启动命令里加--DB_PASSWORD=xxx。这个习惯不能丢,等你的项目上线后被人扫到数据库明文密码,那才是真正的“后悔药没处买”。
最后给你一个实操技巧:在application.yml里把spring.thymeleaf.cache设为false,本地开发修改页面立刻生效,部署时记得改回true,否则缓存把页面活着,你改了代码看不到效果会一头雾水。还有,用mvn clean package -DskipTests打jar包时,如果出现端口被占用,先查lsof -i:8080或netstat -ano,别急着改端口。这套流程走下来,你的校园博客系统既能应付答辩,也能经得住面试官一问到底。希望帮到你。
本文还有配套的精品资源,点击获取