教程

Thymeleaf 中 th:text 和 th:value 之间的区别

1、概览 Thymeleaf 是一种流行的 Java 模板引擎,它允许我们创建动态网页。它提供了多种属性,用于将数据从模型绑定到视图。 在本教程中,我们将了解 Thymeleaf 中 th:text 和 th:value 属性之间的主要区别。 2、th:text 属性 Thymeleaf 中的 th:text 属性用于设置元素的文本内容。 它还取代了标准的 HTML text 属性。因此,我们可以把它放在任何支持文本内容的 HTML 元素中,如标题、段落、标签等。 我们还可以使用该属性来显示动态文本内容,例如网页上的标题。 假设我们想在 HTML 页面上显示 controller 提供的 title 属性。 首先,让我们创建一个 controller 类和一个指定模型属性的方法: @GetMapping public String show(Model model) { model.addAttribute("title", "Baeldung"); return "attributes/index"; } 接下来,我们将在标题元素中显示值: <h1 th:text="${title}"/> 在这里,Thymeleaf 会计算表达式 ${title},并将该值插入标题元素。 我们将得到的 HTML 如下: <h1>Baeldung</h1> 此外,与标准 HTML text 属性不同,th:text 属性支持表达式。除了变量,这些表达式还可能包括运算符和函数。 例如,让我们在没有提供 title 属性的情况下指定默认值: <h1 th:text="${title} ?: 'Default title'"/> 3、th:value 属性 另一方面,th:value 属性用于设置通常需要用户输入的元素的值。输入框、复选框、单选按钮和下拉按钮等元素都属于此类。

在 Spring Boot 中使用 OpenAI ChatGPT API

1、概览 在本教程中,我们将学习如何在 Spring Boot 中调用 OpenAI ChatGPT API。我们将创建一个 Spring Boot 应用程序,通过调用 OpenAI ChatGPT API 来生成对提示的回复。 2、OpenAI ChatGPT API 在开始本教程之前,让我们先了解一下本教程中将使用的 OpenAI ChatGPT API。我们将调用 create chat completion API 来生成对提示的回复。 2.1、API 参数和认证 让我们来看看 API 强制的请求参数: model - 是我们将向其发送请求的模型版本。该模型有几个版本。我们将使用 gpt-3.5-turbo 模型,它是 该模型的最新公开版本。 messages - 发送给模型的提示。每条 message 都需要两个字段:role 和 content。role 字段指定信息的发送者。在请求中是 user,在回复中是 assistant。content 字段是实际的消息。 API 调用需要认证,我们需要生成一个 OpenAI API key。在调用 API 时在 Authorization 头中设置该 key。 cURL 格式的请求示例如下: $ curl https://api.openai.com/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $OPENAI_API_KEY" \ -d '{ "model": "gpt-3.

在 Spring 6 中使用虚拟线程(Virtual Threads)

1、简介 在这个简短的教程中,我们将了解如何在 Spring Boot 中使用虚拟线程(Virtual Threads)。 虚拟线程是 Java 19 的 预览特性,这意味着它们将在未来 12 个月内被纳入 JDK 的正式版本中。虚拟线程最初是由 Loom 项目引入的,在 Spring 6 中,开发人员可以一睹为快了。 首先,我们将了解 “平台线程” 与 “虚拟线程” 的主要区别。接下来,我们将使用虚拟线程从头开始构建一个 Spring-Boot 应用。最后,我们会进行一个测试,测试 web 应用的吞吐量提升了多少。 2、虚拟线程和平台线程 主要区别在于,虚拟线程在运行周期内不依赖操作系统线程:它们与硬件脱钩,因此被称为 “虚拟”。这种解耦是由 JVM 提供的抽象层赋予的。 虚拟线程的运行成本远低于平台线程。它们消耗的内存要少得多。这就是为什么我们可以创建数百万个虚拟线程而不会出现内存不足的问题,而标准平台(或内核)线程只能创建数百个。 从理论上讲,这赋予了开发人员一种超级能力:无需依赖异步代码即可管理高度可扩展的应用程序。 3、在 Spring 6 中使用虚拟线程 从 Spring Framework 6(和 Spring Boot 3)开始,虚拟线程功能正式全面可用,但虚拟线程是 Java 19 的预览特性。这意味着我们需要告诉 JVM 我们想在应用程序中启用虚拟线程。由于我们使用 Maven 构建应用程序,因此要确保在 pom.xml 中包含以下代码: <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>19</source> <target>19</target> <compilerArgs> --enable-preview </compilerArgs> </configuration> </plugin> </plugins> </build> 从 Java 的角度来看,要使用 Apache Tomcat 和虚拟线程,我们需要一个简单的配置类和几个 Bean:

Spring Boot 应用中的事务处理

在 Spring 应用中,无论使用 JdbcTemplate 还是 JPA/Hibernate 或 Spring Data JPA,都需要处理数据库事务。 数据库事务是一个事务单元,它要么全部完成,要么都不完成,并使数据库处于一致状态。在实现数据库事务时,需要考虑到 ACID(原子性、一致性、隔离性、持久性)属性。 让我们了解一下如何在 Spring Boot 应用中处理数据库事务。 使用 JDBC 进行事务处理 首先,让我们快速了解一下我们通常是如何在普通 JDBC 中处理数据库事务的。 class UserService { void register(User user) { String sql = "..."; Connection conn = dataSource.getConnection(); // <1> try(conn) { // <6> conn.setAutoCommit(false); // <2> PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setString(1, user.getEmail()); pstmt.setString(2, user.getPassword()); pstmt.executeUpdate(); // <3> conn.commit(); // <4> } catch(SQLException e) { conn.rollback(); // <5> } } } 在上述代码片段中:

RestTemplate 上传文件

在调用第三方Api服务的时候,如果涉及到文件上传,就需要自己通过 Http 客户端发起 Multipart 请求来上传文件。 在 Spring 应用中比较流行的 Http 客户端就是 RestTemplate。本文将会指导你如何用 RestTemplate 发起 Multipart 请求来上传文件。 服务端 在服务端创建一个用于测试的 FileUploadController, 它接受来自客户端的 Multipart 文件请求,并且响应文件的相关信息。 package cn.springdoc.demo.controller; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.MediaType; import org.springframework.util.StreamUtils; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import jakarta.servlet.http.HttpServletResponse; @RestController @RequestMapping("/upload") public class FileUploadController { private static final Logger log = LoggerFactory.getLogger(FileUploadController.class); /** * 文件上传 * @param file * @param response * @return * @throws IOException */ @PostMapping(consumes = MediaType.

Spring Modulith 简介

1、简介 模块化单体(Modular Monolith)是一种架构风格,在这种风格中,我们的源代码按照模块的概念进行结构化。对于许多组织来说,模块化单体是一个很好的选择。它有助于保持一定程度的独立性,这有助于我们在需要时过渡到微服务架构。 Spring Modulith 是 Spring 的一个实验项目,可用于模块化单体应用程序。此外,它还支持开发人员构建结构合理、领域一致的 Spring Boot 应用程序。 在本教程中,我们将讨论 Spring Modulith 项目的基础知识,并举例说明如何实际使用它。 2、模块化单体架构 我们有不同的选择来构建应用程序的代码。传统上,我们围绕基础设施设计软件解决方案。但是,当我们围绕业务设计应用程序时,就能更好地理解和维护系统。模块化单体架构就是这样一种设计。 模块化单体架构因其简单性和可维护性而越来越受到架构师和开发人员的青睐。如果我们将领域驱动设计(DDD)应用于现有的单体应用程序,就可以将其重构为模块化单体架构: 我们可以通过确定应用程序的领域和定义有界上下文,将单体的核心拆分成模块。 让我们来看看如何在 Spring Boot 框架内实现模块化单体应用程序。Spring Modulith 包含一系列库,可帮助开发人员构建模块化 Spring Boot 应用程序。 3、Spring Modulith 基础 Spring Modulith 可帮助开发人员使用由领域(domain)驱动的模块,并支持对这种模块化进行验证和文档化。 3.1、Maven 依赖 首先,让我们在 pom.xml 的 <dependencyManagement> 部分导入 spring-modulith-bom 依赖: <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.experimental</groupId> <artifactId>spring-modulith-bom</artifactId> <version>0.5.1</version> <scope>import</scope> <type>pom</type> </dependency> </dependencies> </dependencyManagement> 此外,我们还需要一些 Spring Modulith 核心依赖: <dependency> <groupId>org.springframework.experimental</groupId> <artifactId>spring-modulith-api</artifactId> </dependency> <dependency> <groupId>org.springframework.experimental</groupId> <artifactId>spring-modulith-starter-test</artifactId> <scope>test</scope> </dependency> 3.2、模块 Spring Modulith 的主要概念是模块。模块是将 API 暴露给其他模块的功能单元。此外,它还有一些不允许其他模块访问的内部实现。当我们设计应用程序时,我们会为每个域(domain)考虑一个模块。

使用 Spring Boot Cli 编码密码

1、概览 Spring Boot CLI 是一个 Spring Boot 工具,用于从命令运行和测试 Spring Boot 应用。该工具提供了一个非常有用的密码编码功能,主要目的是避免暴露明文形式的密码。 在本教程中,我们将深入 Spring Security 的世界,学习如何使用 Spring Boot CLI 对密码进行编码。 2、密码编码 密码编码是一种将密码以二进制格式表示并保存在存储介质上的简单方法。我们可以使用 Spring Security 对密码进行编码,也可以通过 Spring Boot CLI 进行编码。 2.1、Spring Security PasswordEncoder Spring Security 提供了 PasswordEncoder 接口,并且预置了很多实现,如 StandardPasswordEncoder 和 BCryptPasswordEncoder。 此外,Spring Security 建议使用基于强大算法和随机生成盐值的 BCryptPasswordEncoder。在框架的早期版本中,可以使用 MD5PasswordEncoder 或 SHAPasswordEncoder 类,但由于其算法的弱点,它们现在已被弃用,这两个类强制开发者将盐作为构造函数参数传递。而 BCryptPasswordEncoder 将在内部生成随机盐,BCryptPasswordEncoder 生成的字符串大小为 60 个字符。 而 StandardPasswordEncoder 类则基于 SHA-256 算法。 显然,在第三方系统中创建的用户密码必须按照 Spring Security 中选择的编码类型进行编码,这样才能成功进行身份认证。 2.2、Spring Boot CLI Password Encoder Spring Boot CLI 提供了许多命令,其中之一就是 encodepassword。该命令允许对密码进行编码,以便与 Spring Security 配合使用。简单地说,Spring Boot Cli 可以使用这种简单的 encodepassword 命令直接将原始密码转换为加密密码:

Spring 中的 “自注入”

1、概览 自注入是指 Spring Bean 将自身作为依赖注入。它使用 Spring 容器获取自身的引用,然后使用该引用执行某些操作。 在这个简短的教程中,我们将了解如何在 Spring 中使用自注入。 2、自注入的场景 自注入最常见的用例是在需要将切面应用于自引用的方法或类时,绕过 Spring AOP 的限制 假设我们有一个执行某些业务逻辑的 service 类,需要调用自身的一个方法作为该逻辑的一部分: @Service public class MyService { public void doSomething() { // ... doSomethingElse(); } @Transactional public void doSomethingElse() { // ... } } 不过,当我们运行应用程序时,可能会发现 @Transactional 并未生效。这是因为 doSomething() 方法直接调用了 doSomethingElse(),从而绕过了 Spring 代理。 为了解决这个问题,我们可以使用自注入获取 Spring 代理的引用,然后通过该代理调用方法。 3、使用 @Autowired 自注入 在 Spring 中,我们可以通过在 Bean 的字段、构造器参数或 setter 方法上使用 @Autowired 注解来进行自注入。 下面是一个在字段上使用 @Autowired 的示例: @Component public class MyBean { @Autowired private MyBean self; public void doSomething() { // 在这里使用 self 引用 } } 通过构造器参数自注入。

在 Spring Boot 中使用 H2 异常:JdbcSQLSyntaxErrorException

1、概览 在 Spring Boot 中使用 h2 作为内存数据库的时候遇到异常:org.h2.jdbc.JdbcSQLSyntaxErrorException: Syntax error in SQL statement expected “identifier”。 本文将会介绍出现 org.h2.jdbc.JdbcSQLSyntaxErrorException 的原因以及解决方案。 2、原因 通常,H2 会抛出 JdbcSQLSyntaxErrorException 来提示 SQL 语句中的语法错误。其中,“expected identifier” 信息表明 SQL 期望一个合适的 标识符,而我们没有给出。 导致这种异常的最常见原因是使用保留关键字作为标识符。 例如,使用关键字 table 来命名特定的 SQL 表就会导致 JdbcSQLSyntaxErrorException。 另一个原因是在 SQL 语句中缺少或错用了关键字。 3、重现异常 作为开发者,我们经常使用 user 一词来表示用户表。不幸的是,它在 H2 中是一个 保留关键字。 为了重现异常,我们将故意使用关键字 user。 因此,首先让我们添加一个基本的 SQL 脚本来初始化 H2 数据库并为其添加数据: INSERT INTO user VALUES (1, 'admin', 'p@ssw@rd'); INSERT INTO user VALUES (2, 'user', 'userpasswd'); 接下来,我们将创建一个实体(Entity)类来映射 user 表:

使用 Spring Security 通过 Header 认证服务之间的调用

1、概览 身份认证是微服务安全的基础。我们可以通过各种方式实现身份认证,如使用基于用户的凭证、证书或 token。 在本教程中,我们将学习如何使用 Spring Security 实现服务间的通信认证。 2、自定义身份认证简介 在某些情况下,使用 Oauth2 或存储在数据库中的密码可能并不可行,因为私有微服务不需要基于用户的交互。然而,我们仍然应该保护应用程序免受任何无效请求的影响。 在这种情况下,我们可以设计一种简单的身份认证技术,使用自定义 header。应用程序将根据预先配置的请求头认证请求。 我们还应在应用程序中启用 TLS,以确保 header 在网络传输中的安全。 我们可能还需要确保一些端点不需要进行任何身份认证,例如 health 或 error 端点。 3、示例应用 假如,我们需要用几个 REST API 构建一个微服务。 3.1、Maven 依赖 首先,我们将创建一个 Spring Boot Web 项目,添加 spring-boot-starter-web 和 spring-boot-starter-test 依赖。 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> 3.2、实现 REST Controller 我们的应用有两个端点,一个端点可通过 secret header 访问,另一个端点可被网络中的所有用户访问。 首先,在 APIController 中实现 /hello 端点: @GetMapping(path = "/api/hello") public String hello(){ return "hello"; } 然后,我们将在 HealthCheckController 类中实现 /health 端点: