教程

使用 Key 和 SecretKey 签发 JWT Token

1、概览 JSON Web Tokens(JWT)是用于保护无状态应用的事实上的标准。Spring Security 框架提供了集成 JWT 以保护 REST API 的方法。 本文将带你了解如何在 Spring Boot 应用中创建 SecretKey 实例来签发和验证 JWT。 2、项目设置 2.1、Maven 依赖 首先,在 pom.xml 中添加 spring-boot-starter-web、spring-boot-starter-security、spring-boot-starter-data-jpa 和 h2 数据库依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>3.2.3</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> <version>3.2.3</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> <version>3.2.3</version> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <version>2.2.224</version> </dependency> Spring Boot Starter Web 提供了用于构建 REST API 的 API。Spring Boot Starter Security 用于提供身份认证和授权。h2 是一个内存数据库,方便快速开发。 然后,还要在 pom.xml 中添加 jjwt-api、jjwt-impl 和 jjwt-jackson 依赖: <dependency> <groupId>io.

Spring 在运行动态地创建 Prototype Scope Bean

1、概览 本文将带你了解如何在 Spring 中使用运行时参数创建一个 Prototype Scope Bean。 在 Spring 中,有许多不同的 Bean Scope,默认的 Scope 是 Singleton(单例)。Singleton Scope 的 Bean 将始终产生相同的对象。 如果每次都需要从容器中获得一个新实例,可以使用 Pototype Scope Bean。然而,在大多数情况下,如果我们想要从一个 Singleton Bean 实例化 Pototype Bean,或者将动态参数传递给 Pototype Bean,可能会遇到一些问题。 2、使用动态参数创建 Prototype Bean 有时,我们需要在每次初始化时将动态参数作为输入来初始化 Spring Bean。通过 Spring,可以使用多种方法为 Prototype Bean 分配不同的动态参数。 首先,创建一个 Prototype Bean Employee : public class Employee { private String name; public Employee(String name) { this.name = name; } public void printName() { System.out.println(name); } } 为 Employee Prototype Bean 创建一个配置:

Spring Boot 启用虚拟线程

本文将带你了解什么是虚拟线程、虚拟线程是如何提高应用吞吐量的,以及如何为 Spring Boot 应用启用虚拟线程。 并发编程的演化 线程 总所周知,线程(Thread)是计算机中的最小执行单元,由操作系统直接进行调度,每个线程都有自己的执行路径和执行状态,可以独立地运行和并发执行多个任务。 线程是一种重量级的资源,线程的创建、销毁以及在多个线程之间切换都需要耗费 CPU 时间,一个系统可以同时创建、调度的线程数量有限。所以,现在应用基本上都会使用 线程池 来解决这个问题,通过池化线程,可以减少线程频繁 创建 和 销毁 的成本。 例如,Servlet 容器(Tomcat、Undertow、Jetty)的并发模型就是通过线程池,为每一个请求分配一个线程池中的线程进行处理。但是,一旦涉及到阻塞操作(IO、网络请求),当前线程就会被挂起进入等待状态,这个线程就不能去执行其他任务。这就导致了,使用传统线程池并发模型的服务器能同时处理的请求有限。 而,当代 Web 应用基本上都是 IO 密集形应用,请求中执行的业务往往涉及到与数据库进行交互、调用远程服务(Socket IO),本地磁盘文件读写等等,因此使用阻塞式线程是非常低效的。 异步非阻塞编程 为了解决传统线程在执行 IO 操作时由于阻塞导致的低效,于是,开始有了一种 响应式异步非阻塞 的编程模型。 在 Java 界,这类优秀的框架很多,如 Netty、WebFlux、Vert.x 等等。它们都提倡一个东西,那就是:异步非阻塞,只要是涉及到 IO、阻塞的地方,当前线程不会等待操作执行完毕,会立即返回去执行其他可执行的任务。这时候,就需要通过监听器(Listener),或者回调(Callback)来获得操作最终的执行结果。 以 Netty 为例,伪代码如下: Channel channel = ...; // 异步写入数据到 Socket channel.write("Hello").addListener(future -> { if(future.isSuccess()) { // 写入成功 } else { // 写入失败 } }); 其中 channel.write("Hello") 用于向 Socket 写入数据,这就是典型的阻塞操作,在传统阻塞式编程中,该方法就会阻塞,直到数据被完全写入到 Socket 中。但是,在 Netty 中,在 write 方法执行后线程就会立即返回一个 ChannelFuture 对象,不会等待写入完成,因此这个线程仍然可以继续执行其他可执行的任务。

根据属性(Properties)动态注册 Spring Bean

1、概览 本文将带你了解如何根据自定义属性动态注册 Bean。 主要是学习 BeanDefinitionRegistryPostProcessor 接口,以及如何使用它将 Bean 添加到 Application Context 中。 2、设置 创建一个简单的 Spring Boot 应用。 首先,定义一个要动态注册的 Bean。然后,提供一个属性来决定如何注册 Bean。最后,定义一个配置类,它将根据自定义属性注册 Bean。 2.1、依赖 添加 Maven 依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>3.2.3</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <version>3.2.3</version> <scope>test</scope> </dependency> 添加 spring-boot-starter 和 spring-boot-starter-test 依赖。 2.2、Bean 类 接下来,根据自定义 application properties 定义要注册的 ApiClient: public class ApiClient { private String name; private String url; private String key; // Getter、Setter 和构造函数 public String getConnectionProperties() { return "Connecting to " + name + " at " + url; } } 假设我们希望根据提供的属性使用这个 Bean 连接到不同的 API。我们不想为每个 API 创建类定义,而是希望动态地为每个 API 定义属性并注册该 Bean。

Querydsl 和 JPA Criteria

1、概览 Querydsl 和 JPA Criteria 是在 Java 中构建类型安全查询的流行框架。它们都提供了以静态类型表达查询的方法,使编写与数据库交互的高效、可维护代码变得更容易。本文将从多个角度对它们进行比较。 2、设置 首先,设置依赖。在所有示例中,都使用 HyperSQL 数据库: <dependency> <groupId>org.hsqldb</groupId> <artifactId>hsqldb</artifactId> <version>2.7.1</version> </dependency> 添加 maven-processor-plugin,使用 JPAMetaModelEntityProcessor 和 JPAAnnotationProcessor 为框架生成元数据。配置如下: <plugin> <groupId>org.bsc.maven</groupId> <artifactId>maven-processor-plugin</artifactId> <version>5.0</version> <executions> <execution> <id>process</id> <goals> <goal>process</goal> </goals> <phase>generate-sources</phase> <configuration> <processors> <processor>org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor</processor> <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor> </processors> </configuration> </execution> </executions> <dependencies> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-jpamodelgen</artifactId> <version>6.2.0.Final</version> </dependency> </dependencies> </plugin> <persistence-unit name="com.baeldung.querydsl.intro"> <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> <properties> <property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver"/> <property name="hibernate.connection.url" value="jdbc:hsqldb:mem:test"/> <property name="hibernate.connection.username" value="sa"/> <property name="hibernate.connection.password" value=""/> <property name="hibernate.hbm2ddl.auto" value="update"/> <property name="hibernate.

Spring 实现两级缓存

1、概览 缓存数据意味着我们的应用无需访问速度较慢的存储层,从而提高了性能和响应速度。我们可以使用任何内存实现库(如 Caffeine)来实现缓存。 虽然这样做可以提高数据检索的性能,但如果应用部署在多个副本上,那么实例之间就无法共享缓存。为了解决这个问题,可以引入一个分布式缓存层,所有实例都可以访问它。 本文将带你了解如何在 Spring 中使用 Spring 的缓存支持(spring-cache)实现两级缓存,以及在本地缓存层缓存失效时如何调用分布式缓存层。 2、示例 Spring Boot 应用 创建一个简单的应用,调用数据库获取一些数据。 2.1、Maven 依赖 首先,添加 spring-boot-starter-web 依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>3.1.5</version> </dependency> 2.2、实现 Service 实现一个 Spring Service,从 Repository 中获取数据。 首先,创建 Customer 实体类: public class Customer implements Serializable { private String id; private String name; private String email; // 标准 Getter / Setter } 然后,实现 CustomerService 类和 getCustomer 方法: @Service public class CustomerService { private final CustomerRepository customerRepository; public Customer getCustomer(String id) { return customerRepository.

在 Servlet Filter 中自动装配 Spring Bean

1、简介 Servlet Filter(过滤器)为拦截和处理传入的请求提供了强大的机制。 本文将会带你了解在 Servlet Filter 中无缝获取 Spring Bean 的各种方法,这种需求在 Spring Web 应用中很常见。 2、Servlet Filter 中 @Autowired 的限制 Spring 的依赖注入机制 @Autowired,是一种方便的方式来将依赖注入到由 Spring 管理的组件中。但它无法与 Servlet Filter 完美配合。这是因为 Servlet Filter 是由 Servlet 容器初始化的,通常是在 Spring 的 ApplicationContext 完全加载和初始化之前 。 因此,当容器实例化 Servlet Filter 时 ,Spring Context 可能尚未可用,从而导致在尝试使用 @Autowired 注解时 ,依赖为 null 或未初始化。 3、项目设置 创建一个通用的 LoggingService,它将自动装配到 Filter 中: @Service public class LoggingService { private final Logger logger = LoggerFactory.getLogger(this.getClass()); public void log(String message,String url){ logger.info("Logging Request {} for URI : {}",message,url); } } 然后,创建 Filter,拦截传入的 HTTP 请求,并使用 LoggingService 依赖记录 HTTP 方法和 URI 的详细信息:

Spring Data JPA 中的 Refresh 和 Fetch

1、简介 Java Persistence API(JPA)是连接 Java 对象和关系数据库的桥梁,允许我们无缝地持久化和检索数据。本文将会带你了解各种策略和技术,以便在 JPA 中进行保存操作后有效地刷新(Refresh)和获取(Fetch)实体。 2、了解 Spring Data JPA 中的实体管理 在 Spring Data JPA 中 ,实体管理围绕 JpaRepository 接口展开,该接口是与数据库交互的主要机制。通过继承了 CrudRepository 的 JpaRepository 接口 ,Spring Data JPA 为实体的持久化、检索、更新和删除提供了一套强大的方法。 此外, Spring 容器会将实体管理器(EntityManager) 自动注入这些 repository 接口。该组件是嵌入 Spring Data JPA 的 JPA 基础架构的一个组成部分,可促进与底层持久化上下文(Persistence Context)的交互和 JPA 查询的执行。 2.1、Persistence Context JPA 中的一个关键组件是持久化上下文(Persistence Context)。把这个上下文想象成一个临时存放区,JPA 在这里管理检索或创建实体的状态。 它可以确保: 实体是唯一的: 在任何时候,上下文中都只存在一个具有特定主键的实体实例。 跟踪更改: 实体管理器(EntityManager)会跟踪上下文中对实体属性所做的任何修改。 保持数据一致性: 在事务处理期间,实体管理器会将上下文中的更改与底层数据库同步。 2.2、JPA 实体的生命周期 JPA 实体有四个不同的生命周期阶段: 瞬时(New)、托管(Managed)、删除(Removed)和游离(Detached)。 当我们使用实体的构造函数创建一个新实体实例时,它处于 “瞬时” 状态。我们可以通过检查实体的 ID(主键)是否为 null 来验证这一点: Order order = new Order(); if (order.

写给 Java / Spring Boot 开发者的 Golang 教程

我使用 Java 很多年了,我非常喜欢 Java 及其生态系统。在 Java 生态系统中,Spring Boot 是我构建 Java 应用的首选框架。 前不久,我在一个项目中使用了 Golang,起初我对它的感觉褒贬不一。但用得越多,就越喜欢它。 每当我尝试学习一种新的语言或框架时,我都会尝试将新框架/语言的概念映射到我已经熟悉的框架/语言中。这有助于我更快地理解新框架/语言的生态系统。 学习任何新知识的最好方法就是用它来构建一些东西。因此,在本文中,我将带你了解如何使用 Go 构建一个 REST API,包括配置管理、日志记录、数据库访问等各个方面。 本文并不会涉及到 Golang 的基础知识,如如何声明变量、循环、函数等。 使用的库 Go 没有类似 Spring Boot 的框架。通常,Go 开发人员喜欢使用标准库,只添加必要的库来构建应用。 本文将会使用到以下库来在 Go 中构建一个 REST API: Gin Web Framework - Web 框架 Viper - 配置库 zap - 日志库 pgx - Go 的 PostgreSQL 驱动程序和工具包 golang-migrate - 数据迁移 安装 Go 和工具 你可以从 https://go.dev/doc/install 下载并安装 Go。安装完成后,将 Go bin 目录添加到 PATH 环境变量中。 export GOPATH=$HOME/go export PATH="$PATH:$GOPATH/bin" 你可以使用 VS Code、IntelliJ IDEA Ultimate(使用 Go 插件)、GoLand 或任何其他 IDE 进行 Go 开发。

Spring Data 3 中的新 CRUD Repository 接口

1、概览 本文将带你了解 Spring Data 3 中引入的新 Repository 接口。 Spring Data 3 引入了基于 List 的 CRUD Repository 接口,可用于替换返回 Iterable 的现有 CRUD Repository 接口。此外,分页和排序接口默认不继承原始 CRUD Repository,而是由用户自己选择。接下来看看这些接口与现有接口有何不同,以及如何使用它们。 2、项目设置 首先创建一个 Spring Boot 应用,其中包含一个简单的实体和将使用新接口的 Repository。 2.1、依赖 首先,为项目添加所需的 spring-boot-starter-data-jpa 依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> <version>3.2.3</version> </dependency> 除此之外,还要配置数据库。可以使用任何 SQL 数据库,本文将使用 H2 内存数据库: <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> </dependency> 2.2、Entity 创建一个 Book 实体: @Entity public class Book { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String title; private String author; private String isbn; // 构造器、Getter、Setter } 有了实体后,来看看如何使用新接口与数据库交互。