Spring Boot 动态修改 Logger 的日志级别

在 Spring Boot 应用中,默认使用 Logback 来记录日志。可以在 application.yaml 或者是 logback-spring.xml 中配置 Logger 的日志级别。 有以下几个常见的日志级别(从低到高): TRACE(跟踪):最低级别的日志,用于输出详细的调试信息,通常用于追踪代码的执行路径。 DEBUG(调试):用于输出调试信息,帮助开发人员调试应用程序。 INFO(信息):用于输出一般性的信息,例如应用程序的启动信息、重要事件等。 WARN(警告):用于输出警告信息,表示潜在的问题或不符合预期的情况,但不会影响应用程序的正常运行。 ERROR(错误):最高级别的日志,用于输出错误信息,表示发生了一个错误或异常情况,可能会影响应用程序的正常运行。 级别越低,包含的信息越详细,级别越高,包含的信息越严重和重要。当设置日志级别时,只有达到或高于该级别的日志才会被记录和输出,而低于该级别的日志将被忽略。如:如果将日志级别设置为 INFO,则会记录和输出 INFO、WARN 和 ERROR 级别的日志,而 DEBUG 和 TRACE 级别的日志将被忽略。这有助于控制日志的详细程度和输出量,以适应特定的调试或生产环境要求。 Log4j 建议只使用 ERROR、WARN、INFO、DEBUG 四个级别。 以 ROOT Logger 的配置为例 在 application.yaml 中配置 logging: level: ROOT: DEBUG 在 logback-spring.xml 中配置 首先,需要在 application.yaml 中配置 logback-spring.xml 配置文件的位置: logging: config: classpath:logback-spring.xml 然后,在 logback-spring.xml 中配置 ROOT Logger 的日志级别: <configuration> <!-- 继承 Spring 预定义的 Logback 配置 --> <include resource="org/springframework/boot/logging/logback/base.xml"/> <!

Spring Boot 定时推送 Websocket 消息

1、概览 本文将带你了解如何在 Spring Boot 中实现定时地往浏览器推送 WebSockets 消息。 另一种方法是使用服务器发送事件 (SSE),但本文不涉及这一点。 Spring 提供了多种调度方式。如 @Scheduled 注解,以及 Project Reactor 提供的 Flux::interval 方法,对于 Webflux 应用来说,该方法开箱即用,它还可以作为独立库用于任何 Java 项目。 此外,还有一些更专业的三方调度框架,如 Quartz Scheduler,但这不在本文范畴。 2、简单的聊天应用 在 上一篇文章 中,使用 WebSockets 构建了一个聊天应用。现在让我们用一项新功能来扩展它:聊天机器人。聊天机器人是向浏览器推送预定消息的服务器端组件。 2.1、Maven 依赖 先在 Maven 中添加必要依赖, pom.xml 如下: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> <dependency> <groupId>io.projectreactor</groupId> <artifactId>reactor-core</artifactId> </dependency> <dependency> <groupId>com.github.javafaker</groupId> <artifactId>javafaker</artifactId> <version>1.0.2</version> </dependency> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> </dependency> 2.2、JavaFaker 使用 JavaFaker 库生成机器人信息。该库通常用于生成测试数据。在这里,用于为聊天室添加一位名为 “Chuck Norris” 的访客。 代码如下: Faker faker = new Faker(); ChuckNorris chuckNorris = faker.

Spring Websockets 的 @SendToUser 注解

1、概览 本文将带你了解如何在 Spring WebSockets 中使用 @SendToUser 注解向特定 Session 或特定用户发送消息。 有关上述 Spring WebSockets 的介绍,请参阅 上一篇文章。 2、WebSocket 配置 首先,需要配置 Message Broker 和 WebSocket 应用端点: @Configuration @EnableWebSocketMessageBroker public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/topic/", "/queue/"); config.setApplicationDestinationPrefixes("/app"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/greeting"); } } 通过 @EnableWebSocketMessageBroker 注解,启用了在 WebSocket 上使用 STOMP(Streaming Text Oriented Messaging Protocol)的基于 Broker 的消息传递。需要强调的是,这个注解需要与 @Configuration 一起使用。 继承 AbstractWebSocketMessageBrokerConfigurer 并不是必须的,但这可以更容易地自定义导入的配置。 在第一个方法中,建立了一个简单的基于内存的 Message Broker,通过以 /topic 和 /queue 为前缀的目标将消息传回客户端。

Spring WebSocket 简介

1、概览 本文将带你学习如何使用 Spring 4 中引入的 WebSocket 功能来实现一个简单的聊天应用。 WebSockets 是 Web 浏览器和服务器之间的一种双向、全双工、持久连接。一旦建立了 WebSocket 连接,该连接就会一直打开,直到客户端或服务器关闭该连接。 2、Maven 依赖 在 pom.xml 中添加所需的依赖: <dependency> <groupId>org.springframework</groupId> <artifactId>spring-websocket</artifactId> <version>5.2.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-messaging</artifactId> <version>5.2.2.RELEASE</version> </dependency> 此外,还需要添加 Jackson 依赖,用于序列化/反序列化 JSON 格式的消息。 <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.10.2</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.10.2</version> </dependency> 3、启用 WebSocket 首先,在配置类上通过 @EnableWebSocketMessageBroker 注解来启用 WebSocket 功能。 配置类需要继承 AbstractWebSocketMessageBrokerConfigurer。 顾名思义,它能在 Message Broker 的支持下处理 WebSocket 消息: @Configuration @EnableWebSocketMessageBroker public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.

Spring RestTemplate 解析 JSON 数组

1、概览 在 Spring Boot 应用中,一般使用 RestTemplate 来执行同步 HTTP 请求。数据通常以 JSON 的形式返回,而 RestTemplate 可以进行自动转换。 本文将带你了解,如何在 RestTemplate 中把响应的 JSON 数组转换为 Object 数组、POJO 数组和 POJO 集合。 2. JSON、POJO 和 Service 假设我们有一个端点 http://localhost:8080/users,它返回如下 JSON 格式的用户列表: [{ "id": 1, "name": "user1", }, { "id": 2, "name": "user2" }] 对应的 User 类如下: public class User { private int id; private String name; // get/set 方法省略 } 定义 Service 接口实现 UserConsumerServiceImpl,并注入 RestTemplate: public class UserConsumerServiceImpl implements UserConsumerService { private final RestTemplate restTemplate; public UserConsumerServiceImpl(RestTemplate restTemplate) { this.

Spring Bean 循环依赖

1、背景 有好几次线上发布老应用时,遭遇代码启动报错,具体错误如下: Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'xxxManageFacadeImpl': Bean with name 'xxxManageFacadeImpl' has been injected into other beans [xxxProductMaintenceFacadeImpl] in its raw version as part of a circular reference, but has eventually been wrap means thff, for expped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesallowEageOfType' with the 'allowEagerInit' flag turned off, for example 咋眼一看,这不就是 Spring Bean 循环依赖报错吗?脑海立马闪过那些年为了进阿里面试时被死亡 N 连问的场景,那时我们都知道 Spring 已经支持 Bean 循环依赖,为啥我们的 Spring Boot 应用启动时还报这个错误?于是,带着这个问题重新温习下 Spring 如何解决 Bean 循环依赖。

Spring Boot 自定义 Jackson ObjectMapper

1、概览 Spring Boot 默认使用 Jackson ObjectMapper 实例来序列化和反序列化 JSON 格式的响应与请求。 本文将带你了解如何在 Spring Boot 中自定义 Jackson ObjectMapper 选项,以及配置序列化和反序列化选项的最常用方法。 2、默认配置 默认情况下,Spring Boot 禁用了以下功能: MapperFeature.DEFAULT_VIEW_INCLUSION DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES SerializationFeature.WRITE_DATES_AS_TIMESTAMPS 从一个简单的例子开始: 客户端向 /coffee?name=Lavazza 端点发送 GET 请求。 Controller 返回一个新的 Coffee 对象。 Spring 使用 ObjectMapper 将 POJO 序列化为 JSON。 使用 String 和 LocalDateTime 对象来演示自定义选项: public class Coffee { private String name; private String brand; private LocalDateTime date; // get / set 省略 } 定义一个简单的 REST Controller 来演示序列化: @GetMapping("/coffee") public Coffee getCoffee( @RequestParam(required = false) String brand, @RequestParam(required = false) String name) { return new Coffee() .

Spring Cloud Feign 集成测试

1、概览 本文将带你了解 Feign 客户端的集成测试。 首先创建一个基本的 Open Feign 客户端,并使用 WireMock 编写一个简单的集成测试。 之后,给客户端添加 Ribbon 配置,并为其构建一个集成测试。最后,配置一个 Eureka 测试容器,并测试此设置,以确保整个配置按预期工作。 2、Feign Client 要设置 Feign 客户端,首先要添加 Spring Cloud OpenFeign Maven 依赖: <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> 然后,创建一个 Book 模型类: public class Book { private String title; private String author; } 最后,创建 Feign 客户端接口: @FeignClient(name = "books-service") public interface BooksClient { @RequestMapping("/books") List<Book> getBooks(); } 现在,我们有了一个从 REST 服务中获取 List<Book> 的 Feign 客户端。接下来,编写一些集成测试。 3、WireMock 3.1、设置 WireMock 服务器 要测试 BooksClient,需要一个提供 /books 端点的 mock 服务,客户端将调用该 mock 服务。为此,我们使用 WireMock。

Spring-boot:repackage 和 Maven package

1、概览 Apache Maven 是一种广泛使用的项目依赖管理工具和项目构建工具。 Spring Boot 通过 Spring Boot Maven Plugin 在 Apache Maven 中提供了对 Spring Boot 的支持。 众所周知,在 Maven 中可以使用 mvn package 将应用打包为 JAR 或 WAR 包。不过,Spring Boot Maven 插件额外添加了一个 repackage goal,也可以在 mvn 命令中调用。 有时,这两个 mvn 命令会让人混淆。本文将带你了解 mvn package 和 spring-boot:repackage 之间的区别。 2、Spring Boot 应用示例 首先,创建一个简单的 Spring Boot 应用作为示例: @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } 创建一个简单的 REST 端点来验证应用是否正常运行: @RestController public class DemoRestController { @GetMapping(value = "/welcome") public ResponseEntity welcomeEndpoint() { return ResponseEntity.

将 Hibernate 代理对象转换为实际的实体对象

1、概览 本文将带你了解 Hibernate 是在什么时候创建代理对象的,代理对象有啥用?以及如何把 Hibernate 的代理对象转换为真正的实体对象。 2、Hibernate 何时创建代理对象? Hibernate 使用代理对象来实现懒加载。 有如下 PaymentReceipt 和 Payment 实体: @Entity public class PaymentReceipt { ... @OneToOne(fetch = FetchType.LAZY) private Payment payment; ... } @Entity @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) public abstract class Payment { ... @ManyToOne(fetch = FetchType.LAZY) protected WebUser webUser; ... } 加载这两个实体中的任何一个,都会导致 Hibernate 为关联字段使用 FetchType.LAZY 创建一个代理对象。 创建并运行集成测试: @Test public void givenPaymentReceipt_whenAccessingPayment_thenVerifyType() { PaymentReceipt paymentReceipt = entityManager.find(PaymentReceipt.class, 3L); Assert.assertTrue(paymentReceipt.getPayment() instanceof HibernateProxy); } 测试代码如上,加载了一个 PaymentReceipt,并验证了 payment 对象不是 CreditCardPayment 的实例,而是一个 HibernateProxy 对象。