1、概览 Zuul 是 Netflix 推出的基于 JVM 的网关和服务器端负载均衡器。Zuul 的规则引擎提供了灵活性,可以编写规则和过滤器(Filter)来增强在 Spring Cloud 微服务架构中的路由功能。。
本文将会带你了解如何通过自定义 Error Filter 来自定义 Zuul 中的异常和 Error 响应。当代码执行过程中发生错误时,这些自定义 Error Filter 将被执行。
2、Zuul 异常 Zuul 中处理的所有异常都是 ZuulException。
ZuulException 不能通过 @ControllerAdvice 捕获,也不能通过 @ExceptionHandling 对方法进行注解。这是因为 ZuulException 是由 Error Filter 抛出的。因此,它会跳过后续的过滤链,永远不会到达 Error Controller。下图描述了 Zuul 中错误处理的层次结构:
出现 ZuulException 时,Zuul 会显示以下错误响应:
{ "timestamp": "2022-01-23T22:43:43.126+00:00", "status": 500, "error": "Internal Server Error" } 在某些情况下,可能需要自定义 ZuulException 响应中的错误信息或状态码。这时,Zuul Filter 就能派上用场了。
3、自定义 Zuul 异常 spring-cloud-starter-netflix-zuul Starter 包括三种 Filter:Pre(前置)、Post(后置)和 Error(错误)。
本文重点深入了解 Error Filter。
1、概览 本文将带你了解 “HttpMessageNotWritableException: no converter for [class …] with preset Content-Type” 异常的原因以及解决办法。
2、原因 异常的堆栈信息说明了一切:Spring 找不到合适的 HttpMessageConverter,无法将 Java 对象转换为 HTTP 响应。
Spring 依靠客户端的 Accept Header 来检测它需要响应的媒体类型(Media Type)。
因此,使用未预注册消息转换器(Message Converter)的 Media Type 将导致 Spring 抛出异常。
3、重现异常 通过一个实际例子来重现异常。
创建一个 Handler Method,指定一种媒体 Media Type(用于响应),这种类型没有注册 HttpMessageConverter。
例如,使用 MediaType.APPLICATION_XML_VALUE 或字符串 application/xml:
@GetMapping(value = "/student/v3/{id}", produces = MediaType.APPLICATION_XML_VALUE) public ResponseEntity<Student> getV3(@PathVariable("id") int id) { return ResponseEntity.ok(new Student(id, "Robert", "Miller", "BB")); } 接着,使用 cURL 对 http://localhost:8080/api/student/v3/1 发起请求:
curl http://localhost:8080/api/student/v3/1 端点响应如下:
1、概览 一般的 Spring Boot 应用通常只需要配置一个数据库,但是有时也可能需要访问多个数据源。本文将带你了解如何在 Spring Boot 中配置和使用多个数据源。
2、默认行为 通常,我们会在 application.yml 中声明 Spring Boot 数据源,如下:
spring: datasource: url: ... username: ... password: ... driverClassname: ... Spring 会将这些配置映射到 org.springframework.boot.autoconfigure.jdbc.DataSourceProperties 的实例。
DataSourceProperties 的定义如下:
@ConfigurationProperties(prefix = "spring.datasource") public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean { // ... /** * Fully qualified name of the JDBC driver. Auto-detected based on the URL by default. */ private String driverClassName; /** * JDBC URL of the database. */ private String url; /** * Login username of the database.
Spring Boot 对于发送邮件这种常用功能也提供了开箱即用的 Starter:spring-boot-starter-mail。
通过这个 starter,只需要简单的几行配置就可以在 Spring Boot 中实现邮件发送,可用于发送验证码、账户激活等等业务场景。
本文将通过实际的案例带你了解如何在 Spring Boot 中使用 QQ 邮箱发送邮件。
关于 Spring 对邮件支持的更多细节,你可以参阅 中文文档。
创建 Spring Boot 应用 在 pom.xml 中添加 spring-boot-starter-mail 依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency> 属性配置 在 application.yaml 中配置属性。
spring: mail: # 指定邮件服务器地址 host: smtp.qq.com # 登录账户 username: 747692844@qq.com # 登录密码 password: "<你的密码/授权码>" # 端口 port: 465 # 默认编码 default-encoding: UTF-8 # 使用的协议 protocol: smtps # 其他的属性 properties: "mail.smtp.connectiontimeout": 5000 "mail.smtp.timeout": 3000 "mail.smtp.writetimeout": 5000 "mail.
1、简介 Spring Cloud Config 是 Spring Cloud 生态下的一个子项目,可让 Spring 应用轻松实现配置外部化。通过它,可以将配置数据作为服务暴露出来,从而使得任何具有 HTTP 客户端的应用都可以轻松地获取配置数据。
本文将会带你了解如何在 Spring Cloud Config 中使用 Git 以外的配置源来存储配置信息。
2、Spring Cloud Config 概览 Spring Cloud Config 采用了典型的客户端-服务器模型。集中式服务器(或多个服务器)从外部数据源读取配置数据。这些服务器会暴露各种 HTTP 端点,允许任何其他应用查询配置数据。
Spring Cloud Config 还能让 Spring Boot 应用非常方便地自动连接到配置服务器。服务器提供的配置数据可以像客户端应用中的其他属性源一样使用。
3、GIT Spring Cloud Config 最常见的使用案例是在 git 仓库中存储配置数据。这种方式的设置有几个优点:
灵活性:git 仓库可以容纳各种类型的文件,包括二进制文件。 安全性:可轻松控制细粒度的读写权限。 审计:强大的历史跟踪功能可轻松审核配置更改。 标准化:无论哪家提供商(Provider),Git 操作都是标准的,这意味着我们可以自行托管或使用任意数量的第三方提供商。 分布式: Git 从一开始就是为分布式设计的,因此非常适合云原生和微服务架构。 尽管有上述种种好处,但 git 并不总是存储配置数据的最佳选择。例如,项目可能已经将配置数据放在其他数据存储(如关系型数据库)中。在这种情况下,将其迁移到 git 可能就得不偿失了。
4、使用 GIT 以外的 配置源 在 Spring Cloud Config 中使用 git 以外的其他配置源时时,指的其实是服务器组件。对数据存储的选择不会影响客户端组件。只有服务器会受到影响。
在 Spring Cloud Config Server 库中,有一个名为 EnvironmentRepository 的接口定义了一个配置源。所有配置源(包括 git 和其他配置源)都必须实现该接口。
1、概览 OpenFeign 是一个可以在 Spring Boot 中使用的声明式 REST 客户端。
假如想通过 OpenFeign 来调用使用 OAuth2 的 REST API,那么就需要给 OpenFeign 设置 Access Token。
本文将会带你了解如何为 OpenFeign 客户端添加 OAuth2 支持。
2、服务之间的认证 服务之间的认证 API 安全中的一个热门话题。我们可以使用 mTLS 或 JWT 为 REST API 提供认证机制。不过,OAuth2 协议是保护 API 的事实解决方案。假设我们想使用另一个服务(客户端)调用一个受保护的服务(服务器)。在这种情况下,使用 “客户端凭证(client credential)” 授权方式。这种授权方式通常用于在两个没有终端用户的 API 或系统之间进行身份认证。
下图显示了这种授权模式中的的主要角色关系:
在客户端凭证模式中,客户端通过 Token Endpoint 从授权服务器(Authorization Server)获取 Access Token。然后,客户端使用 Access Token 访问源服务器(Resource Server)上受资保护的资源。资源服务器会验证 Access Token,如果有效,则为请求提供服务。
2.1、授权服务器 创建一个授权服务器来发放 Access Token。为了方便,我们在 Spring Boot 中使用嵌入式 Keycloak。假设我们使用 GitHub 上的 授权服务器 项目。
首先,在嵌入式 Keycloak 服务器的 realm 管理中定义 Payment-app 客户端:
1、概览 本文将带你了解如何使用 swagger-maven-plugin 来在 Swagger 中记录枚举,并在 Swagger 编辑器中验证生成的 JSON 文档。
2、Swagger 是啥? Swagger 是一种开源工具,用于定义基于 REST 的 API。在当今世界,大多数组织都在向微服务和 API 优先的方向发展。Swagger 在设计和记录 API 方面非常方便。它还提供了 Swagger 编辑器、Swagger UI 和 Swagger CodeGen 等各种工具来协助 API 开发。
此外,Swagger 是 OpenAPI 规范或 OAS 的一种实现,OAS 定义了一套用于开发 REST API 的标准,有助于全球各地的组织将编写 API 的过程标准化。
我们应用生成的 JSON 文件也将遵循 OpenAPI 规范。
在 Swagger 中使用 Enum 是个有必要的,有些 API 需要用户只能使用一组特定的预定义值。这些预定义的常量值被称为枚举(Enum)。同样,当 Swagger 公开 API 时,需要要确保用户从这组预定义值中选择一个值,而不是任意的值。
换句话说,需要在 swagger.json 文件中记录枚举,让用户知道可选的选项。
3、实现 实现一个 POST API,用于为组织招聘特定角色的员工(Employee)。角色只能是以下角色之一:Engineer、Clerk、Driver 或 Janitor。
创建 Role 枚举,其中包含 Employee 角色的所有可能值,然后创建一个 Employee 类,将角色作为其属性之一。
简介 本文将会带你了解 Spring Data JPA 对锁的支持,以及如何在检索时使用共享锁或者独占锁。
JPA LockModeType JPA 提供了几种加锁的选项,可以在获取实体时通过 LockModeType 枚举来指定。
如下:
LockModeType.PESSIMISTIC_READ 可对相关表记录加共享锁或读锁。 LockModeType.PESSIMISTIC_WRITE 可对相关表记录加速独占锁或写锁。 如果底层数据库不支持共享锁,那么 PESSIMISTIC_READ 策略将退回到 PESSIMISTIC_WRITE,因为该选项已被广泛支持。
根据 ID 获取实体时加锁 JPA EntityManager 提供了一个 find 方法的重载,可以传递 LockModeType 采参数。用于在获取实体时上锁:
Post post = entityManager.find(Post.class, id, lockMode); Spring Data JpaRepository 未提供此选项,但我们可以使用自定义 Spring Data Repository 来实现。
例如,可以让 PostRepository 继承 JpaRepository,CustomPostRepository:
@Repository public interface PostRepository extends JpaRepository<Post, Long>, CustomPostRepository { } In the CustomPostRepository interface, we can define a lockById method like this:
MyBatis Plus 是 MyBatis 框架的一个增强。除了基本的 MyBatis 功能外,它还提供了快速的 CURD 方法,以及投影查询、分页查询、动态条件等等功能,极大的提高了开发效率。
本文将会通过案例教你如何在 Spring Boot 中整合 MyBatis Plus。
文中使用的软件版本如下:
Spring Boot:3.0.3 MySQL:8.0.0 MyBatis Plus:3.5.4 初始化演示数据 首先在本地数据库执行以下 SQL 脚本,创建一张名为 t_user 的数据表:
CREATE TABLE `t_user` ( `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', `create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `enabled` tinyint unsigned NOT NULL COMMENT '是否启用。0:禁用,1:启用', `name` varchar(50) COLLATE utf8mb4_general_ci NOT NULL COMMENT '名字', PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`) ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户'; 然后再执行如下脚本,创建初始记录:
1、概览 本文将带你了解如何在 Spring Security 中针对不同的 URL 进行不同的安全配置。
2、设置 创建应用,在 pom.xml 中添加 Web 和 Security 依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> 3、创建 API 创建一个包含两个 API 的 RESTful 服务:Product(产品) API 和 Customer(客户) API。
3.1、Product API 创建 ProductController。它包含一个 getProducts 方法,该方法返回产品列表:
@RestController("/products") public class ProductController { @GetMapping public List<Product> getProducts() { return new ArrayList<>(Arrays.asList( new Product("Product 1", "Description 1", 1.0), new Product("Product 2", "Description 2", 2.0) )); } } 3.2、Customer API 同样,定义 CustomerController: