优雅!使用 Spring Boot 3.3 + @ControllerAdvice 实现全局数据绑定与预处理
在现代 Web 应用中,数据的传递与处理往往需要全局化处理,尤其是涉及到类似于用户信息、网站通用配置等数据时,开发者通常需要在多个控制器和页面中共享这些数据。传统的方式是在每个控制器或页面中重复传递这些信息,这种做法显然不够优雅,也增加了维护成本。为了解决这一问题,Spring Framework 提供了 @ControllerAdvice
注解,可以在全局范围内实现数据绑定与预处理,从而极大地简化代码结构。
@ControllerAdvice
是 Spring 提供的一个专门用于处理全局范围内的控制器逻辑增强的注解。它可以用来处理全局异常、全局数据绑定、全局权限校验等功能。在本文中,我们将重点讲解如何使用 @ControllerAdvice
结合 @ModelAttribute
实现全局数据绑定,将应用的一些全局配置数据注入到所有控制器和视图中。此外,我们还会结合前端 jQuery
调用 JSON 接口动态加载数据,并优化控制器类的设计。
@ControllerAdvice 注解的详细用法
@ControllerAdvice
的核心功能是在不修改现有控制器逻辑的前提下,增强其功能。通过 @ControllerAdvice
,我们可以统一处理如下几种场景:
全局异常处理:可以集中捕获并处理所有控制器的异常。
全局数据绑定:可以为所有控制器统一注入公共数据,避免重复代码。
全局权限校验:在控制器逻辑执行之前进行统一的权限检查。
运行效果:
若想获取项目完整代码以及其他文章的项目源码,且在代码编写时遇到问题需要咨询交流,欢迎加入下方的知识星球。
项目配置
Maven 配置(pom.xml)
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.icoderoad</groupId>
<artifactId>global-binding</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>global-binding</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<!-- Spring Boot Web 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Thymeleaf 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- Lombok 依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!-- Spring Boot 配置处理依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
YAML 配置(application.yaml)
在 application.yaml
中,我们配置了全局的数据,例如应用名称、页面标题等,这些配置将会通过 @ControllerAdvice
进行全局绑定。
app:
name: "ICodeRoad"
title: "Spring Boot 全局数据绑定示例"
配置类(AppProperties.java)
我们通过 @ConfigurationProperties
注解读取 YAML 文件中的自定义配置。Lombok 用于简化实体类代码。
package com.icoderoad.global.binding.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private String name;
private String title;
}
全局数据绑定(GlobalDataBinder.java)
@ControllerAdvice
用于定义全局范围的控制器建议,这里我们实现了全局数据绑定,将 AppProperties
中的配置数据传递到每一个视图中。
package com.icoderoad.global.binding.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ModelAttribute;
import com.icoderoad.global.binding.config.AppProperties;
@ControllerAdvice
public class GlobalDataBinder {
@Autowired
private AppProperties appProperties;
/**
* 将应用程序的全局数据绑定到所有的模型中
*/
@ModelAttribute("appName")
public String getAppName() {
return appProperties.getName();
}
@ModelAttribute("appTitle")
public String getAppTitle() {
return appProperties.getTitle();
}
}
控制器类(HomeController.java)
创建一个简单的控制器来渲染主页,并验证全局数据绑定的效果。
package com.icoderoad.global.binding.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.icoderoad.global.binding.config.AppProperties;
/**
* 处理首页和数据接口的控制器
*/
@RestController
@RequestMapping("/api")
public class HomeController {
@Autowired
private AppProperties appProperties;
/**
* 返回首页信息的 JSON 数据
*/
@GetMapping("/app-info")
public AppProperties getAppInfo() {
// 直接返回 AppProperties 对象作为 JSON 响应
return appProperties;
}
}
在这个控制器中,/api/app-info
接口会返回全局配置信息(AppProperties
)的 JSON 数据,供前端通过 jQuery 进行动态加载。
前端代码
在前端页面中,我们使用 jQuery 通过 AJAX 请求动态加载后端的 JSON 数据,并将其显示在页面中。使用了 Bootstrap 来进行页面的简单布局,并通过 CDN 方式加载 jQuery 和 Bootstrap 的资源。
在 src/main/resources/templates/
目录下创建 index.html
文件,使用 Thymeleaf 渲染全局绑定的数据。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>全局数据绑定</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css">
</head>
<body>
<div class="container">
<h1 id="appTitle">加载中...</h1>
<p>欢迎来到 <span id="appName">加载中...</span> 的主页!</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
$(document).ready(function () {
// 使用 jQuery 的 Ajax 请求从后端获取应用程序信息
$.getJSON('/api/app-info', function(data) {
// 动态更新页面内容
$('#appTitle').text(data.title);
$('#appName').text(data.name);
});
});
</script>
</body>
</html>
运行效果
运行项目后,访问 http://localhost:8080/
,首页会通过 jQuery 发送 AJAX 请求,获取后端的应用配置信息,并将这些数据动态地加载到页面中。在网络正常的情况下,页面初次加载时会显示“加载中...”的占位内容,随后 AJAX 请求返回的数据会替换掉占位符,展示出真实的应用名称和标题。后端控制器返回的数据接口可以轻松扩展,前端页面可以根据需要进行相应的数据处理与展示,整个数据传输过程简单明了,代码结构也非常清晰易维护。
总结
在实际开发中,@ControllerAdvice
提供了强大的全局控制能力,使得我们可以在全局范围内进行异常处理、数据绑定等操作。通过 @ModelAttribute
实现的全局数据绑定,可以有效减少控制器中的重复代码,使得代码更加简洁、可维护。结合 jQuery 的动态加载,我们可以轻松实现前后端的数据交互,使应用具备更高的动态性和响应性。
这种开发方式特别适用于大型应用程序,能够帮助开发团队减少代码冗余,集中处理全局逻辑,使应用具有更好的可扩展性和易维护性。在日常开发中,充分利用 Spring boot 提供的全局控制机制,能够让我们的代码更加优雅。