Spring

Spring Boot 3 和 Spring Framework 6.0 - 新功能

1、概览 本文简单地介绍了 Spring Boot 3 和 Spring Framework 6.0 中的新特性。 2、Java 17 之前已经支持Java 17,现在这个 LTS 版本成为基线版本。 由于 Java 本身不是本文的主题,这里只会列举对 Spring Boot 开发人员最重要的新特性。 2.1、Record Java Record(JEP 395),是一种快速创建数据类(Data Class)方式,即那些目的仅仅是包含数据并在模块之间传递数据的类,也被称为 POJO(Plain Old Java Objects,简单Java对象)和 DTO(Data Transfer Objects,数据传输对象)。 使用 Record 可以轻松创建不可变的 DTO: public record Person (String name, String address) {} 目前,在将它们与 Bean Validation 结合使用时,需要小心,因为构造函数参数不支持验证约束,例如在 Controller 方法中的 JSON 对象。 2.2、字符块 通过 JEP 378,现在就可以创建多行文本块,而无需在换行时连接字符串: String textBlock = """ Hello, this is a multi-line text block. """; 2.

构建自己的 Spring Initializr 服务

Spring Initializr 是 Spring 官方提供的一个用于快速创建和初始化 Spring 项目的在线工具。它可以让开发人员选择所需的 Spring 模块、版本、语言(Java、Kotlin 或 Groovy)和构建工具(Maven 或 Gradle),并生成一个基本的项目结构。现在大多数 IDE 都对 Spring Initializr 提供了支持! 官方的 Spring Initializr 服务(start.spring.io)部署在海外,在国内访问经常出现各种连网络接失败的问题。好在 Spring Initializr 是一个开源的项目,我们可以用它来构建自己的 Spring Initializr 服务。 Spring Initializr Github 仓库:https://github.com/spring-io/start.spring.io 构建 Spring Initializr 服务 Clone 项目 需要先在机器上安装 git 软件。 git clone https://github.com/spring-io/start.spring.io.git 该仓库下有三个工程,我们只关心其中2个: start-client 前端工程,使用 React 开发。 start-site 后端工程,是一个 Spring Boot 应用。 执行构建 项目使用 maven 构建,且提供了 mvnw(Maven Wrapper) 脚本,它是一个用于管理和运行 Maven 项目的工具。它的作用是在没有全局安装 Maven 的情况下,通过自动下载和配置特定版本的 Maven 来确保项目的构建和运行环境一致性。也就是说 不需要在本地安装 Maven,通过此脚本即可完成构建。 进入项目根目录,执行如下命令进行自动构建:

在同一个 Bean 中调用另一个 @Cacheable 方法时缓存失效

1、简介 在 Spring 中可以使用 @Cacheable 注解通过 AOP 技术地轻松为 Bean 中的方法启用缓存功能。但是,当你尝试在 Bean 的其他方法中直接调用 @Cacheable 方法时,缓存功能会失效。 本文将会解释这种情况下缓存功能失效的原因以及解决办法。 2、复现问题 首先,初始化一个启用缓存的 Spring Boot 应用。然后创建一个 MathService,其中的 square 方法注解了 @Cacheable: @Service @CacheConfig(cacheNames = "square") public class MathService { private final AtomicInteger counter = new AtomicInteger(); @CacheEvict(allEntries = true) public AtomicInteger resetCounter() { counter.set(0); return counter; } @Cacheable(key = "#n") public double square(double n) { counter.incrementAndGet(); return n * n; } } 接着,再在 MathService 中创建一个 sumOfSquareOf2 方法,调用两次 square 方法:

限制 Webclient 的并发请求数量

1、简介 本文介绍了一些限制 WebClient 并发请求数量的方式。 2、服务端 限制 WebClient 并发请求数量是为了避免服务器因大量并发请求而宕机。有些服务自身也提供了一些限制策略。 2.1、一个简单的 Controller 为了演示,先定义一个简单的 @RestController,它返回固定范围的随机数字: @RestController @RequestMapping("/random") public class RandomController { @GetMapping Integer getRandom() { return new Random().nextInt(50); } } 接下来,我们将模拟一些耗时的操作,并限制并发请求的数量。 2.2、服务器限制并发请求数 修改服务,模拟一个更真实的场景。 首先,限制服务器可接受的并发请求数,并在达到限制时抛出异常。 其次,增加处理响应的延迟,模拟耗时的操作。 创建 Concurrency 用于限制并发数量: public class Concurrency { public static final int MAX_CONCURRENT = 5; static final AtomicInteger CONCURRENT_REQUESTS = new AtomicInteger(); public static int protect(IntSupplier supplier) { try { if (CONCURRENT_REQUESTS.incrementAndGet() > MAX_CONCURRENT) { throw new UnsupportedOperationException("max concurrent requests reached"); } TimeUnit.

在 Spring 中使用 Groovy

1、概览 Groovy 是一种功能强大的动态 JVM 语言,具有众多特性。在 Spring 中使用 Groovy 可以大大提高应用程序的灵活性和可读性。从 Spring 4 开始 支持基于 Groovy 的配置。 在本教程中,我们将了解 Groovy 与 Spring 结合使用的各种方法。首先介绍了如何使用 Spring 提供的多个选项来创建 Groovy Bean 定义。接下来,了解如何使用 Groovy 脚本加载 Application Context。最后,学习如何使用 XML 和 GroovyScriptEngine 类将 Groovy 作为脚本执行(无需编译)。 2、Maven 依赖 首先,在 pom.xml 中定义 Groovy 依赖: <dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groovy</artifactId> <version>3.0.12</version> </dependency> 此外,还需要添加 GMavenPlus 插件来编译 Groovy 文件: <build> <plugins> <plugin> <groupId>org.codehaus.gmavenplus</groupId> <artifactId>gmavenplus-plugin</artifactId> <version>1.9.0</version> <executions> <execution> <goals> <goal>addSources</goal> <goal>addTestSources</goal> <goal>generateStubs</goal> <goal>compile</goal> <goal>generateTestStubs</goal> <goal>compileTests</goal> <goal>removeStubs</goal> <goal>removeTestStubs</goal> </goals> </execution> </executions> </plugin> </plugins> </build> 3、Bean 定义 传统上,开发人员通过 XML 配置来声明 Bean。这种方式后来被通过 Java 注解以编程方式定义 Bean 所取代。另一种声明 Bean 的方式是通过 Groovy 脚本。

@EnableMethodSecurity 注解

1、概览 使用 Spring Security,可以为应用配置身份认证和授权,以控制方法(如端点)的访问权限。 在 5.6 之前,使用 @EnableGlobalMethodSecurity 注解是标准的做法,在 5.6 之后,@EnableMethodSecurity 引入了一种更灵活的方法来配置方法安全授权(Method Security)。 在本教程中,我们将通过示例代码了解 @EnableMethodSecurity 如何代替 @EnableGlobalMethodSecurity,以及他们之间的区别。 2、@EnableMethodSecurity 和 @EnableGlobalMethodSecurity 让我们来看看方法授权如何与 @EnableMethodSecurity 和 @EnableGlobalMethodSecurity 配合使用。 2.1、@EnableMethodSecurity 通过 @EnableMethodSecurity,可以看到 Spring Security 为授权类型(Authorization Type)采用基于 Bean 的配置。 我们现在为每种类型都设置了一个配置,而不是全局配置。例如,Jsr250MethodSecurityConfiguration: @Configuration(proxyBeanMethods = false) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) class Jsr250MethodSecurityConfiguration { // ... @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) Advisor jsr250AuthorizationMethodInterceptor() { return AuthorizationManagerBeforeMethodInterceptor.jsr250(this.jsr250AuthorizationManager); } @Autowired(required = false) void setGrantedAuthorityDefaults(GrantedAuthorityDefaults grantedAuthorityDefaults) { this.jsr250AuthorizationManager.setRolePrefix(grantedAuthorityDefaults.getRolePrefix()); } } MethodInterceptor 主要包含一个 AuthorizationManager,它现在将 “检查和返回最终决策的 AuthorizationDecision 对象” 的责任委托给适当的实现,这里是 AuthenticatedAuthorizationManager。

在 Spring Context 中重新初始化 Singleton Bean

1、概览 在本教程中,我们将了解如何在运行时重新初始化 Spring Context 中的 Singleton Bean。 默认情况下,Singleton Scope Bean 不会在应用生命周期中重新初始化。不过,有时可能需要重新创建 Bean,例如在需要更新属性时。我们将介绍几种实现此目的的方法。 2、示例 一个小示例。创建一个 Bean,从配置文件中读取属性保存在内存中。如果配置文件中的属性值被修改,那么 Bean 就要重新加载配置。 2.1、Singleton Bean 首先创建 ConfigManager 类: @Service("ConfigManager") public class ConfigManager { private static final Log LOG = LogFactory.getLog(ConfigManager.class); private Map<String, Object> config; private final String filePath; public ConfigManager(@Value("${config.file.path}") String filePath) { this.filePath = filePath; initConfigs(); } private void initConfigs() { Properties properties = new Properties(); try { properties.load(Files.newInputStream(Paths.get(filePath))); } catch (IOException e) { LOG.error("Error loading configuration:", e); } config = new HashMap<>(); for (Map.

使用 Webclient 以流式下载大文件

1、简介 在本教程中,我们将学习如何使用 WebClient 从服务器以流式下载一个大文件。我们通过创建一个简单的 Controller 和两个客户端进行演示。最后,我们将了解如何以及何时使用 Spring 的 DataBuffer 和 DataBufferUtils 工具类。 2、服务器 创建一个可以下载文件的简单 Controller。 首先,构建一个 FileSystemResource,传递一个文件路径,然后将其封装为 ResponseEntity 的 body: @RestController @RequestMapping("/large-file") public class LargeFileController { @GetMapping ResponseEntity<Resource> get() { return ResponseEntity.ok() .body(new FileSystemResource(Paths.get("/tmp/large.dat"))); } } 其次,我们需要生成下载所用的示例文件,文件内容并不重要,所以我们使用 fallocate 在磁盘上生成一个指定大小的“空”内容文件。如下: fallocate -l 128M /tmp/large.dat 然后就可以开始编写客户端了。 3、WebClient 使用 ExchangeStrategies 处理大文件 先用一个简单而有限的 WebClient 下载文件。使用 ExchangeStrategies 来提高 exchange() 操作的可用内存限制。这样,就能操作更多字节,但仍受限于 JVM 可用的最大内存。 使用 bodyToMono() 从服务器获取 Mono<byte[]>: public class LimitedFileDownloadWebClient { public static long fetch(WebClient client, String destination) { Mono<byte[]> mono = client.

对 RestTemplate 上的 URI 变量进行编码

1、概览 我们经常遇到的一个编码问题是,URI 变量中包含一个加号(+)。例如,如果我们的 URI 变量值为 http://localhost:8080/api/v1/plus+sign,那么加号将被编码为空格,这可能会导致意外的服务器响应。 在本教程中,我们将学习如何在 Spring 的 RestTemplate 上对 URI 变量进行编码。 让我们来看看解决这个问题的几种方法。 2、项目设置 创建一个使用 RestTemplate 进行 API 调用的小项目。 2.1、Spring Web 依赖 在 pom.xml 中添加 Spring Web Starter 依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> 或者,可以使用 Spring Initializr 生成项目并添加依赖。 2.2、RestTemplate Bean 创建一个 RestTemplate Bean: @Configuration public class RestTemplateConfig { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } } 3、 调用 API 创建一个 service 类,调用公共 API http://httpbin.org/get。 API 会返回一个包含请求参数的 JSON 响应。例如,如果我们在浏览器上访问 URL https://httpbin.

Spring v6.0.12 发布

⭐ 新特性 ArithmeticException:在 @Scheduled(fixedDelay = Long.MAX_VALUE, timeUnit = TimeUnit.MINUTES) 上 long 溢出 #31210 对 RequestResponseBodyMethodProcessor 中的 resolveArgument 方法进行优化 #31175 更新 BeanValidationBeanRegistrationAotProcessor 中 validation exception 的日志级别 #31147 跳过在 PathMatchingResourcePatternResolver 中搜索不存在的目录。#31111 在 Argument[Type]PreparedStatementSetter 的 doSetValue() 中为 argValue 添加 @Nullable #31086 优化 StringUtils 中的 whitespace 检查 #31067 使用 simple JPARepository 时缺少代理提示 #31050 为 ReactiveAdapterRegistry 中的现有 adapter 注册 override #31047 DefaultListableBeanFactory#getBeanNamesForType 在解析 FactoryBean 时未考虑目标类型 #30987 让 spring-core 访问 org.jboss.vfs,以便在 WildFly 上支持 VfsUtils #30973 当 contentLength 可用时,在 StringHttpMessageConverter 中使用 readNBytes #30942 当数组长度不大于 1 时,跳过数组排序 #30934 避免对每个 SseEventBuilder 条目进行刷新 #30912 使用 protected 修饰 DefaultGenerationContext(DefaultGenerationContext, String) 构造函数 #30895 在 Spring MVC 中的 AbstractResourceResolver 子类中添加缺失的 @Nullable 注解 #30893 创建 scope Bean 实例时的性能瓶颈 #30883 对同一 Bean 类中的多个 @Autowired 方法进行确定性的 Bean 初始化 #30359 优化 ClassUtils#getMostSpecificMethod #30272 Hibernate native 查询代理缺少 native 提示 #29603 检查异常原因以支持 @PropertySource(ignoreResourceNotFound) #22276 调整 PayloadMethodArgumentResolver 中的验证元数据处理方式 #21852 🐞 Bug 修复 Spring Boot 启动失败 does not reside in the file system: manifoldclass://622488023/.