Java 22 是一个重大改进,我认为对于每个人来说都值得升级。其中包含了一些重要的最终发布功能,比如 Project Panama,以及许多更好的预览功能。我无法一一介绍,但我想简要介绍一些我最喜欢的功能。如果你也想尝试一下的话,代码在 这里(https://github.com/spring-tips/java22)。
我喜欢 Java 22,当然也喜欢 GraalVM,这两个版本今天都发布了!Java 当然是我们最喜欢的运行时和语言,而 GraalVM 则是一个高性能的 JDK 发行版,它支持其他语言并允许超前编译(AOT)(它们被称为 GraalVM 原生镜像)。GraalVM 包含新 Java 22 版本的所有功能,还有一些额外的实用工具,所以我一直建议下载此版本。我特别感兴趣的是 GraalVM 原生镜像功能。生成的二进制文件几乎可以立即启动,与 JRE 相比占用的内存也少得多。GraalVM 并不新鲜,但值得注意的是,Spring Boot 拥有一个强大的引擎,支持将 Spring Boot 应用转化为 GraalVM 原生镜像。
简介 我使用的是的 Java SDKMAN 包管理器。在运行 macOS 的苹果 Silicon 芯片上运行。
从 官网 下载安装 GraalVM Community Edition 的预发布版,GraalVM Community 是开源版本。(GraalVM 也有免费的商业版本,但不是开源的)。它允许你通过配置文件引导优化(PGO)等技术构建更快的本地镜像。
解压它,并使用 SDKMAN 命令行工具手动安装,如下所示:
sdk install java 22.07-graalce $HOME/bin/graalvm-jdk-22+36.1/Contents/Home` 然后输入:sdk default java 22.07-graalce,打开了一个新的 shell。输入 javac --version 和 java --version 以及 native-image --version 来验证一切正常。
我使用 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 开发。
1、概览 Blowfish 加密算法最初是作为 DES 加密算法的替代方案而设计的,是当今最流行的加密算法之一。Blowfish 是一种对称的分组加密算法,由 Bruce Schneier 于 1993 年设计 。该算法的块大小为 64 位,密钥长度为 446 位,优于 DES 和 3DES 算法。
本文将带你了解如何使用 Java Cryptography Architecture(JCA)中提供的 Blowfish 算法实现加密和解密。
2、生成密钥 由于 Blowfish 是一种对称加密算法,它在加密和解密过程中使用相同的密钥。所以,接下来我们要创建一个密钥来加密文本。这个密钥应该被安全地保存,不应该在公共场合分享。
定义密钥:
// 定义密钥 String secretKey = "MyKey123"; byte[] keyData = secretKey.getBytes(); //使用 Blowfish 算法创建 SecretKeySpec SecretKeySpec secretKeySpec = new SecretKeySpec(keyData, "Blowfish"); 接下来,就可以通过加密模式构建 Cipher 了:
// 使用 Blowfish 算法创建 Cipher Cipher cipher = Cipher.getInstance("Blowfish"); 然后,使用加密模式(Cipher.ENCRYPT_MODE)初始化 cipher,并指定 Secret Key:
// 使用 Secret Key 在加密模式下初始化 cipher cipher.
1、概览 后端 HTTP API 开发最重要的功能之一是解析前端传递的请求查询参数。
本文将带你了解几种直接从 HttpServletRequest 获取查询参数的方法,以及 Spring MVC 提供的一些简洁方法。
2、HttpServletRequest 中的方法 首先,来看看 HttpServletRequest 提供的与参数相关的方法。
2.1、HttpServletRequest#getQueryString() HttpServletRequest#getQueryString() 可以直接从 URL 获取查询字符串信息:
@GetMapping("/api/byGetQueryString") public String byGetQueryString(HttpServletRequest request) { return request.getQueryString(); } 使用 curl 向该 API 发送一个包含多个参数的 GET 请求时,getQueryString() 方法会返回 ? 后面的所有字符:
$ curl 'http://127.0.0.1:8080/spring-mvc-basics/api/byGetQueryString?username=bob&roles=admin&roles=stuff' username=bob&roles=admin&roles=stuff 如果将 @GetMapping 更改为 @RequestMapping,当使用 POST/PUT/PATCH/DELETE HTTP 方法发送请求时,返回的响应相同。也就是说 HttpServletRequest#getQueryString 始终获取到的是 URL 中的查询参数,无论 HTTP 方法是什么。因此,本教程只关注GET请求。
2.2、HttpServletRequest#getParameter(String) 为了简化参数的解析,HttpServletRequest 提供了一个 getParameter 方法,可以通过参数名获取参数值:
@GetMapping("/api/byGetParameter") public String byGetParameter(HttpServletRequest request) { String username = request.
十六进制(Hexadecimal)是一种数制系统,它使用 16 个数字来表示数值,分别是 0 到 9 和 A 到 F。
十六进制经常用于表示字节数据。在十六进制表示中,一个字节可以用两个十六进制数字表示。例如,字节的取值范围是 0 到 255,可以用 00 到 FF 来表示。其中,00 表示二进制的 00000000,FF 表示二进制的 11111111。这在 Socket 通信协议的定义中很常见。
简单来说,对于一些较短的二进制数据,可以把它序列化为十六进制字符串,其中每 2 个字符,表示一个字节。同样,也可以把十六进制的字符串解析为字节数组。最常见的场景就是把 Hash 计算的结果表示为十六进制字符串。
通常我们可以选择使用第三方的 commons-codec 库来实现格式化和解析十六进制字符串。可能是这个功能需求太常见,于是从JDK 17 开始,标准库中提供了一个 HexFormat 工具类,用于格式化和解析十六进制字符串。
简单地编码和解码 简单地把字节数组编码为十六进制字符串,以及把十六进制字符串解析为字节数组。
package cn.springdoc.demo.test; import java.util.HexFormat; public class Main { public static void main(String[] args) throws Exception { HexFormat format = HexFormat.of(); String hex = format.formatHex("hello springdoc.cn".getBytes()); System.out.println("Hex=" + hex); byte[] bytes = format.parseHex(hex); System.
1、概览 本文将带你了解 Java 抛出 UndeclaredThrowableException 异常的原因。
2、UndeclaredThrowableException 从理论上讲,当我们尝试抛出一个未声明的受检异常时,Java 会抛出一个 UndeclaredThrowableException 异常。也就是说,我们没有在 throws 子句中声明受检异常,但却在方法体中抛出了该异常。
受检异常 - 指的是必须要调用者用 try/catch 语句处理或者是再次 throws 出去的异常(即非 RuntimeException 子类)。
有人可能会说这是不可能的,因为 Java 编译器会通过编译错误来防止这种情况发生。
例如,如果我们尝试编译:
public void undeclared() { throw new IOException(); } Java 编译器提示的失败信息如下:
java: unreported exception java.io.IOException; must be caught or declared to be thrown 尽管在编译时可能不会抛出未声明的受检异常,但在运行时仍有可能发生。
例如,一个运行时代理拦截一个不抛出任何异常的方法:
public void save(Object data) { // 省略 } 如果代理本身抛出了受检异常,从调用者的角度来看,save 方法也会抛出受检异常。调用者可能对该代理一无所知,因此会将该异常归咎于 save 方法。
在这种情况下,Java 会将实际已检查异常封装在 UndeclaredThrowableException 中,然后抛出 UndeclaredThrowableException。而 UndeclaredThrowableException 本身就是一个 非受检异常(RuntimeException)。
在 上一文 一文中,介绍了如何在 Java 中使用 RSA 非对称加密算法 进行加密、解密、生成数字签名和验签。
和 “非对称加密算法” 对应的就是 “对称加密算法”。非对称加密算法(如 RSA)的密钥通常由 公钥 和 私钥 组成,且遵守公钥加密、私钥解密的模式。而对称加密算法则只有一个密钥,加密和解密都使用同一个密钥。
对称加密算法中,比较安全且流行的就是 AES 算法,本文将会带你了解如何在 Java 中使用 AES 对数据进行加密和解密。
AES 介绍 AES(Advanced Encryption Standard)是一种对称加密算法,也被称为高级加密标准。它是一种广泛使用的加密算法,具有高度的安全性和效率,已被广泛应用于各种领域,包括网络通信、数据存储和加密协议等。AES 使用相同的密钥进行加密和解密操作,因此被归类为对称加密算法。
密钥 可以使用 Java 中的 javax.crypto.KeyGenerator API 来生成随机的 AES 密钥。
package cn.springdoc.demo.test; import java.security.SecureRandom; import java.util.Base64; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; public class Main { public static void main(String[] args) throws Exception { // 获取 AES 密钥生成器 KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); // 设置密钥长度和随机源 keyGenerator.
RSA(Rivest-Shamir-Adleman)算法是一种非对称加密算法,广泛用于数据加密和数字签名领域。它是由 Ron Rivest、Adi Shamir 和 Leonard Adleman 于 1977 年共同提出的。
RSA 算法常用于如下场景:
公钥加密,私钥解密 私钥加密,公钥解密(不推荐) 私钥签名,公钥验签 生成密钥对 通过 Java java.security 包下的工具类可以生成 RSA 公钥和私钥。
package cn.springdoc.demo.test; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.util.Base64; /** * @author springdoc.cn * 生成 RSA 密钥对 */ public class Main { public static void main(String[] args) throws Exception { // 初始化 Key 生成器,指定算法类型为 RSA KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); // 密钥长度为 2048 位 keyPairGenerator.initialize(2048); // 生成密钥对 KeyPair keyPair = keyPairGenerator.
1、概览 本文将带你了解几种模拟 HttpServletRequest 对象的方法。
首先,从 Spring Test 中的 MockHttpServletRequest 开始,这是一个功能齐全的模拟类型。然后,了解如何使用 Mockito 和 JMockit 这两个流行的模拟库进行测试。最后,了解如何使用匿名子类进行测试。
2、测试 HttpServletRequest 当我们要模拟客户端 request 信息(如 HttpServletRequest)来测试 Servlet 可能会有点麻烦,该接口定义了各种方法,需要进行实现。
要测试的目标 UserServlet 类,如下:
public class UserServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { String firstName = request.getParameter("firstName"); String lastName = request.getParameter("lastName"); response.getWriter().append("Full Name: " + firstName + " " + lastName); } } 要对 doGet() 方法进行单元测试,需要模拟 request 和 response 参数,以模拟实际的运行时行为。
3、使用 Spring 的 MockHttpServletRequest Spring-Test 提供了一个功能齐全的类 MockHttpServletRequest,它实现了 HttpServletRequest 接口。
1、概览 空指针异常 NullPointerException 是一个常见问题,避免这种问题的方法之一是在方法参数上添加 @NotNull 等校验注解。
给方法参数添加了 @NotNull 注解后,还需要其他的一些设置才能自动对参数进行非空校验。
2、给方法参数添加 @NotNull 注解 创建一个类,其中包含一个返回 String 长度的方法。
在 String 参数上添加 @NotNull 注解:
public class NotNullMethodParameter { public int validateNotNull(@NotNull String data) { return data.length(); } } 注意,有多个包下都有 @NotNull 注解,我们使用的应该是 jakarta.validation.constraints 包。
创建 NotNullMethodParameter 实例,然后使用 null 参数调用方法。
NotNullMethodParameter notNullMethodParameter = new NotNullMethodParameter(); notNullMethodParameter.doesNotValidate(null); 尽管在参数上使用了 @NotNull,但还是出现了空指针异常:NullPointerException。
注解未生效,因为没有 Validator 来执行它。
3、添加 Validator 添加 Hibernate Validator(jakarta.validation 的实现)来识别 @NotNull。
<dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>8.0.0.Final</version> </dependency> 使用默认的 ValidatorFactory 创建 validator。