Spring

解决 Spring 中 “not eligible for auto-proxying” 警告

1、概览 本文将带你了解出现 “not eligible for auto-proxying” 警告的原因以及如何修复它。 2、“not eligible for auto proxying” 的原因 2.1、配置示例 首先,创建一个自定义 RandomInt 注解,使用它来注解应该插入指定范围内的随机整数的字段。 @Retention(RetentionPolicy.RUNTIME) public @interface RandomInt { int min(); int max(); } 其次,创建一个简单的 Spring 组件 DataCache 类。将一个可能被使用的随机 Group 分配给缓存,例如用于支持分片。为了实现这一点,使用自定义的注解来注解该字段: @Component public class DataCache { @RandomInt(min = 2, max = 10) private int group; private String name; } 现在,来看看 RandomIntGenerator 类。它是一个 Spring 组件,用于在 RandomInt 注解注解的字段中插入随机 int 值: @Component public class RandomIntGenerator { private Random random = new Random(); private DataCache dataCache; public RandomIntGenerator(DataCache dataCache) { this.

把 Spring Bean 设置为 Null

1、概览 本文将带你了解如何把 Spring Context 中的 Bean 设置为 null。在某些情况下,这可能很有用。例如,在测试时不想提供 Mock 对象。以及,在使用一些可选功能时,可能希望避免创建实现,并直接传递 null。 2、组件设置 有几种方法可以将 Bean 设置为 null,具体取决于 Context 的配置方式,本文主要考虑 XML、注解和 Java 配置的方式。 使用一个简单的设置,包含两个类: @Component public class MainComponent { private SubComponent subComponent; public MainComponent(final SubComponent subComponent) { this.subComponent = subComponent; } public SubComponent getSubComponent() { return subComponent; } public void setSubComponent(final SubComponent subComponent) { this.subComponent = subComponent; } } 本例将演示如何在 Spring Context 中将 SubComponent 设置为 null: @Component public class SubComponent {} 3、在 XML 配置中使用占位符 在 XML 配置中,可以使用一个特殊的占位符来标识 null 值:

Spring Bean 的命名

1、概览 当有多个相同类型的实现时,需要对 Spring Bean 进行不同的命名。这是因为如果 Bean 没有唯一的名称,Spring 在注入 Bean 时会出现歧义。 通过控制 Bean 的命名,可以告诉 Spring 我们想将哪个 Bean 注入到目标对象中。 本文将带你了解 Spring Bean 命名策略,以及如何为同一类型的 Bean 赋予多个名称。 2、默认 Bean 命名策略 Spring 为创建 Bean 提供了多种注解,可以在不同的级别使用。例如,可以在 Bean 类上放置一些注解,在创建 Bean 的方法上放置另一些注解。 首先,来看看 Spring 的默认命名策略。当只指定注解而不指定任何值时,Spring 是如何命名 Bean 的? 2.1、类级注解 首先从在类级别使用的注解的默认命名策略开始。Spring 会使用类名为 Bean 命名,并将第一个字母转换为小写。 举个例子: @Service public class LoggingService { } 如上,Spring 为 LoggingService 类创建了一个 Bean,并使用 loggingService 名称进行了注册。 这种默认的命名策略适用于所有用于创建 Spring Bean 的类级别注解,例如 @Component、@Service 和 @Controller。 2.2、方法级注解 Spring 提供了 @Bean 和 @Qualifier 等注解,可用于创建 Bean 的方法。

在 Spring 中使用 Thymeleaf 显示错误信息

1、概览 本文将带你了解如何在 Spring 应用中使用 Thymeleaf 模板来渲染错误信息。 我们会通过一个简单的 Spring Boot 项目来进行演示,该项目是一个 “用户注册” 应用,需要验证客户端传递的各个字段,还要处理全局错误。 2、Spring Boot 应用示例 创建一个简单的 Spring Boot 用户注册应用,需要一个 Controller、一个 Repository 和一个 Entity。 2.1、Maven 依赖 添加所有需要的 Spring Boot Starter:Web Mvc、Hibernate Validation、 Thymeleaf 和 JPA。 此外,还需要一个 H2 内存数据库依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> <version>1.4.200</version> </dependency> 2.2、实体 User 实体如下: @Entity public class User { @Id @GeneratedValue(strategy = GenerationType.

在集成测试中覆盖 Spring Bean

1、概览 在 Spring 集成测试中,我们可能想要覆盖应用的一些 Bean。通常,可以使用专门为测试定义的 Spring Bean 来实现。然而,在 Spring Context 中提供多个具有相同名称的 Bean,可能会遇到 BeanDefinitionOverrideException 异常。 本文将带你了解如何在 Spring Boot 应用中 Mock 或 Stub 集成测试的 Bean,同时避免 BeanDefinitionOverrideException。 2、在测试中使用 Mock 或 Stub 在深入了解细节之前,应该了解如何在测试中使用 Mock 或 Stub。这是一种强大的技术,可以确保应用不会出现错误。 也可以在 Spring 中采用这种方法。不过,只有在使用 Spring Boot 时才能直接模拟集成测试 Bean。 或者,也可以使用测试配置来 Stub 或 Mock bean。 3、Spring Boot 应用示例 创建一个简单的 Spring Boot 应用,包含了一个 Controller、Service 和一个 Configuration 类。 @RestController public class Endpoint { private final Service service; public Endpoint(Service service) { this.service = service; } @GetMapping("/hello") public String helloWorldEndpoint() { return service.

Spring Framework 6.1 正式发布

Spring Framework 6.1 中的新变化 核心容器 总体上 与虚拟线程和 JDK 21 兼容。 虚拟线程的配置选项:专用的 VirtualThreadTaskExecutor 和 SimpleAsyncTaskExecutor 上的虚拟线程模式,以及具有新线程每个任务策略和虚拟线程模式的类似的 SimpleAsyncTaskScheduler。 与 Project CRaC(JVM 检查点恢复)的生命周期集成(请参阅 相关文档)),包括 -Dspring.context.checkpoint=onRefresh 选项。 为 ThreadPoolTaskExecutor 和 ThreadPoolTaskScheduler 以及 SimpleAsyncTaskScheduler 集成了生命周期 暂停/恢复功能 和 并行优雅停机 功能。 可使用 -Dspring.context.exit=onRefresh 选项进行 AppCDS 训练运行,这是主要的用例;请参阅 31595。 可达性元数据贡献改进,为即将到来的 GraalVM 变动做准备:缺失的可达性元数据将很快报告为运行时异常,以获得更好的开发人员体验。参见 31213。 异步/响应式销毁方法(如 R2DBC ConnectionFactory);参见 26691。 异步/响应式 Cacheable 方法,包括缓存接口和 CaffeineCacheManager 中的相应支持;参见 17559 和 17920。 响应式 @Scheduled 方法(包括 Kotlin 正则表达式);参见 22924。 为每个 @Scheduled 方法选择特定目标调度器;见 20818。 用于一次性任务(仅有初始延迟)的 @Scheduled 方法;见 31211。 @Scheduled 方法的可观测性;见 29883。 Spring 不会为 @Async 或 @EventListener 注解的方法生成开箱即用的观测结果,但会帮助你为这些方法的执行传播上下文(例如带有当前 trace id 的 MDC 日志)。请参见新的 ContextPropagatingTaskDecorator、相关参考文档部分 和 issue 31130。 Validator 工厂方法,用于编程式 validator 实现;请参阅 29890。 Validator.

在 Spring 中使用 DeferredResult 实现长轮询

1、概览 长轮询(Long polling)通常用于在 B/S 架构的应用中保持客户端和服务器的连接,直到信息可用。通常在服务器必须调用下游服务以获取信息并等待结果时使用。 本文将带你了解如何在 Spring MVC 应用中使用 DeferredResult 实现长轮询,以及如何处理错误和超时。 2、使用 DeferredResult 进行长轮询 可以在 Spring MVC 中使用 DeferredResult 来异步处理入站 HTTP 请求。它允许释放 HTTP 工作线程来处理其他入站请求,并将工作转移到另一个工作线程。因此,它可以帮助处理需要较长计算或任意等待时间的请求,提高服务的可用性。 2.1、Publisher 使用 DeferredResult 创建一个 Publisher(发布者)应用。 首先,定义一个 Spring @RestController,它可以使用 DeferredResult,但不会将工作转移到另一个工作线程: @RestController @RequestMapping("/api") public class BakeryController { @GetMapping("/bake/{bakedGood}") public DeferredResult<String> publisher(@PathVariable String bakedGood, @RequestParam Integer bakeTime) { DeferredResult<String> output = new DeferredResult<>(); try { Thread.sleep(bakeTime); output.setResult(format("Bake for %s complete and order dispatched. Enjoy!", bakedGood)); } catch (Exception e) { // .

Spring 应用中的 HandlerInterceptor 和 Filter

1、概览 本文将会带你了解 Spring MVC HandlerInterceptor 和 Servlet Filter 之间的区别和各自的应用场景。 2、Filter Filter 是 Web 服务器的一部分,而不是 Spring 框架的组件。对于传入请求,可以使用 Filter 来操作甚至阻止请求到达任何 Servlet。反之亦然,也可以阻止响应到达客户端。 Spring Security 就是使用 Filter 进行身份认证和授权的一个很好的例子。要配置 Spring Security,只需添加一个 Filter,即 DelegatingFilterProxy。这样,Spring Security 就能拦截所有进出流量。这就是 Spring Security 可以在 Spring MVC 之外使用的原因。 2.1、创建 Filter 创建一个实现 jakarta.servlet.Filter 接口的 Filter 实现类: public class LogFilter implements Filter { private Logger logger = LoggerFactory.getLogger(LogFilter.class); @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { logger.info("Hello from: " + request.

Spring 中的 @AliasFor 注解

1、概览 本文将会带你了解 Spring 中的 @AliasFor 注解。 首先介绍一些框架中的使用示例,最后看看如何在自定义注解中使用 @AliasFor。 2、注解 @AliasFor 自 4.2 版起成为框架的一部分。多个 Spring 核心注解已更新为包含此注解。 可以将其用于装饰单个注解中的属性,或者在由元注解组成的注解中使用。元注解是可以应用于其他注解的注解。 在同一个注解中,使用 @AliasFor 来声明属性的别名,这样就可以交替使用它们。或者,可以在组合注解中使用 @AliasFor 来覆盖元注解中的属性。换句话说,当使用 @AliasFor 在组合注解中装饰属性时,它会覆盖其元注解中的指定属性。 许多核心 Spring 注解,如 @Bean、@ComponentScan、@Scope、@RequestMapping 和 @RestController 现在都使用 @AliasFor 来配置其内部属性别名。 @AliasFor 注解的定义如下: @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented public @interface AliasFor { @AliasFor("attribute") String value() default ""; @AliasFor("value") String attribute() default ""; Class<? extends Annotation> annotation() default Annotation.class; } 既可以隐式地使用这个注解,也可以显式地使用它。隐式使用仅限于注解内部的别名。相比之下,显式使用还可以用于元注解中的属性。 3、注解中的显示别名 参考 Spring 的核心注解 @ComponentScan: @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Repeatable(ComponentScans.class) public @interface ComponentScan { @AliasFor("basePackages") String[] value() default {}; @AliasFor("value") String[] basePackages() default {}; .

Spring 中的条件注解

1、简介 本文将带你了解 Spring 中的 @Conditional 注解。它用于根据特定条件来控制 Bean 的创建和注册。 2、声明条件 首先来看看在哪些情况下可以使用条件注解。 最常见的用法是包含或排除整个配置类: @Configuration @Conditional(IsDevEnvCondition.class) class DevEnvLoggingConfiguration { } 或者是一个 Bean: @Configuration class DevEnvLoggingConfiguration { @Bean @Conditional(IsDevEnvCondition.class) LoggingService loggingService() { return new LoggingService(); } } 如上,这样就可以根据特定条件(如环境类型或特定需求)来调整应用的行为。在上例中,只为开发环境初始化了额外的 LoggingService。 另一种方式是直接在组件类上放置条件。 @Service @Conditional(IsDevEnvCondition.class) class LoggingService { // ... } 可以将上述示例应用于任何使用 @Component、@Service、@Repository 或 @Controller 注解声明的 Bean。 3、预定义条件注解 Spring 自带一组预定义的条件注解。 首先,看看如何根据配置的属性值来创建组件: @Service @ConditionalOnProperty( value="logging.enabled", havingValue = "true", matchIfMissing = true) class LoggingService { // ... } 第一个属性 value 指定了要匹配的配置属性。第二个属性 havingValue 定义了该条件所需的值。最后,matchIfMissing 属性告诉 Spring,如果参数缺失,是否应匹配该条件。