教程

在 Spring Boot 中使用 DataFieldMaxValueIncrementer 获取自增序列

DataFieldMaxValueIncrementer 是 spring-jdbc 项目中的一个接口。用于在应用中生成连续、自增的序列。可用于 主键ID、订单号、流水号 等等。 它基于数据库实现,主要有 2 大子类。 AbstractSequenceMaxValueIncrementer:用于支持序列(SEQUENCE)的数据库(如 Oracle),使用标准的数据库序列。 AbstractColumnMaxValueIncrementer:用于不支持序列的数据库(如,MYSQL),使用一张表来模拟。 spring-jdbc 已经为当前流行的关系型数据库提供了具体的实现,类体系结构如下: DataFieldMaxValueIncrementer |-AbstractDataFieldMaxValueIncrementer |-AbstractSequenceMaxValueIncrementer |-OracleSequenceMaxValueIncrementer # Oracle 数据库 |-PostgresSequenceMaxValueIncrementer # Postgres 数据库 |-SqlServerSequenceMaxValueIncrementer # SqlServer 数据库 |- ... # 还有一些其他的,这里忽略 |-AbstractColumnMaxValueIncrementer |-MySQLMaxValueIncrementer # MYSQL 数据库 DataFieldMaxValueIncrementer DataFieldMaxValueIncrementer 接口只有 3 个方法,很简单。 public interface DataFieldMaxValueIncrementer { int nextIntValue() throws DataAccessException; long nextLongValue() throws DataAccessException; String nextStringValue() throws DataAccessException; } nextIntValue:以 int 类型返回下一个序列。 nextLongValue:以 long 类型返回下一个序列。 nextStringValue:以 String 类型返回下一个序列。 使用 MySQLMaxValueIncrementer 本文以 MySQLMaxValueIncrementer 为例(毕竟 MYSQL 最流行)。

自定义从 JWT Claim 到 Spring Security Authority 的映射

1、简介 本文将带你了解如何自定义从 JWT(JSON Web Token)Claim 到 Spring Security 权限(Authority)的映射。 2、背景 基于 Spring Security 的应用接收到请求时,它会经过一系列步骤,本质上旨在实现两个目标。 认证请求,以便应用知道谁在访问它 决定通过身份认证的请求是否可以执行相关操作 对于使用 JWT 的应用,授权方面包括: 从 JWT payload(通常是 scope 或 scp Claim)中提取 Claim 值 将这些 Claim 声明映射到一组 GrantedAuthority 对象中 一旦 Security 引擎设置了这些权限,它就可以评估是否有任何访问限制适用于当前请求,并决定是否可以继续处理。 3、默认的映射 在开箱即用的情况下,Spring 使用一种直接的策略将 Claim 声明转换为 GrantedAuthority 实例。首先,它会提取 scope 或 scp Claim,并将其拆分成一个字符串列表。接下来,它会为每个字符串创建一个新的 SimpleGrantedAuthority,使用前缀 SCOPE_,后跟 scope 值。 接下来,创建一个简单的端点来演示这个策略。看看 Authentication 实例有哪些关键属性。 @RestController @RequestMapping("/user") public class UserRestController { @GetMapping("/authorities") public Map<String,Object> getPrincipalInfo(JwtAuthenticationToken principal) { Collection<String> authorities = principal.getAuthorities() .

跨域请求异常:The 'Acess-Control-Allow-Origin' header contains multiple values '*, *". but only one is allowed

1. 问题 在浏览器中进行跨域请求一个接口时报错如下: Access to XMLHttoRequest at ‘https://xxx.lzw.me/abc/getToken from origin “http://localhost:3001 has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: The ‘Acess-Control-Allow-Origin’ header contains multiple values ‘*, *”. but only one is allowed. 2. 原因 从报错信息上理解,是 Acess-Control-Allow-Origin 的值为 *, *,设置了多个。从 nginx 配置上查看设置的值是 *。以非跨域的方式请求该接口,则可以在 Response 中看到 header 信息中包含了两个 Acess-Control-Allow-Origin 的设置。那么原因就是在 nginx 之外的其它网关或应用程序中还设置了该 header 值。找到它并移除个性化的设置逻辑即可。 3. 解决方法 如果 nginx 出口网关的设置是多余的,CORS 在应用程序层已有管理,则可以直接移除此处的配置。 如果是应用程序内部针对该接口添加了设置,考虑到需支持允许所有接口跨域访问,应修改应用程序移除相关逻辑。 如果希望在 nginx 层作统一配置管理,也可在应用程序出口网关层作 header 过滤。例如针对 Spring zuul 网关的设置实例:

Spring 单例 Bean 如何处理并发请求?

1、Spring Bean 和 Java 堆内存 Java 堆是一个全局共享内存,应用中的所有运行线程都可以访问它。当 Spring 容器创建 Singleton Scope Bean 时,该 Bean 将存储在堆中。这样,所有并发线程都能指向同一个 Bean 实例。 2、如何处理并发请求? 举例来说,Spring 应用中有一个名为 ProductService 的单例 Bean: @Service public class ProductService { private final static List<Product> productRepository = asList( new Product(1, "Product 1", new Stock(100)), new Product(2, "Product 2", new Stock(50)) ); public Optional<Product> getProductById(int id) { Optional<Product> product = productRepository.stream() .filter(p -> p.getId() == id) .findFirst(); String productName = product.map(Product::getName) .orElse(null); System.out.printf("Thread: %s; bean instance: %s; product id: %s has the name: %s%n", currentThread().

Spring Cloud 通过配置文件禁用服务发现

1、概览 在 Spring Cloud 中可以通过配置文件来启用、禁用服务发现,而不需要改动代码。 2、 设置 Eureka Server 和 Eureka Client 先创建一个 Eureka 服务器和一个 Discovery Client。 Eureka 服务创建过程,略! 2.1、Discovery Client 设置 创建 “Discovery Client” 应用,在 Eureka Server 上进行注册。 在 pom.xml 中添加 Web 和 Eureka Client starter 依赖: <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> 还需要在 dependencyManagement 中添加 spring-cloud-starter-parent 依赖,用于定义 cloud 组件的版本。 如果使用 Spring Initializr 创建项目,这些参数已经设置好了。如果没有,可以手动添加到 pom.xml 文件中: <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-parent</artifactId> <version>${spring-cloud-dependencies.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <properties> <spring-cloud-dependencies.

Spring Boot 属性迁移

1、简介 本文将带你了解 spring-boot-properties-migrator 模块,它是 Spring 为促进 Spring Boot 升级而提供的支持。用于帮助迁移 application properties。 随着每个 Spring Boot 版本的升级,可能会有一些属性被标记为已弃用、不再支持或新引入的属性。Spring 为每个升级发布了详细的 变更日志。然而,阅读这些变更日志可能会有些繁琐。这就该 spring-boot-properties-migrator 出场了,它通过为我们的设置提供个性化的信息来帮助我们进行属性迁移。 让我们来看看如何使用。 2、Demo 应用 把 Spring Boot 应用从 2.3.0 升级到 2.6.3。 2.1、Properties 在演示应用中,有两个 properties 文件。在默认 properties 文件 application.properties 中,添加如下配置: spring.resources.cache.period=31536000 spring.resources.chain.compressed=false spring.resources.chain.html-application-cache=false dev Profile YAML 文件 application-dev.yaml: spring: resources: cache: period: 31536000 chain: compressed: true html-application-cache: true properties 文件包含了几个在 Spring Boot 2.3.0 和 2.6.3 之间被替换或移除的属性。 同时提供了 .properties 和 .yaml 文件,以便更好地进行演示。 2.2、添加依赖 添加 spring-boot-properties-migrator 模块。

处理 Spring Security 异常

1、概览 本文通过一个示例来带你了解如何处理 Spring Security Resource Server 产生的 Spring Security 异常。 2、Spring Security Spring Security 是 Spring 的一个子项目。它试图将 Spring 项目中的所有用户访问控制功能进行整合。访问控制允许限制特定用户或角色在应用中可以执行的选项。 在本例中,我们重点关注 Exception Handler 的配置。Spring Security 提供了三种不同的接口来实现这一目的并控制产生的事件: Authentication Success(认证成功)Handler Authentication Failure(认证失败)Handler Access Denied(拒绝访问)Handler 3、Security Configuration 首先,配置类必须创建一个 SecurityFilterChain Bean。它将负责管理应用的所有安全配置。因此,我们必须在这里引入 Handler。 定义所需的配置: @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.csrf() .disable() .httpBasic() .disable() .authorizeRequests() .antMatchers("/login") .permitAll() .antMatchers("/customError") .permitAll() .antMatchers("/access-denied") .permitAll() .antMatchers("/secured") .hasRole("ADMIN") .anyRequest() .authenticated() .and() .formLogin() .failureHandler(authenticationFailureHandler()) .successHandler(authenticationSuccessHandler()) .and() .exceptionHandling() .accessDeniedHandler(accessDeniedHandler()) .and() .logout(); return http.

在 Spring Boot 中处理 GraphQL 异常

1、概览 本文将带你了解如何在 Spring Boot 中处理 GraphQL 异常,以及 GraphQL 规范中关于错误响应的规定。 2、根据 GraphQL 规范进行响应 根据 GraphQL 规范,每个接收到的请求都必须返回一个格式良好的响应。这个格式良好的响应是一个 Map,包含了来自相应成功或失败的请求操作的数据或错误。此外,响应可能包含部分成功的结果数据和字段错误。 响应 Map 的关键 key 是 errors、data 和 extensions。 响应中的 errors 描述了请求操作过程中出现的任何异常、错误。如果没有错误发生,则响应中不得出现 errors 字段。接下来,我们将了解规范中描述的不同类型的错误。 data 字段描述了成功执行所请求操作的结果。如果操作是 Query,该字段就是 Query Root Operation 类型的对象。如果操作是 Mutation,则该字段是 Mutation Root Operation 类型的对象。 如果请求的操作在执行前就因信息缺失、验证错误或语法错误而失败,那么响应中就必须没有 data 字段。如果操作在执行过程中失败,且结果不成功,则 data 必须为 null。 响应 map 可能包含一个名为 extensions 的附加字段,它是一个 map 对象。该字段便于实现者在响应中提供他们认为合适的其他自定义内容。因此,对其内容格式没有额外的限制。 如果响应中没有 data 字段,则必须有 errors 字段,并且必须至少包含一个 error。此外,它还应说明失败的原因。 下面是一个 GraphQL 错误示例: mutation { addVehicle(vin: "NDXT155NDFTV59834", year: 2021, make: "Toyota", model: "Camry", trim: "XLE", location: {zipcode: "75024", city: "Dallas", state: "TX"}) { vin year make model trim } } 当违反唯一性约束时,错误响应如下所示:

Spring 获取所有带自定义注解的 Bean

1、概览 本文将带你了解如何在 Spring 中获取到所有带自定义注解的 Bean。 不同的 Spring 版本,实现方式也不同。 2、使用 Spring Boot 2.2 或更高版本 从 Spring Boot 2.2 起,可以使用 getBeansWithAnnotation 方法。 创建一个示例。首先,定义自定义注解。注意,要使用 @Retention(RetentionPolicy.RUNTIME) 元注解,以确保程序在运行时可以访问注解: @Retention(RetentionPolicy.RUNTIME) public @interface MyCustomAnnotation { } 现在,用注解定义第一个 Bean(还要添加 @Component 注解): @Component @MyCustomAnnotation public class MyComponent { } 然后,定义另一个带有注解的 Bean。不过,这次通过 @Configuration 中的 @Bean 方法来创建: public class MyService { } @Configuration public class MyConfigurationBean { @Bean @MyCustomAnnotation MyService myService() { return new MyService(); } } 现在,写一个测试,测试 getBeansWithAnnotation 方法能否获取到上述两个 Bean:

Spring RestClient 教程

1、简介 RestClient 是 Spring 6.1 M2 中引入的同步 HTTP 客户端,它取代了 RestTemplate。同步 HTTP 客户端以阻塞方式发送和接收 HTTP 请求和响应,这意味着它会等待每个请求完成后才继续下一个请求。 本文将带你了解 RestClient 的功能以及它与 RestTemplate 的比较。 2、RestClient 和 RestTemplate RestTemplate,顾名思义,是基于模板设计模式构建的。它是一种行为设计模式,用于在方法中定义算法的框架,允许子类为某些步骤提供特定的实现。虽然这是一种强大的模式,但它会导致需要进行方法覆写,这可能是不方便的。 为了改进这一点,RestClient 采用了 Fluent API。Fluent API 也是一种设计模式,它允许以一种使代码更易读和表达的方式进行方法链式调用,通常无需使用中间变量。 从创建一个基本的 RestClient 开始: RestClient restClient = RestClient.create(); 3、使用各种 HTTP 方法发起请求 与 RestTemplate 或其他 REST 客户端类似,RestClient 可以使用不同的 HTTP 方法发起请求。 创建一个用于操作的 Article 类: public class Article { Integer id; String title; // 构造函数和 getter/setter } 3.1、使用 GET 请求获取资源 GET 请求用于检索 Web 服务器上指定资源的数据,而不对其进行修改。通常,这是一个只读的操作! 获取服务器响应的字符串(String):