Spring

配置 Spring 以接收和返回 XML 格式的数据

1、概览 虽然 JSON 是 RESTful 服务的事实标准,但在某些情况下,可能需要使用 XML。例如:老掉牙的银行 API 就是通过 XML 进行交互的。 Spring 通过 Jackson XML 提供了一种简单的方法来支持 XML 端点。 2、依赖 第一步是添加 依赖。注意 spring-boot-starter-web Starter 默认不包含支持 XML 的库。需要手动添加: <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> </dependency> 另外,也可以使用 JAXB 来实现,但总的来说,JAXB 更啰嗦,而且 API 没有 Jackson 那么优雅好用。不过,如果使用的是 Java 8,JAXB 库与实现都位于 javax 包中,因此无需在应用中添加任何其他依赖。 在 Java 9 开始的版本中,javax 包被移动并更名为 jakarta,因此 JAXB 需要额外的 依赖: <dependency> <groupId>jakarta.xml.bind</groupId> <artifactId>jakarta.xml.bind-api</artifactId> <version>4.0.0</version> </dependency> 另外,它需要一个运行时实现来处理 XML Mapper,这可能会导致其他的问题。 3、端点 由于 JSON 是 Spring REST Controller 的默认格式,因此需要在端点上明确配置 “消费” 和 “生产” 的数据类型是 XML:

在 Spring Boot 中记录完整的请求体和响应体日志

完整的请求日志对于 故障排查 和 审计 来说极其重要。通过查看日志,可以检查数据的准确性、参数的传递方式以及服务器返回的数据。 由于 Socket 流不能重读,所以需要一种实现来把读取和写入的数据缓存起来,并且可以多次重复读取缓存的内容。 Spring 提供 2 个可重复读取请求、响应的 Wrapper 工具类: ContentCachingRequestWrapper ContentCachingResponseWrapper 通过类名不难看出,这是典型的装饰者设计模式。它俩的作用就是把读取到的 请求体 和写出的 响应体 都缓存起来,并且提供了访问缓存数据的 API。 创建 RequestLogFilter 创建 RequestLogFilter 继承 HttpFilter,以记录完整的请求和响应日志。 package cn.springdoc.demo.web.filter; import java.io.IOException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.util.ContentCachingRequestWrapper; import org.springframework.web.util.ContentCachingResponseWrapper; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpFilter; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; /** * 记录请求日志 */ public class RequestLogFilter extends HttpFilter { static final Logger log = LoggerFactory.getLogger(RequestLogFilter.class); /** * */ private static final long serialVersionUID = 8991118181953196532L; @Override protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { // Wrapper 封装 Request 和 Response ContentCachingRequestWrapper cachingRequest = new ContentCachingRequestWrapper(request); ContentCachingResponseWrapper cachingResponse = new ContentCachingResponseWrapper(response); // 继续执行请求链 chain.

在 Spring 应用中为 REST API 实现异常处理

1、概览 本文将地带你了解如何在 Spring 中为 REST API 实现异常处理。 在 Spring 3.2 之前,在 Spring MVC 应用中处理异常的两种主要方法是 HandlerExceptionResolver 或 @ExceptionHandler 注解。这两种方法都有一些明显的缺点。 自 3.2 以来,可以使用 @ControllerAdvice 注解来解决前两种解决方案的局限性,并促进整个应用中统一的异常处理。 Spring 5 引入了 ResponseStatusException 类,一种在 REST API 中进行基本错误处理的快速方法。 所有这些都有一个共同点:它们都很好地处理了关注点的分离。应用通常可以抛出异常来表示某种失败,然后再单独进行处理。 2、解决方案 1:Controller 级的 @ExceptionHandler 第一种解决方案适用于 @Controller 层面。定义一个处理异常的方法,并用 @ExceptionHandler 进行注解: public class FooController{ //... @ExceptionHandler({ CustomException1.class, CustomException2.class }) public void handleException() { // } } 这种方法有一个很大的缺点:@ExceptionHandler 注解方法仅对特定 Controller 有效,而不是对整个应用全局有效。当然,可以将其添加到每个 Controller 中,但这并不适合作为通用的异常处理机制。 也可以通过让所有 Controller 都继承一个 Base Controller 类来绕过这一限制。 然而,对于某些原因无法实现上述方法的应用来说,这种解决方案可能会成为一个问题。例如,Controller 可能已经从另一个 Base 类继承而来,而该 Base 类可能在另一个 Jar 中或不可直接修改,或者 Controller 本身不可直接修改。

Spring 中的 @Scheduled 注解

1、概览 本文将带你了解如何使用 Spring @Scheduled 注解来配置和调度定时任务。 使用 @Scheduled 对方法进行注解时,需要遵循如下简单的规则: 方法的返回类型通常应为 void(如果不是,返回值将被忽略) 方法不应有任何参数 2、启用定时调度 可以在配置类上使用 @EnableScheduling 注解来启用 Spring 中的定时任务和 @Scheduled 注解的支持: @Configuration @EnableScheduling public class SpringConfig { ... } 也可以在 XML 中启用,如下: <task:annotation-driven> 3、以固定延迟调度任务 配置一个任务,使其在固定延迟后运行: @Scheduled(fixedDelay = 1000) public void scheduleFixedDelayTask() { System.out.println( "Fixed delay task - " + System.currentTimeMillis() / 1000); } 如上,上一次执行结束与下一次执行开始之间的持续时间是固定的。任务会一直等待到前一个任务结束。 在必须确保上一次执行完成后再次运行的情况下,应使用此选项。 4、以固定频率调度任务 在固定的时间间隔内执行一项任务: @Scheduled(fixedRate = 1000) public void scheduleFixedRateTask() { System.out.println( "Fixed rate task - " + System.currentTimeMillis() / 1000); } 如果任务的每次执行都是独立的,则应使用该选项。

Spring Profile 指南

1、概览 本文将带你了解 Spring 中的 Profile(配置文件),这是 Spring 的核心功能之一。可以把 Bean 配置在不同的 Profile,例如:dev、test、prod。然后,可以在不同的环境中激活指定的 Profile,以便只加载当前环境所需的 Bean。 2、在 Bean 上使用 @Profile 从简单的开始,看看如何使用 @Profile 注解将 Bean 映射到特定的 Profile。 该注解接受一个(或多个) Profile 名称。 考虑一个场景:有一个 Bean,它只能在开发过程中激活,不能在生产过程中部署。 用 dev Profile 注解该 Bean,它只会在开发环境中被加载到容器。在生产环境中, dev Profile 不会被激活: @Component @Profile("dev") public class DevDatasourceConfig Profile 名称也可以用 NOT 运算符作为前缀,如 !dev,以将其从 Profile 排除。 在如下示例中,只有当 dev Profile 未激活时,组件才会被激活: @Component @Profile("!dev") public class DevDatasourceConfig 3、在 XML 中声明 Profile Profile 也可以用 XML 配置。<beans> 标签有一个 profile 属性,该属性包含以逗号分隔的 Profile 值: <beans profile="dev"> <bean id="devDatasourceConfig" class="org.

Spring Bean 的生命周期

“讲一讲 Spring Bean 的生命周期”,这算是一道非常经典的面试题了! 如果没有研究过 Spring 源码,单纯去背面试题,这个问题也是可以回答出来的,但是单纯的背缺乏理解,而且面试一紧张,就容易背岔了。 在前面的文章中,给大家分析了 Spring 中 Bean 的创建是在 createBean 方法中完成的。在该方法中,真正干活的实际上是 doCreateBean 方法,具体位置在 AbstractAutowireCapableBeanFactory#doCreateBean,大家在面试时候常被问到的 Spring Bean 的生命周期,实际上就是问 doCreateBean 方法的执行逻辑。 doCreateBean 方法整体上来说,干了四件事: Bean 的实例化。 Bean 属性填充。 Bean 初始化。 Bean 销毁方法注册。 这里大家注意区分 实例化 和 初始化 两个方法,实例化是指通过反射创建出来 Bean 实例的过程,而初始化则是调用一些回调函数进行 Bean 的一些预处理。 1、实例化 // Instantiate the bean. BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } Object bean = instanceWrapper.

Spring Cache 入门教程

1、缓存抽象 本文将带你了解如何使用 Spring Cache 来提高系统性能。 2、开始使用 Spring 提供的核心缓存抽象位于 spring-context 模块中。 <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.3</version> </dependency> 另外,还有一个名为 spring-context-support 的模块,它位于 spring-context 模块之上,并提供了更多由 EhCache 或 Caffeine 支持的 CacheManager。如果想使用这些模块作为缓存存储,则需要使用 spring-context-support 模块: <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>5.3.3</version> </dependency> 由于 spring-context-support 模块临时依赖于 spring-context 模块,因此 spring-context 不需要单独的依赖声明。 2.1、Spring Boot 如果是 Spring Boot 项目,可以利用 spring-boot-starter-cache Starter 来轻松添加缓存依赖项: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> <version>2.4.0</version> </dependency> 该 Starter 包含了 spring-context-support 模块。 3、启用缓存 只需在任何配置类中添加 @EnableCaching 注解,即可启用缓存功能: @Configuration @EnableCaching public class CachingConfig { @Bean public CacheManager cacheManager() { return new ConcurrentMapCacheManager("addresses"); } } 当然,也可以通过 XML 配置来启用缓存:

Spring 中的 @Autowired 注解

1、概览 从 Spring 2.5 开始,框架引入了注解驱动的依赖注入功能。该功能的主要注解是 @Autowired 。它允许 Spring 解析并注入所依赖的 Bean 到 Bean 中。 本文将带你了解如何启用自动装配以及自动装配 Bean 的各种方法,以及如何使用 @Qualifier 注解来解决 Bean 冲突以及潜在的异常情况。 2、启用 @Autowired 注解 Spring 框架支持自动依赖注入。换句话说,通过在 Spring 配置文件中声明所有 Bean 的依赖关系,Spring 容器可以自动装配依赖 Bean 之间的关系。这就是所谓的 Spring Bean 自动装配。 首先启用注解驱动注入来加载 Spring 配置(基于 Java 的配置): @Configuration @ComponentScan("com.baeldung.autowire.sample") public class AppConfig {} <context:annotation-config> 主要用于激活 Spring XML 文件中的依赖注入注解。 Spring Boot 还引入了 @SpringBootApplication 注解。这个注解相当于使用 @Configuration、@EnableAutoConfiguration 和 @ComponentScan。 在应用的 main 类中使用这个注解: @SpringBootApplication public class App { public static void main(String[] args) { SpringApplication.

Spring 中的 @Value 注解

1、概览 本文将带你了解 Spring 中的 @Value 注解。它用于向 Spring 管理的 Bean 中的字段注入值,可在字段或构造函数/方法上注解。 2、应用设置 创建一个简单的 Spring 应用来介绍该注解的不同用法。 首先,在 Properties 文件中定义想用 @Value 注解注入的值。 Properties 文件如下: value.from.file=Value got from the file priority=high listOfValues=A,B,C 然后,在配置类中定义一个 @PropertySource,指定 Properties 文件的名称以导入该 Properties 文件。 3、示例 通过注解注入 String(这种行为完全是多此一举,这里只是为了演示): @Value("string value") private String stringValue; 使用 @PropertySource 注解导入了 Properties 文件后,就可以使用 @Value 注解处理 Properties 文件中的值。 如下,注入 value.from.file 属性值: @Value("${value.from.file}") private String valueFromFile; 也可以使用相同的语法从系统属性中设置值。 假设定义了一个名为 systemValue 的系统属性,如下: @Value("${systemValue}") private String systemValue; 可以给未定义的属性提供默认值,如下: @Value("${unknown.param:some default}") private String someDefault; 如果同一个属性在系统属性和 Properties 文件中都被定义了,那么系统属性将会生效。

Spring 和 Spring Boot 中的属性配置

1、概览 本文将带你了解如何通过 Java 配置和 @PropertySource 在 Spring 中设置和使用 Properties,以及 Properties 在 Spring Boot 中的工作原理。 2、通过注解注册 Properties 文件 Spring 3.1 引入了新的 @PropertySource 注解,作为一种方便的机制,用于将属性源添加到环境中。 可以将此注解与 @Configuration 注解结合使用: @Configuration @PropertySource("classpath:foo.properties") public class PropertiesWithJavaConfig { //... } 另一种非常有用的注册新 Properties 文件的方法是使用占位符,它允许在运行时动态选择不同的文件: @PropertySource({ "classpath:persistence-${envTarget:mysql}.properties" }) ... 2.1、定义多个属性位置 @PropertySource 注解是可重复的(Java 8 的可重复注解特性)。因此,如果使用的是 Java 8 或更高版本,就可以使用此注解来定义多个属性位置: @PropertySource("classpath:foo.properties") @PropertySource("classpath:bar.properties") public class PropertiesWithJavaConfig { //... } 当然,也可以使用 @PropertySources 注解并指定一个 @PropertySource 数组。这不仅适用于 Java 8 或更高版本,也适用于任何受支持的 Java 版本: @PropertySources({ @PropertySource("classpath:foo.properties"), @PropertySource("classpath:bar.properties") }) public class PropertiesWithJavaConfig { //.