1、简介 在处理分布式系统时,调用外部服务并保持低延迟是一项至关重要的任务。
本文将带你了解如何使用 OpenFeign 和 CompletableFuture 来并行处理多个 HTTP 请求,处理错误,并设置网络和线程超时。
2、示例项目 为了说明并行请求的用法,我们要实现一个功能,允许客户在网站上购买物品。首先,该服务发出一个请求,根据客户所在国家获取可用的付款方式。其次,它发送一个请求给客户生成有关购买的报告。购买报告不包括有关付款方式的信息。
先添加 spring-cloud-starter-openfeign 依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> 3、创建调用外部依赖的客户端 使用 @FeignClient 注解创建两个指向 localhost:8083 的客户端:
@FeignClient(name = "paymentMethodClient", url = "http://localhost:8083") public interface PaymentMethodClient { @RequestMapping(method = RequestMethod.GET, value = "/payment_methods") String getAvailablePaymentMethods(@RequestParam(name = "site_id") String siteId); } 第一个客户端名为 paymentMethodClient。它调用 GET /payment_methods,使用代表客户所在国家/地区的 site_id 请求参数获取可用的付款方式。
第二个客户端如下:
@FeignClient(name = "reportClient", url = "http://localhost:8083") public interface ReportClient { @RequestMapping(method = RequestMethod.POST, value = "/reports") void sendReport(@RequestBody String reportRequest); } 我们将其命名为 reportClient,它调用 POST /reports 生成购买报告。
1、概览 通常,我们使用遍历从 JDBC ResultSet 中迭代检索到的数据,不过在某些情况下,我更喜欢用 record Stream。
本文将带你了解使用 Stream API 处理 JDBC 结果集的几种方法。
2、使用 Spliterators 首先是纯 JDK 方法,使用 Spliterators 创建流。
首先,为实体定义一个 Model:
public record CityRecord(String city, String country) { } 在 CityRecord 中,我们存储了有关 city(城市)及其 country(国家)的信息。
接下来,创建一个能与数据库交互并返回 Stream<CityRecord> 的 Repository:
public class JDBCStreamAPIRepository { private static final String QUERY = "SELECT name, country FROM cities"; private final Logger logger = LoggerFactory.getLogger(JDBCStreamAPIRepository.class); public Stream<CityRecord> getCitiesStreamUsingSpliterator(Connection connection) throws SQLException { PreparedStatement preparedStatement = connection.
1、概览 在现代 Web 应用中,用户身份认证和授权是至关重要的组成部分。从零开始构建身份认证层是一项具有挑战性的复杂任务。不过,随着基于云的身份认证服务的兴起,这一过程变得简单多了。
Firebase Authentication 就是这样一个例子,它是 Firebase 和谷歌 提供的一种完全托管的身份认证服务。
本文将带你了解如何将 Firebase Authentication 与 Spring Security 整合,以创建和认证我们的用户。我们要进行必要的配置,实现用户注册和登录功能,并创建一个自定义 Authentication Filter 来验证私有 API 端点的用户 Token。
2、项目设置 在实现之前,需要加入 SDK 依赖并正确配置应用。
2.1、依赖 首先,在项目的 pom.xml 文件中添加 Firebase admin 依赖:
<dependency> <groupId>com.google.firebase</groupId> <artifactId>firebase-admin</artifactId> <version>9.3.0</version> </dependency> 该依赖提供了必要的类,用于在应用中与 Firebase Authentication 服务交互。
2.2、定义 Firebase 配置 Bean 现在,为了与 Firebase Authentication 交互,我们需要配置私钥(Private Key)来验证 API 请求。
在本例中,我们在 src/main/resources 目录下创建 private-key.json 文件。不过,在生产中,私钥应从环境变量中加载,或从 secret 管理系统中获取,以提高安全性。
使用 @Value 注解加载私钥,并用它来定义 Bean:
@Value("classpath:/private-key.json") private Resource privateKey; @Bean public FirebaseApp firebaseApp() { InputStream credentials = new ByteArrayInputStream(privateKey.
1、简介 本文将会带你了解如何使用 Java JMS(Java Message Service)从 IBM MQ 队列读写消息。
2、设置环境 我们可以在 Docker 容器中运行 IBM MQ,以避免手动安装和配置的复杂性。
使用以下命令以基本配置运行容器:
docker run -d --name my-mq -e LICENSE=accept -e MQ_QMGR_NAME=QM1 MQ_QUEUE_NAME=QUEUE1 -p 1414:1414 -p 9443:9443 ibmcom/mq 接下来,需要在 pom.xml 文件中添加 IBM MQ 客户端:
<dependency> <groupId>com.ibm.mq</groupId> <artifactId>com.ibm.mq.allclient</artifactId> <version>9.4.0.0</version> </dependency> 3、配置 JMS Connection 首先,我们需要用 QueueConnectionFactory 建立 JMS Connection(连接),用于创建与队列管理器(Queue Manager)的连接:
public class JMSSetup { public QueueConnectionFactory createConnectionFactory() throws JMSException { MQQueueConnectionFactory factory = new MQQueueConnectionFactory(); factory.setHostName("localhost"); factory.setPort(1414); factory.setQueueManager("QM1"); factory.setChannel("SYSTEM.DEF.SVRCONN"); return factory; } } 首先创建一个 MQQueueConnectionFactory 实例,用于配置和创建与 IBM MQ 服务器的连接。我们将主机名设置为 localhost,因为 MQ 服务器是在本地 Docker 容器内运行的。暴露的映射端口为 1414。
1、概览 在使用 Hibernate 时,经常会遇到这样的情况:在将实体持久化到数据库之前,需要更改字段的值。这种情况可能是因为需要执行必要的字段转换。
本文将通过一个示例:即在执行更新和插入操作前将字段值转换为大写字母,来了解实现这一目的的不同方法。
2、实体生命周期回调 首先,定义一个简单的实体类 Student:
@Entity @Table(name = "student") public class Student { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @Column private String name; // Getter / Setter 方法省略 } 第一种方法是 JPA 实体生命周期回调。JPA 提供了一组注解,允许我们在不同的 JPA 生命周期事件中执行一个方法,例如:
@PrePresist:在插入事件之前执行。 @PreUpdate:在更新事件之前执行。 我们在 Student 实体类中添加 changeNameToUpperCase() 方法。该方法将 name 字段改为大写。该方法通过 @PrePersist 和 @PreUpdate 进行注解,以便 JPA 在持久化和更新之前调用该方法:
@Entity @Table(name = "student") public class Student { @PrePersist @PreUpdate private void changeNameToUpperCase() { name = StringUtils.
1、概览 本文将带你了解如何在 Spring Boot 应用中使用 HttpStatusCode,重点是 3.3.3 版中引入的最新增强功能。通过这些增强功能,HttpStatusCode 已被纳入 HttpStatus 实现,从而简化了我们处理 HTTP 状态码的方式。
这些改进的主要目的是提供一种更灵活、更可靠的方法来处理标准和自定义 HTTP 状态码,使我们在处理 HTTP 响应时具有更高的灵活性和可扩展性,同时保持向后兼容性。
2、HttpStatus 枚举 在 Spring 3.3.3 之前,HTTP 状态码在 HttpStatus 中表示为枚举。这限制了自定义或非标准 HTTP 状态码的使用,因为枚举是一组固定的预定义值。
尽管 HttpStatus 类尚未被弃用,但一些返回原始 Integer 状态码的枚举和方法(如 getRawStatusCode() 和 rawStatusCode())现已被弃用。
使用 @ResponseStatus 注解来提高代码的可读性仍然是我们推荐的方法。
我们可以将 HttpStatus 与 HttpStatusCode 结合使用,以实现更灵活的 HTTP 响应管理:
@GetMapping("/exception") public ResponseEntity<String> resourceNotFound() { HttpStatus statusCode = HttpStatus.NOT_FOUND; if (statusCode.is4xxClientError()) { return new ResponseEntity<>("Resource not found", HttpStatusCode.valueOf(404)); } return new ResponseEntity<>("Resource found", HttpStatusCode.
1、概览 对应用程序进行分析可以深入了解其运行时的行为。Java 生态系统中有多种流行的分析器(Profiler),如用于通用分析的 NetBeans Profiler、JProfiler 和 VisualVM。
本文将带你了解如何以编程方式使用 NetBeans profiler API。
2、NetBeans Profiler NetBeans IDE 提供免费的分析器来分析 Java 应用。它通过 IDE 中直观的嵌入式用户界面,提供了评估 CPU 性能和内存使用情况的功能。
然而,NetBeans Profiler 还提供了可用于编程式的分析 API。这可用于 Heap Dump 的自动化分析,而不需要依赖于 UI 界面。
Heap Dump(堆转储)是一段时间内应用的内存快照。它是深入了解内存使用情况的良好指标,因为它包括内存中的实时对象、对象的类和字段以及对象之间的引用。
3、示例项目 要使用 NetBeans Profiler API,首先在 pom.xml 中添加 依赖:
<dependency> <groupId>org.netbeans.modules</groupId> <artifactId>org-netbeans-lib-profiler</artifactId> <version>RELEASE220</version> </dependency> 该依赖提供了 JavaClasses 和 Instances 等各种工具类,以帮助我们分析类、创建的实例数量和使用的内存。
接着,创建一个简单的项目并分析它的 Heap Dump:
class SolarSystem { private static final Logger LOGGER = Logger.getLogger(SolarSystem.class.getName()); private int id; private String name; private List<String> planet = new ArrayList<>(); // 构造函数 public void logSolarSystem() { LOGGER.
1、概览 本文将带你了解如何使用 Spring JDBC 框架的 JdbcTemplate 来调用存储过程。数据库存储过程类似于函数。函数支持输入参数并有返回类型,而存储过程同时支持输入和输出参数。
2、先决条件 来看看 PostgreSQL 数据库中一个简单的存储过程:
CREATE OR REPLACE PROCEDURE sum_two_numbers( IN num1 INTEGER, IN num2 INTEGER, OUT result INTEGER ) LANGUAGE plpgsql AS ' BEGIN sum_result := num1 + num2; END; '; 存储过程 sum_twoo_numbers 接收两个输入数字,并在输出参数 sum_result 中返回它们的和。一般来说,存储过程可以支持多个输入和输出参数。但在本例中,我们只考虑了一个输出参数。
3、使用 JdbcTemplate#call() 方法 来看看如何使用 JdbcTemplate#call() 方法调用数据库存储过程:
void givenStoredProc_whenCallableStatement_thenExecProcUsingJdbcTemplateCallMethod() { List<SqlParameter> procedureParams = List.of(new SqlParameter("num1", Types.INTEGER), new SqlParameter("num2", Types.NUMERIC), new SqlOutParameter("result", Types.NUMERIC) ); Map<String, Object> resultMap = jdbcTemplate.call(new CallableStatementCreator() { @Override public CallableStatement createCallableStatement(Connection con) throws SQLException { CallableStatement callableStatement = con.
1、简介 在 JPA 中,CAST 和 TREAT 是两个不同的关键字,用于操作数据类型和实体关系。本文将带你了解 CAST 和 TREAT 的区别,并通过示例来说明它们的用法。
2、JPA 中的 CAST JPA 中的 CAST 操作符主要用于 JPQL 查询中的类型转换。它允许我们显式地将一个值从一种数据类型转换为另一种数据类型。例如,可以使用 CAST 将字符串转换为整数,反之亦然。
CAST 的语法如下:
CAST(expression AS type) expression 是我们要转换的值或字段,type 是我们要将表达式转换为的目标数据类型。
3、JPA 中的 TREAT 相比之下,TREAT 操作符是为在 JPQL 查询中对实体进行类型安全的向下转换而设计的。它在处理继承层次结构时特别有用。使用 TREAT 时,我们指定实体的子类型,然后 JPA 会检查实际实体是否确实属于该类型。
与 CAST 不同,TREAT 不会改变值的底层数据类型。相反,它允许我们像访问目标类型的值一样访问该值。
TREAT 的语法如下:
TREAT(expression AS type) expression 是要处理的值,type 是目标数据类型。
4、适用场景和用法 在 JPA 查询中,CAST 和 TREAT 都用于处理类型转换,但它们的用途不同。
4.1、CAST 操作符 CAST 用于将一种数据类型转换为另一种数据类型,以便进行操作或比较。在执行查询时,如果需要的数据类型与数据库中存储的数据类型不同,通常会使用 CAST。
示例如下:一个名为 Employee 的实体,其 salary 字段在数据库中存储为字符串。下面是 Employee 实体的定义:
1、概览 本文将带你了解 Spring Reactive 中的 switchIfEmpty() 操作符及其在使用和不使用 defer() 操作符时的行为,了解这些操作符在不同场景中的交互方式,并通过实际示例来说明它们对响应式流(Reactive Stream)的影响。
2、switchIfEmpty() 和 Defer() 的使用 switchIfEmpty() 是 Mono 和 Flux 中的一个操作符,用于在源生产者为空时执行备用生产者流。如果主源 Publisher 没有发布数据,该操作符就会切换到替代源的数据发布。
考虑一个从大型文件中通过 ID 检索用户详细信息的场景。当请求文件中的用户详细信息时,遍历文件会消耗大量时间。因此,对于经常访问的 ID,将其详细信息缓存起来更有意义。
当端点收到请求时,首先搜索缓存。如果用户详细信息可用,返回响应。如果没有,则从文件中获取数据并缓存起来,以备后续请求。
在这种情况下,主数据提供者(Primary Data Provider)是检查缓存中 KEY 是否存在的流,而备用数据提供者是检查文件中的 KEY 并更新缓存的流。switchIfEmpty() 操作符可以根据缓存中数据的可用性高效地切换数据源提供者。
了解 defer() 操作符的使用也很重要,它可以推迟函数的求值,直到发生订阅。如果我们不在 switchIfEmpty() 中使用 defer() 操作符,表达式就会立即(急切地)求值,从而可能导致意想不到的副作用。
3、示例设置 通过示例来了解 switchIfEmpty() 操作符在不同情况下的行为。
3.1、Data Model 首先,定义一个 User 模型类,其中包含一些详细信息,如 id、email、username 和 roles:
public class User { @JsonProperty("id") private String id; @JsonProperty("email") private String email; @JsonProperty("username") private String username; @JsonProperty("roles") private String roles; // Getter / Setter 省略 } 3.