1、概览 Java 虚拟机(JVM)是驱动 Java 应用的核心引擎,负责执行编译后的 .class 文件,并通过即时编译(JIT)和垃圾回收(GC)等技术管理内存并提升性能。
JVM 具有高度灵活性,通过特定参数可轻松调整其行为以提升性能、排查问题或启用实验性功能。本文将带你了解用于配置 JVM 的不同参数前缀。
2、JVM 参数是什么? JVM 参数是能改变虚拟机行为的特殊命令行选项,用于控制内存设置、性能调优、调试监控开关、垃圾回收配置及实验性功能。
启动 JVM 时可通过以下方式指定这些参数:
java -Xmx512m -Denv=prod -verbose:gc -XX:+UseG1GC -jar App.jar 上述命令中使用了多种参数前缀,每个前缀标识了不同的配置类型。
-Xmx512m - 设置最大堆内存为 512 MB(非标准参数) -Denv=prod - 定义名为 env、值为 prod 的系统属性(System Property) -verbose:gc - 启用垃圾回收日志记录(标准参数) -XX:+UseG1GC - 指定 JVM 使用 G1 垃圾回收器(高级参数) 下面详细解析各类参数前缀的作用。
3、不同的 JVM 参数前缀 3.1、System Properties (-D) 系统属性(System Properties)通常用于配置 JVM 特定参数,如文件编码、用户目录、JVM 版本等 Java 相关配置。
通过 -D 命令行参数可定义键值对形式的系统属性,例如:
java -Denv=prod -jar App.jar 在 -D 参数前缀中,字母 D 代表 Define(定义)。Java 为避免歧义未使用其他字母,该前缀简短、直观且易记忆,明确表示正在定义属性。
1、简介 跨站脚本攻击(XSS)是一种安全漏洞,允许攻击者向网页应用中注入恶意脚本。这些脚本能在用户浏览器中执行,导致数据窃取、会话劫持或页面篡改等风险。
本文将带你了解如何在 Java 应用中过滤 HTML 输入以防止 XSS 攻击。
2、项目设置 首先,需要在 pom.xml 中添加 OWASP Java HTML sanitizer 库:
<dependency> <groupId>com.googlecode.owasp-java-html-sanitizer</groupId> <artifactId>owasp-java-html-sanitizer</artifactId> <version>20240325.1</version> </dependency> 该库提供高度可配置的策略驱动式 Sanitizer(净化器),既能处理复杂 HTML 内容,又能有效防御 XSS 攻击。
3、实现基础版 OWASP HTML 过滤 添加依赖后,我们定义一个工具方法,利用该库清理可能非法的 HTML 输入。
以下创建了一个可复用的工具类,采用默认策略(仅允许基础格式化标签)实现 HTML 过滤:
public class HtmlSanitizerUtil { private static final PolicyFactory POLICY = Sanitizers.FORMATTING.and(Sanitizers.LINKS); public static String sanitize(String htmlContent) { return POLICY.sanitize(htmlContent); } } 上例中,我们通过组合两个内置 Sanitizer(Sanitizers.FORMATTING 和 Sanitizers.LINKS)配置过滤策略。该策略允许基础 HTML 格式化标签(如 <b>、<i>、<u>)以及通过 <a> 标签实现的超链接。随后 sanitize() 方法将此策略应用于输入字符串,返回过滤后的 HTML 内容。
1、概览 MapStruct 通过注解来定义 POJO 属性间的映射关系。其 Maven 插件会读取注解中定义的元数据,自动生成 Mapper 工具类。此外,它还支持通过自定义映射工具实现细粒度控制。
本文将带你了解如何用 MapStruct 将层次化的源实体嵌套属性映射到扁平化的目标实体
2、用例 类体系图如下。通过源实体 Order 和目标实体 OrderDto 来演示 MapStruct 库执行嵌套映射的能力:
源实体 Order 表示具有嵌套结构的复杂对象,包含 Customer 和 Product:
public class Order { private Customer customer; private Product product; // Getter/Setter 省略 } public class Customer { private String name; private Address address; // Getter/Setter 省略 } public class Product { private String name; private double price; // Getter/Setter 省略 } 此外,Customer 实体具有一个类型为 Address 的 address 属性:
1、概览 本文将带你了解如何处理 Hibernate 异常 “DuplicateMappingException: Column is duplicated in mapping for entity”。
2、理解 DuplicateMappingException 简而言之,DuplicateMappingException 是 MappingException 的子类,专门用于处理重复的对象关系映射错误。
当实体类中多次映射同一列时,会出现 “Column is duplicated in mapping for entity” 的提示信息。这种情况下,Hibernate 无法处理这种重复映射。
3、重现 DuplicateMappingException 了解了导致 Hibernate 抛出 DuplicateMappingException 的原因后,让我们通过实践来复现这个问题。
首先,定义一个 Person 实体类:
@Entity public class Person { @Id private int id; @Column(name = "first_name") private String firstName; @Column(name = "first_name") // 重复定义了 first_name 列 private String lastName; // 省略 Getter/Setter } 简而言之,一个 person 由 id、firstName 和 lastName 组成。@Entity 注解表明 Person 类是一个 JPA 实体,而 @Id 注解表示主键。此外,@Column 注解将每个实体字段映射到特定的表列。
1、概览 一个 Docker 容器会运行一个进程、应用程序,有时仅是一个脚本或命令,以执行其设计任务。
每个容器一旦内部没有任何进程或脚本运行,就会停止并退出。有些容器默认会持续运行,直到用户选择停止它们,例如 MySQL 数据库容器、Spring Boot Web 应用容器或 SMTP 邮件服务器容器。但有时,我们需要让容器在其主要任务完成后仍然保持运行,比如 Ubuntu 容器。
本文将带你了解如何使用 Docker Compose 实现这一需求。
2、Docker Compose 设置 Docker Compose 是我们用来定义和运行多容器服务的工具。唯一的前提条件是在受支持的操作系统平台上安装 Docker(包括 Docker Server、Docker Client 和 Docker Compose)。
本教程中我们使用 Linux Ubuntu。
3、运行 Ubuntu 容器 我们使用一个名为 docker-compose.yml 的示例 Docker Compose 配置文件来定义服务:
services: demo: image: ubuntu 3.1、启动 Docker Compose 服务 使用 -d 选项在后台运行服务的容器:
$ docker-compose up -d 输出如下,显示它创建了一个容器:
Creating ubuntu_demo_1 ... done 查看 Docker Compose 服务创建的容器是否正在运行。
使用 docker ps -a 命令列出容器:
1、概览 使用 Spring 的 JdbcTemplate 时,若需将查询结果转换为 POJO 列表,常会遇到 IncorrectResultSetColumnCountException 异常。
该异常通常因误用 queryForList() 方法引发,特别是尝试将其直接映射到自定义 POJO 类时。
本文将带你了解异常成因、queryForList() 的正确用法,以及如何将查询结果映射到自定义类。
2、理解异常 假设存在 STUDENT_TBL 数据表,其中包含四名学生数据:
CREATE TABLE STUDENT_TBL ( ID int NOT NULL PRIMARY KEY, NAME varchar(255), MAJOR varchar(255) ); INSERT INTO STUDENT_TBL VALUES (1, 'Kai', 'Computer Science'); INSERT INTO STUDENT_TBL VALUES (2, 'Eric', 'Computer Science'); INSERT INTO STUDENT_TBL VALUES (3, 'Kevin', 'Banking'); INSERT INTO STUDENT_TBL VALUES (4, 'Liam', 'Law'); 同时创建 Student POJO 类:
1、简介 本文将带你了解 PostgreSQL 中的 LISTEN 和 NOTIFY 命令,包括其功能、使用方法以及在应用中的实际应用。
2、LISTEN 和 NOTIFY 是什么? PostgreSQL 支持使用 LISTEN 和 NOTIFY 命令在服务器和连接的客户端之间进行异步通信。这些特定于 PostgreSQL 的扩展使我们能够将数据库用作一个简单的 MQ 系统,允许我们从数据库中生成客户端可以做出反应的事件。这在很多方面都很有用,如实时仪表盘、缓存失效、数据审计等。
2.1、监听通知 使用 LISTEN 命令注册接收事件的监听,需指定目标频道名称:
postgres=# LISTEN my_channel; LISTEN 完成注册后,该连接即可接收该频道的异步事件通知。
所有注册监听的连接都会收到通知,因此该系统实际采用广播机制而非单播。这意味着可通过此方式轻松向所有客户端同步数据库内发生的事件。
注意:若使用 psql 工具,不会自动接收通知。需重新执行 LISTEN 命令,此时会显示自上次监听后触发的所有通知:
postgres=# LISTEN my_channel; LISTEN ..... postgres=# LISTEN my_channel; LISTEN Asynchronous notification "my_channel" with payload "Hello, World!" received from server process with PID 66. 此处可见某连接触发了 Payload 为 “Hello, world!” 的事件,监听连接已收到通知。
虽然监听器数量无硬性上限,但每个监听器需保持数据库连接开启以接收通知,因此实际受限于最大连接数限制。此外,每个监听器都会占用资源,过多监听器可能导致性能问题。
2.2、发布通知 了解如何监听事件后,还需掌握如何触发事件。使用 NOTIFY 命令可触发事件,需指定频道名称和发送的消息:
1、概览 在使用 Spring AOP 时有许多错综复杂的问题。其中一个常见问题是如何处理同一类中的方法调用,因为这种直接调用会使 AOP 功能失效。
本文将带你了解 Spring AOP 的工作原理以及如何解决同类中 AOP 方法直接调用导致的 AOP 功能失效的问题。
2、代理 和 Spring AOP 代理对象可以看作是一个 “包装”,它可以在调用目标对象时添加功能。
在 Spring 中,当一个 Bean 需要额外功能时,就会创建一个代理,并将代理注入到其他 Bean 中。例如,如果某个方法具有事务(Transactional)注解或任何缓存注解,那么该代理就会被用来在调用目标对象时添加所需的功能。
3、AOP:内部方法调用和外部方法调用 如上所述,Spring 创建的代理添加了所需的 AOP 功能。
来看一个缓存示例,看看在内部和在外部调用方法有何不同:
@Component @CacheConfig(cacheNames = "addOne") public class AddComponent { private int counter = 0; @Cacheable public int addOne(int n) { counter++; return n + 1; } @CacheEvict public void resetCache() { counter = 0; } } 当 Spring 创建 AddComponent Bean 时,也会创建一个代理对象,以添加用于缓存的 AOP 功能。当另一个对象需要 AddComponent Bean 时,Spring 会提供代理对象用于注入。
1、概览 在基于 Maven 的 Java 项目中,经常需要将项目版本号输出到文本文件中。用于版本跟踪、日志记录以及确保不同构建和部署之间的一致性。
Maven 会在 pom.xml 文件中的 <version> 标签下维护项目版本。利用 Maven 的资源过滤(Resource Filtering)功能和附加插件,我们可以在构建过程中自动提取并存储该版本号。
本文将带你了解 Mave 实现版本号输出的两种方法:第一种是使用 Maven Resources Plugin,第二种是使用 Maven Antrun Plugin。
2、使用 Maven Resources Plugin Maven 资源插件通常用于文件过滤,允许用 pom.xml 中定义的值替换资源文件中的占位符。
该插件可以在构建过程中直接在资源文件中动态插入项目属性(version、artifactId、groupId)。
2.1、启用资源过滤 首先配置 pom.xml 文件中的 <resources> 部分,以启用项目中资源文件的过滤功能。
<build> <resources> <resource> <directory>src/main/resources</directory> <!-- 启用资源过滤 --> <filtering>true</filtering> </resource> </resources> </build> <filtering>true</filtering> 此配置会告诉 Maven 处理 src/main/resources 目录中的所有文件,并应用过滤功能,用 Maven 项目属性(如 ${project.version})中的相应值替换任何占位符。
2.2、创建版本文件 接下来,在 src/main/resources 目录中创建一个资源文件(例如 version.txt)。该文件可包含 ${project.version} 等占位符,它们将在构建过程中被动态替换为 pom.xml 中定义的实际版本号。
如下,创建一个 version.txt 文件,内容如下:
1、简介 Java Class-File(类文件) API 是在 JEP-484 中引入的,是 Java 24 的一部分。它旨在创建一个接口,允许在不依赖于 ASM 库的传统 JDK 内部复制实现的情况下进行类文件处理。
本文将带你了解如何从头开始构建类文件,以及如何使用类文件 API 将一个类文件转换为另一个类文件。
2、 Class-File 核心的 API Class-File 有三个核心元素:
元素 - 代表代码的一部分,如变量、指令、方法或类。此外,一个元素可能包含其他元素。例如,一个类元素可能包含方法元素,而方法元素又包括变量或指令元素。 构建器 -(如方法构建器和代码构建器)用于创建每种类型的元素。 转换函数 - 可用于使用构建器将元素转换为其他元素。 3、生成类文件 使用 MethodBuilder 和 CodeBuilder 类生成类文件。
3.1、示例方法 先来看一个简单的代码段,它根据员工的角色和基本工资计算员工的工资:
public double calculateAnnualBonus(double baseSalary, String role) { if (role.equals("sales")) { return baseSalary * 0.35; } if (role.equals("engineer")) { return baseSalary * 0.25; } return baseSalary * 0.15; } 3.2、使用 MethodBuilder 和 CodeBuilder 我们可以使用 MethodBuilder 和 CodeBuilder 类来生成与 calculateAnnualBonus() 功能相同的方法