Spring

在 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,如果参数缺失,是否应匹配该条件。

在 Spring 应用中的 Service 层进行验证

1、概览 本文将带你了解如何在 Spring 应用的 Service 层中使用 Spring Validation 进行校验。 2、应用分层 Spring Web 应用通常分为如下几层: Consumer 层或 Web 层是 Web 应用程序的最上层。它负责解析用户的输入并提供适当的响应。其他层抛出的异常也必须由 Web 层处理。由于 Web 层是应用程序的入口点,因此它负责身份认证,是防止未授权用户的第一道防线。 在 Web 层之下是 Service 层。它充当事务屏障,同时承载应用和基础设施服务。Service 层的公共 API 由应用服务提供。它们通常作为事务边界,并负责授权事务。基础设施服务提供与外部工具(包括文件系统、数据库和电子邮件服务器)连接的 “管道代码”。这些方法通常被多个应用服务使用。 Web 应用的最底层是持久层。换句话说,它负责与数据存储进行交互。 3、Service 层的验证 Service 层是应用中的一个层,用于在 Controller 和持久层之间进行通信。此外,业务逻辑也存储在 Service 层中。其中特别包括验证逻辑。Model 状态用于 Controller 层和 Service 层之间的通信。 在业务层进行验证逻辑有其优点和缺点。Spring 的验证(和数据绑定)架构并不排除任何一种方式。验证未绑定在 Web 层,这易于本地化,并且允许使用任何可用的 Validator。 此外,客户端输入数据并不总是通过 REST Controller 处理,如果不在 Service 层进行验证,非法数据可能会通过,引发多个问题。 在这种情况下,可以使用标准的 Java JSR-303 验证方案。 4、示例 使用 Spring Boot 开发一个简单的用户注册应用。 4.1、Domain 只有 name、age、phone 和 password 属性:

在 Spring MVC 中设置 JSON Content Type

1、简介 Content Type 表示请求/响应数据的媒体类型(Media Type)。当 Conroller 收到 Web 请求时,它会根据 Content Type 解析请求数据,然后根据 Content Type 响应数据。目前在 REST 中最流行的 Content Type 就是 JSON。 本文将会带你了解如何在 Spring MVC 中设置请求和响应的 Content Type。 2、@RequestMapping 注解 简而言之,@RequestMapping 是将 Web 请求映射到 Spring Controller 的重要注解。它有各种属性,包括 HTTP 方法、请求参数、Header 和媒体类型。 一般来说,媒体类型分为两类:消费(请求)、生产(响应)。也可以在 Spring 中定义自定义媒体类型。 其目的在于限制 Handler 可消费、生产的媒体类型。 2.1、请求的媒体类型 通过 consumes 属性指定 Controller 可接受的媒体类型,可以有多个。 定义一个简单的端点: @RequestMapping(value = "/greetings", method = RequestMethod.POST, consumes="application/json") public void addGreeting(@RequestBody ContentType type, Model model) { } 如果 Controller 不支持客户端指定的媒体类型,则会返回 HTTP “415 Unsupported Media Type” 错误。

Spring 中的 Context Path 与 Servlet Path

1、简介 DispatcherServlet 在 Spring 应用中扮演着重要角色,它为应用提供了一个入口点。Context Path 定义了终端用户访问应用的 URL。 本文将带你了解 Spring 中 Context Path(上下文路径)与 Servlet Path(Servlet 路径)的区别。 2、Context Path 简而言之,Context Path 是访问 Web 应用时使用的名称。它是应用的根路径。默认情况下,Spring Boot 在 ROOT 上下文路径("/")上提供服务。 因此,默认情况下,Spring Boot 应用可以通过 http://localhost:8080/ 访问。 不过,在某些情况下,我们可能需要更改应用的 Context。配置 Context Path 有多种方法,最简单的方式就是通过位于 src/main/resources 文件夹下的 application.properties 进行配置。 server.servlet.context-path=/demo 如上,此时应用的主页为: http://localhost:8080/demo 特别是在把应用部署到外部服务器时,往往需要修改应用的 Context Path,以便于其他一起部署的应用分开来。 3、Servlet Path Servlet Path 表示 DispatcherServlet 的 Path。DispatcherServlet 是一个实际的 Servlet,继承自 HttpSerlvet 。默认值与 Context Path 类似,即(“/”): spring.mvc.servlet.path=/ 在 Boot 的早期版本中,该属性位于 ServerProperties 类中,名称为 server.servlet-path=/。 从 2.

使用 WebClient 上传文件

1、概览 文件上传是现在应用中很常见的需求,从 Spring 5 开始可以通过响应式上传文件。有了响应式编程的加持,能够使用较少的线程和背压(Backpressure)机制,以非阻塞的方式进行上传。 本文将带你了解使用 WebClient(一种非阻塞、响应式的 HTTP 客户端)通过 BodyInserters 上传文件的两种不同方法。WebClient 是名为 Project Reactor 的响应式编程库的一部分。 2、使用 WebClient 上传文件 首先,在项目中添加 spring-boot-starter-webflux 依赖: <dependency> <groupId>org.springframework.boot</groupId>. <artifactId>spring-boot-starter-webflux</artifactId> </dependency> 2.1、上传单个文件 首先,声明上传地址的 URL: URI url = UriComponentsBuilder.fromHttpUrl(EXTERNAL_UPLOAD_URL).build().toUri(); 比方说,在本例中要上传 PDF。因此使用 MediaType.APPLICATION_PDF 作为 ContentType。上传端点会返回一个 HttpStatus。由于只希望得到一个结果,所以将其封装在一个 Mono 中: Mono<HttpStatus> httpStatusMono = webClient.post() .uri(url) .contentType(MediaType.APPLICATION_PDF) .body(BodyInserters.fromResource(resource)) .exchangeToMono(response -> { if (response.statusCode().equals(HttpStatus.OK)) { return response.bodyToMono(HttpStatus.class).thenReturn(response.statusCode()); } else { throw new ServiceException("Error uploading file"); } }); 调用这个方法的方法也可以返回一个 Mono,可以一直进行下去,直到真正需要访问结果为止。就绪后,可以在 Mono 对象上调用 block() 方法。

Spring 中 applicationContext.xml 与 spring-servlet.xml 的区别

1、简介 所有 Java Web 框架都建立在 Servlet Api 的基础之上。在基于 Spring 开发的 Java Web 应用中,有三个文件起着至关重要的作用。通常,按以下顺序将它们串联起来:web.xml -> applicationContext.xml -> spring-servlet.xml。 本文将带你了解 applicationContext.xml 和 spring-servlet.xml 之间的区别。 2、applicationContext.xml 反转控制(IoC)是 Spring 的核心。在使用 IoC 的框架中,通常由容器负责实例化、创建和删除对象。在 Spring 中,applicationContext 就扮演着 IoC 容器的角色。 在开发标准 J2EE 应用时,会在 web.xml 文件中声明 ContextLoaderListener。此外,还定义了一个 contextConfigLocation 来指定 XML 配置文件。 <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext*.xml</param-value> </context-param> 应用启动时,Spring 会加载该配置文件,并使用它创建 WebApplicationContext 对象。如果没有 contextConfigLocation,默认情况下,系统将查找 /WEB-INF/applicationContext.xml 来加载。 简而言之,applicationContext 是 Spring 的核心接口。它为应用提供配置信息。 在该文件中,提供了与应用相关的配置。通常,这些配置包括基本数据源、属性占位符文件(Property Place Holder)和用于项目本地化的消息源(Message Source),以及其他增强功能。 示例如下: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:c="http://www.springframework.org/schema/c" xmlns:p="http://www.

使用 Spring 和 Hibernate 进行表分区

简介 本文将带你了解如何使用 Spring 和 Hibernate 实现表分区。 表分区的目标是将一个大型表分割为多个较小的分区表,以便关联的表和索引记录可以放入内存缓冲池,从而实现更高效的查找或扫描操作。 使用 PostgreSQL 进行表分区 PostgreSQL 为 表分区 提供了三种策略: 列表分区(List Partitioning) 范围分区(Range Partitioning) Hash 分区(Hash Partitioning) 本例使用列表分区,按大洲来划分数据表。 例如,users 分区如下: CREATE TABLE users ( id bigint NOT NULL, first_name varchar(255), last_name varchar(255), registered_on timestamp(6), partition_key varchar(255), PRIMARY KEY (id, partition_key) ) PARTITION BY LIST (partition_key) CREATE TABLE users_asia PARTITION OF users FOR VALUES IN ('Asia') CREATE TABLE users_africa PARTITION OF users FOR VALUES IN ('Africa') CREATE TABLE users_north_america PARTITION OF users FOR VALUES IN ('North America') CREATE TABLE users_south_america PARTITION OF users FOR VALUES IN ('South America') CREATE TABLE users_europe PARTITION OF users FOR VALUES IN ('Europe') CREATE TABLE users_australia PARTITION OF users FOR VALUES IN ('Australia') posts 表分区如下: