啥? 前端也要会干Java?🛵🛵🛵

Spring Boot + Vue3 个人博客系统

前言

欲渡黄河冰塞川,将登太行雪满山。

------李白《行路难》

有时候会思考,AI 给人类带来了便利、效率、社会的进步,但是真的有让我们生活更美好吗?作为前端,想必大家也感受到了冲击。想着尝试下 Java,下面是搭建的一个博客系统,主要介绍后端基础知识。


一、项目简介

这是一个前后端分离的个人博客系统。后端基于 Java 21 + Spring Boot 3 + Spring Data JPA + MySQL 提供 RESTful API;前端使用 Vue 3 + Vite + Pinia 构建页面,通过 Axios 调用后端接口完成文章的增删改查。

整体采用经典的三层架构:

bash 复制代码
浏览器(Vue3 前端)
        │
        │  HTTP 请求  /api/articles
        ▼
Controller  控制器层   ← 定义 API 路由,接收/返回 JSON
        │
        ▼
Service     业务层     ← 处理业务逻辑、事务控制
        │
        ▼
Repository  数据层     ← JPA 操作数据库
        │
        ▼
MySQL 数据库(blog 库,article 表)

二、技术栈

类别 技术 说明
语言 Java 21 项目编译与运行版本
框架 Spring Boot 3.4.5 快速构建 Web 应用
ORM Spring Data JPA + Hibernate 对象关系映射,免写大部分 SQL
数据库 MySQL 8 持久化存储
校验 Jakarta Validation 请求参数自动校验
工具 Lombok 减少 getter/setter 等样板代码
构建 Maven 依赖管理与打包

前端(简要):Vue 3、Vue Router、Pinia、Axios、Vite,开发端口 5173,通过代理将 /api 转发到后端 8080


三、后端目录结构

bash 复制代码
backend/
├── pom.xml                              # Maven 依赖配置
├── mvnw / mvnw.cmd                      # Maven Wrapper,无需全局安装 Maven
└── src/main/
    ├── java/com/blog/
    │   ├── BlogBackendApplication.java  # 程序入口
    │   ├── config/
    │   │   └── CorsConfig.java          # 跨域配置
    │   ├── controller/
    │   │   └── ArticleController.java   # 文章 API 接口
    │   ├── service/
    │   │   └── ArticleService.java      # 文章业务逻辑
    │   ├── repository/
    │   │   └── ArticleRepository.java   # 数据访问接口
    │   ├── entity/
    │   │   └── Article.java             # 数据库实体(表映射)
    │   ├── dto/
    │   │   └── ArticleRequest.java      # 请求参数对象
    │   ├── common/
    │   │   └── ApiResponse.java         # 统一响应格式
    │   └── exception/
    │       └── GlobalExceptionHandler.java  # 全局异常处理
    └── resources/
        └── application.yml              # 应用配置(端口、数据库等)

四、各 Java 文件详解

1. BlogBackendApplication.java --- 启动入口

java 复制代码
@SpringBootApplication
public class BlogBackendApplication {
    public static void main(String[] args) {
        SpringApplication.run(BlogBackendApplication.class, args);
    }
}

@SpringBootApplication 是一个组合注解,等价于开启自动配置、组件扫描和配置类注册。执行 main 方法后,Spring 容器启动,自动发现并注册所有带 @RestController@Service@Configuration 等注解的 Bean。

启动命令:

bash 复制代码
cd backend
.\mvnw.cmd spring-boot:run

服务默认监听 8080 端口。


2. application.yml --- 核心配置

yaml 复制代码
server:
  port: 8080

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/blog?...
    username: root
    password: 你的密码
  jpa:
    hibernate:
      ddl-auto: update    # 根据 Entity 自动建表/更新表结构
    show-sql: true        # 控制台打印 SQL,便于调试
    open-in-view: false   # 关闭 OSIV,避免懒加载问题延伸到 Web 层

关键配置说明:

  • ddl-auto: update :首次启动时根据 Article 实体自动创建 article 表,字段变更时自动同步,开发阶段非常方便。
  • show-sql: true:在终端输出 Hibernate 生成的 SQL,便于排查问题。
  • open-in-view: false:推荐的生产实践,事务在 Service 层结束,避免在 Controller 渲染时才触发数据库查询。

3. entity/Article.java --- 数据库实体

实体类与数据库表 article 一一对应,是 JPA 操作的核心对象。

java 复制代码
@Data
@Entity
@Table(name = "article")
public class Article {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;                          // 主键,自增

    @Column(nullable = false, length = 200)
    private String title;                     // 标题,最长 200 字符

    @Column(nullable = false, columnDefinition = "TEXT")
    private String content;                   // 正文,TEXT 类型

    @Column(length = 500)
    private String summary;                   // 摘要,可选

    @Column(nullable = false, length = 50)
    private String author;                    // 作者

    @CreationTimestamp
    @Column(nullable = false, updatable = false)
    private LocalDateTime createdAt;          // 创建时间,插入时自动填充

    @UpdateTimestamp
    @Column(nullable = false)
    private LocalDateTime updatedAt;          // 更新时间,每次修改自动刷新
}

注解含义:

注解 作用
@Entity 声明为 JPA 实体
@Table(name = "article") 映射到 article
@Id + @GeneratedValue 主键 + 数据库自增策略
@Column 列约束(非空、长度、类型)
@CreationTimestamp Hibernate 在插入时自动写入当前时间
@UpdateTimestamp Hibernate 在更新时自动刷新时间
@Data(Lombok) 自动生成 getter/setter/toString 等

注意:createdAtupdatedAt 由 Hibernate 管理,前端创建/更新文章时不需要传入这两个字段。


4. dto/ArticleRequest.java --- 请求参数对象

DTO(Data Transfer Object)用于接收前端传来的 JSON,与 Article 实体分离,避免把数据库字段(如 id、时间戳)暴露给外部随意修改。

java 复制代码
@Data
public class ArticleRequest {

    @NotBlank(message = "标题不能为空")
    @Size(max = 200, message = "标题长度不能超过200")
    private String title;

    @NotBlank(message = "内容不能为空")
    private String content;

    @Size(max = 500, message = "摘要长度不能超过500")
    private String summary;

    @NotBlank(message = "作者不能为空")
    @Size(max = 50, message = "作者长度不能超过50")
    private String author;
}

校验规则:

  • @NotBlank:不能为 null、空字符串或纯空格
  • @Size(max = N):字符串最大长度限制

Controller 方法参数加上 @Valid 后,校验失败会抛出 MethodArgumentNotValidException,由全局异常处理器统一返回 400 错误信息。


5. repository/ArticleRepository.java --- 数据访问层

java 复制代码
public interface ArticleRepository extends JpaRepository<Article, Long> {
}

继承 JpaRepository<Article, Long> 后,无需编写实现类,Spring Data JPA 在运行时自动生成代理,直接拥有以下方法:

方法 作用
findAll() 查询全部文章
findById(id) 按 ID 查询,返回 Optional<Article>
save(article) 新增或更新(有 id 则更新,无 id 则插入)
deleteById(id) 按 ID 删除
existsById(id) 判断记录是否存在

如需复杂查询(按作者筛选、分页等),可在此接口中按命名规则添加方法,例如 List<Article> findByAuthor(String author)


6. service/ArticleService.java --- 业务逻辑层

Service 是业务核心,Controller 不直接操作数据库,所有逻辑在此完成。

java 复制代码
@Service
@RequiredArgsConstructor
public class ArticleService {

    private final ArticleRepository articleRepository;

    // 查询列表
    public List<Article> listAll() {
        return articleRepository.findAll();
    }

    // 查询详情,不存在则抛异常
    public Article getById(Long id) {
        return articleRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("文章不存在"));
    }

    // 创建文章
    @Transactional
    public Article create(ArticleRequest request) {
        Article article = new Article();
        copyProperties(request, article);
        return articleRepository.save(article);
    }

    // 更新文章
    @Transactional
    public Article update(Long id, ArticleRequest request) {
        Article article = getById(id);
        copyProperties(request, article);
        return articleRepository.save(article);
    }

    // 删除文章
    @Transactional
    public void delete(Long id) {
        if (!articleRepository.existsById(id)) {
            throw new RuntimeException("文章不存在");
        }
        articleRepository.deleteById(id);
    }

    private void copyProperties(ArticleRequest request, Article article) {
        article.setTitle(request.getTitle());
        article.setContent(request.getContent());
        article.setSummary(request.getSummary());
        article.setAuthor(request.getAuthor());
    }
}

设计要点:

  • @Service:注册为 Spring Bean,供 Controller 注入使用
  • @RequiredArgsConstructor (Lombok):为 final 字段生成构造器,实现构造器注入(推荐方式)
  • @Transactional:写操作(增删改)开启事务,出错时自动回滚
  • copyProperties:将 DTO 字段复制到实体,创建时 new 新对象,更新时先查出再覆盖

7. controller/ArticleController.java --- API 接口层

Controller 负责接收 HTTP 请求、调用 Service、返回统一格式的 JSON。

java 复制代码
@RestController
@RequestMapping("/api/articles")
@RequiredArgsConstructor
public class ArticleController {

    private final ArticleService articleService;

    @GetMapping
    public ApiResponse<List<Article>> list() { ... }

    @GetMapping("/{id}")
    public ApiResponse<Article> detail(@PathVariable Long id) { ... }

    @PostMapping
    public ApiResponse<Article> create(@Valid @RequestBody ArticleRequest request) { ... }

    @PutMapping("/{id}")
    public ApiResponse<Article> update(@PathVariable Long id, @Valid @RequestBody ArticleRequest request) { ... }

    @DeleteMapping("/{id}")
    public ApiResponse<Void> delete(@PathVariable Long id) { ... }
}

完整 API 列表:

方法 路径 说明
GET /api/articles 获取文章列表
GET /api/articles/{id} 获取文章详情
POST /api/articles 创建文章
PUT /api/articles/{id} 更新文章
DELETE /api/articles/{id} 删除文章

常用注解:

注解 作用
@RestController 组合 @Controller + @ResponseBody,返回值直接序列化为 JSON
@RequestMapping 类级别路由前缀
@GetMapping / @PostMapping 映射 HTTP 方法
@PathVariable 读取 URL 路径中的 {id}
@RequestBody 将请求体 JSON 反序列化为 Java 对象
@Valid 触发 DTO 上的校验注解

8. common/ApiResponse.java --- 统一响应格式

所有接口返回相同结构,前端只需判断 code 字段即可:

json 复制代码
{
  "code": 200,
  "message": "success",
  "data": { ... }
}
java 复制代码
@Data
public class ApiResponse<T> {
    private int code;
    private String message;
    private T data;

    public static <T> ApiResponse<T> success(T data) {
        return new ApiResponse<>(200, "success", data);
    }

    public static <T> ApiResponse<T> success(String message, T data) {
        return new ApiResponse<>(200, message, data);
    }

    public static <T> ApiResponse<T> error(int code, String message) {
        return new ApiResponse<>(code, message, null);
    }
}

成功时 code = 200;失败时由异常处理器返回 code = 400 及具体错误信息。


9. exception/GlobalExceptionHandler.java --- 全局异常处理

java 复制代码
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(RuntimeException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ApiResponse<Void> handleRuntimeException(RuntimeException e) {
        return ApiResponse.error(400, e.getMessage());
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ApiResponse<Void> handleValidationException(MethodArgumentNotValidException e) {
        String message = e.getBindingResult().getFieldErrors().stream()
                .map(FieldError::getDefaultMessage)
                .collect(Collectors.joining("; "));
        return ApiResponse.error(400, message);
    }
}

@RestControllerAdvice 拦截所有 Controller 抛出的异常,统一转换为 ApiResponse 格式,避免直接返回 Spring 默认的错误页面。

处理的异常类型:

  1. RuntimeException:业务异常,如「文章不存在」
  2. MethodArgumentNotValidException:参数校验失败,如「标题不能为空」

10. config/CorsConfig.java --- 跨域配置

前后端分离开发时,前端 localhost:5173 访问后端 localhost:8080 属于跨域请求,需要配置 CORS:

java 复制代码
@Configuration
public class CorsConfig {

    @Bean
    public CorsFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedOriginPattern("*");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        config.setAllowCredentials(true);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }
}

允许所有来源、请求头、HTTP 方法,解决浏览器跨域拦截问题。生产环境建议将 * 改为具体的前端域名。


五、完整请求流程:以「创建文章」为例

用户在前端填写标题、作者、摘要、正文,点击保存。以下是请求在后端的完整流转过程:

sql 复制代码
① 前端发送 POST /api/articles
   Body: { "title": "...", "author": "...", "summary": "...", "content": "..." }
        │
        ▼
② CorsConfig 放行跨域请求
        │
        ▼
③ ArticleController.create()
   - @RequestBody 将 JSON 转为 ArticleRequest 对象
   - @Valid 触发字段校验(标题/内容/作者非空等)
   - 校验失败 → GlobalExceptionHandler 返回 400
        │
        ▼
④ ArticleService.create()
   - new Article() 创建空实体
   - copyProperties() 将 DTO 字段写入实体
   - articleRepository.save() 持久化到数据库
   - @Transactional 保证事务一致性
   - Hibernate 自动填充 createdAt、updatedAt
        │
        ▼
⑤ ArticleRepository.save()
   - JPA 生成 INSERT SQL 写入 MySQL
        │
        ▼
⑥ Controller 包装为 ApiResponse 返回
   { "code": 200, "message": "创建成功", "data": { "id": 1, "title": "...", ... } }
        │
        ▼
⑦ 前端收到响应,跳转到文章详情页

查询列表 流程更简单:GET /api/articles → Controller → Service.listAll() → Repository.findAll() → 返回 List。

删除文章 流程:Controller 接收 DELETE /api/articles/{id} → Service 先 existsById 检查 → deleteById 删除 → 返回成功消息。


六、Maven 依赖说明(pom.xml

xml 复制代码
<properties>
    <java.version>21</java.version>
</properties>

<dependencies>
    spring-boot-starter-web          <!-- Web MVC、内嵌 Tomcat -->
    spring-boot-starter-data-jpa     <!-- JPA + Hibernate -->
    spring-boot-starter-validation   <!-- @Valid 参数校验 -->
    mysql-connector-j                <!-- MySQL 驱动 -->
    lombok                           <!-- 简化代码 -->
    spring-boot-starter-test         <!-- 单元测试 -->
</dependencies>

七、前端简要说明

前端位于 frontend/ 目录,使用 Vue 3 + Vite 构建,主要页面:

页面 路由 功能
首页 / 展示最新文章
文章列表 /articles 列表展示、搜索
文章详情 /articles/:id 阅读文章
写文章/编辑 /articles/new/articles/:id/edit 表单提交

src/api/article.js 封装了五个 API 方法,对应后端五个接口;src/api/request.js 配置 Axios 实例,统一处理 ApiResponsecode 判断和错误提示。

前端通过 Vite 代理将 /api 请求转发到 http://localhost:8080,开发时无需关心跨域问题。


八、总结

本项目的 Java 后端遵循 Controller → Service → Repository 分层设计,职责清晰:

  • Entity 映射数据库表结构
  • DTO 隔离外部输入,配合 Validation 做参数校验
  • Repository 借助 JPA 简化数据访问
  • Service 承载业务逻辑与事务
  • Controller 暴露 REST API
  • ApiResponse + GlobalExceptionHandler 统一响应与错误处理
  • CorsConfig 解决前后端分离的跨域问题

这种结构是 Spring Boot 项目的标准实践,扩展新功能(如用户登录、评论、标签分类)时,只需按相同模式新增 Entity → Repository → Service → Controller 即可。

相关推荐
Hommy881 小时前
【剪映小助手】添加贴纸接口(Add Sticker)
后端·github·剪映小助手·视频剪辑自动化·剪映api
CaffeinePro2 小时前
FastAPI响应处理:返回值、状态码、响应头与异常标准化与案例解析
后端
HuanYu2 小时前
PageHelper分页的原理
后端
于先生吖2 小时前
SpringBoot对接大模型开发AI命理测算系统:八字排盘与AI解析接口源码全解
人工智能·spring boot·后端
张不才3 小时前
一个静默吞数据的时间戳陷阱
后端
李少兄3 小时前
从原理到实战:Spring IoC/DI 核心知识体系与高频面试题全解
java·后端·spring
ServBay3 小时前
ServBay 1.30.0 更新:双平台引入 MCP 服务,AI 编程助手成为全栈本地运维
后端·ai编程
张不才3 小时前
分页查出来的数据总少几条?可能是 MyBatis 后置过滤的坑
后端
Windeal3 小时前
Agent ToolCall 循环怎么定制?PI Extension 与 DeepAgents Middleware 两条岔路深度对比
后端·openai