Skip to content

Commit e905f6b

Browse files
authored
Merge pull request #3 from NerosoftDev/develop
Develop
2 parents 8226a42 + 33648c5 commit e905f6b

22 files changed

Lines changed: 812 additions & 5 deletions

README.md

Lines changed: 299 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,300 @@
11
# Mediator
2-
A mediator component for CQRS pattern application.
2+
3+
一个轻量级的 Java Mediator 组件,用于在 CQRS 场景下统一处理 `Command``Query``Event`,并支持中间件管道、消息验证和事件并行策略。
4+
5+
## 项目简介
6+
7+
`Mediator` 通过“消息 + 处理器”的模式解耦业务调用方与实现方:
8+
9+
- `Command`:执行动作(通常无返回值)
10+
- `Query<R>`:查询数据(有返回值)
11+
- `Event`:发布通知(可被多个处理器订阅)
12+
13+
默认实现为 `PipelinedMediator`,核心特性:
14+
15+
- 自动匹配消息处理器(`Handler<T, R>`
16+
- 支持中间件链(`Middleware`
17+
- 支持验证器(`Validator<T>`),失败时抛出 `ValidationException`
18+
- 支持事件并行分发策略(`HandlerParallelStrategy`
19+
- 支持事件异常处理策略(`HandlerExceptionStrategy`
20+
21+
## 依赖与环境
22+
23+
项目为 Maven 工程(见 `pom.xml`):
24+
25+
- `groupId`: `com.nerosoft`
26+
- `artifactId`: `Mediator`
27+
- `version`: `1.0.0`
28+
- 测试依赖:`org.junit.jupiter:junit-jupiter:6.0.3`
29+
- 编译版本:`maven.compiler.source/target = 25`
30+
31+
## 使用方法
32+
33+
### 1. 定义 Command 与 Handler
34+
35+
```java
36+
public record UserCreateCommand(String name, String email) implements Command {}
37+
38+
public class UserCreateCommandHandler implements Handler<UserCreateCommand, Void> {
39+
@Override
40+
public Void handle(UserCreateCommand message) {
41+
System.out.println("create user: " + message.email());
42+
return null;
43+
}
44+
}
45+
```
46+
47+
### 2. (可选)定义 Validator
48+
49+
```java
50+
public class UserCreateCommandValidator implements Validator<UserCreateCommand> {
51+
@Override
52+
public ValidationResult validate(UserCreateCommand message) {
53+
if (message.name() == null || message.name().isBlank()) {
54+
return ValidationResult.failure("Name is required");
55+
}
56+
if (message.email() == null || !message.email().contains("@")) {
57+
return ValidationResult.failure("Email is invalid");
58+
}
59+
return ValidationResult.success();
60+
}
61+
}
62+
```
63+
64+
### 3. 组装 Mediator
65+
66+
```java
67+
Mediator mediator = new PipelinedMediator()
68+
.use(() -> java.util.stream.Stream.of(new UserCreateCommandHandler()))
69+
.use(() -> java.util.stream.Stream.of(new UserCreateCommandValidator()))
70+
.use(() -> java.util.stream.Stream.of(
71+
(message, next) -> {
72+
long start = System.nanoTime();
73+
try {
74+
return next.invoke();
75+
} finally {
76+
long cost = System.nanoTime() - start;
77+
System.out.println("handled " + message.getClass().getSimpleName() + " in " + cost + " ns");
78+
}
79+
}
80+
));
81+
```
82+
83+
### 4. 发送消息
84+
85+
```java
86+
mediator.send(new UserCreateCommand("Alice", "alice@example.com"));
87+
```
88+
89+
如校验失败,会抛出 `ValidationException`,可通过 `getErrors()` 读取错误列表。
90+
91+
## Spring Boot 集成方法
92+
93+
本项目本身不依赖 Spring;推荐在你的 Spring Boot 业务工程中引入该库后,通过 `@Configuration` 装配 `Mediator`
94+
95+
### 1. 在业务工程引入依赖
96+
97+
如果你已将该库发布到私有仓库或本地仓库,可在业务工程 `pom.xml` 中添加:
98+
99+
```xml
100+
<dependency>
101+
<groupId>com.nerosoft</groupId>
102+
<artifactId>Mediator</artifactId>
103+
<version>1.0.0</version>
104+
</dependency>
105+
```
106+
107+
### 2. 将 Handler / Validator / Middleware 交给 Spring 管理
108+
109+
```java
110+
@Component
111+
public class UserCreateCommandHandler implements Handler<UserCreateCommand, Void> {
112+
@Override
113+
public Void handle(UserCreateCommand message) {
114+
return null;
115+
}
116+
}
117+
118+
@Component
119+
public class UserCreateCommandValidator implements Validator<UserCreateCommand> {
120+
@Override
121+
public ValidationResult validate(UserCreateCommand message) {
122+
return ValidationResult.success();
123+
}
124+
}
125+
```
126+
127+
### 3. 在配置类中组装 `PipelinedMediator`
128+
129+
```java
130+
import com.nerosoft.mediator.*;
131+
import org.springframework.context.ApplicationContext;
132+
import org.springframework.context.annotation.Bean;
133+
import org.springframework.context.annotation.Configuration;
134+
135+
import java.util.concurrent.Executors;
136+
137+
@Configuration
138+
public class MediatorConfiguration {
139+
140+
@Bean
141+
public Mediator mediator(ApplicationContext applicationContext) {
142+
return new PipelinedMediator()
143+
.use(() -> applicationContext.getBeansOfType(Handler.class).values().stream())
144+
.use(() -> applicationContext.getBeansOfType(Validator.class).values().stream())
145+
.use(() -> applicationContext.getBeansOfType(Middleware.class).values().stream())
146+
.use(() -> Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()));
147+
}
148+
}
149+
```
150+
151+
### 4. 在业务服务中使用
152+
153+
```java
154+
import com.nerosoft.mediator.Mediator;
155+
import org.springframework.stereotype.Service;
156+
157+
@Service
158+
public class UserApplicationService {
159+
private final Mediator mediator;
160+
161+
public UserApplicationService(Mediator mediator) {
162+
this.mediator = mediator;
163+
}
164+
165+
public void createUser(String name, String email) {
166+
mediator.send(new UserCreateCommand(name, email));
167+
}
168+
}
169+
```
170+
171+
说明:
172+
173+
- `Handler` 默认按“消息泛型类型”匹配
174+
- 多个 `Middleware` 会按流顺序组成责任链
175+
- `Validator` 返回失败时会抛出 `ValidationException`,可统一在全局异常处理中转换为 HTTP 响应
176+
177+
## 中间件使用方法
178+
179+
`Middleware` 用于在消息进入 `Handler` 之前或之后插入通用逻辑,适合做日志、耗时统计、权限检查、审计、链路追踪等横切处理。
180+
181+
### 1. 中间件接口
182+
183+
```java
184+
@FunctionalInterface
185+
public interface Middleware {
186+
Object handle(com.nerosoft.mediator.internal.Message message, com.nerosoft.mediator.internal.MiddlewareDelegate next);
187+
}
188+
```
189+
190+
其中:
191+
192+
- `message`:当前正在处理的消息
193+
- `next`:责任链中的下一个中间件或最终 `Handler`
194+
195+
### 2. 注册中间件
196+
197+
在创建 `PipelinedMediator` 时,通过 `.use(() -> Stream.of(...))` 传入中间件流:
198+
199+
```java
200+
Mediator mediator = new PipelinedMediator()
201+
.use(() -> java.util.stream.Stream.of(new UserCreateCommandHandler()))
202+
.use(() -> java.util.stream.Stream.of(new UserCreateCommandValidator()))
203+
.use(() -> java.util.stream.Stream.of(
204+
(message, next) -> {
205+
System.out.println("before: " + message.getClass().getSimpleName());
206+
try {
207+
return next.invoke();
208+
} finally {
209+
System.out.println("after: " + message.getClass().getSimpleName());
210+
}
211+
}
212+
));
213+
```
214+
215+
### 3. 执行顺序
216+
217+
中间件会按照注册顺序形成链式调用:
218+
219+
1. 第一个中间件先执行
220+
2. 调用 `next.invoke()` 进入下一个中间件
221+
3. 最后到达对应的 `Handler`
222+
4. 返回结果后,中间件可以继续做收尾处理
223+
224+
如果你注册了多个中间件,它们的执行顺序与传入流的顺序一致。
225+
226+
### 4. 常见使用场景
227+
228+
#### 日志与耗时统计
229+
230+
```java
231+
(message, next) -> {
232+
long start = System.nanoTime();
233+
try {
234+
return next.invoke();
235+
} finally {
236+
long cost = System.nanoTime() - start;
237+
System.out.println("cost(ns): " + cost);
238+
}
239+
}
240+
```
241+
242+
#### 权限或参数预检查
243+
244+
```java
245+
(message, next) -> {
246+
if (message == null) {
247+
throw new IllegalArgumentException("message can not be null");
248+
}
249+
return next.invoke();
250+
}
251+
```
252+
253+
### 5. 与 Spring Boot 结合
254+
255+
如果项目已集成 Spring Boot,可以把中间件声明成 `@Component`,然后在配置类中统一注入到 `PipelinedMediator`
256+
257+
```java
258+
@Bean
259+
public Mediator mediator(ApplicationContext applicationContext) {
260+
return new PipelinedMediator()
261+
.use(() -> applicationContext.getBeansOfType(Middleware.class).values().stream());
262+
}
263+
```
264+
265+
## Event 并行策略(可选)
266+
267+
给事件类型添加注解控制并发行为:
268+
269+
```java
270+
@HandlerParallelStrategy(HandlerParallelStrategy.WHEN_ALL)
271+
@HandlerExceptionStrategy(HandlerExceptionStrategy.CONTINUE)
272+
public class UserCreatedEvent implements Event {}
273+
```
274+
275+
- `NO_WAIT`:派发后不等待
276+
- `WHEN_ALL`:等待全部处理器完成
277+
- `WHEN_ANY`:任一处理器完成即继续
278+
- `STOP`:任一处理器异常立即终止
279+
- `CONTINUE`:收集异常,最后统一抛出
280+
281+
## 包内容
282+
283+
- `com.nerosoft.mediator`
284+
- 核心抽象:`Mediator``Command``Query``Event`
285+
- 扩展点:`Handler``Middleware``Validator`
286+
- 默认实现:`PipelinedMediator`
287+
- `com.nerosoft.mediator.strategy`
288+
- 事件并行与异常策略注解
289+
- `com.nerosoft.mediator.validation`
290+
- `ValidationResult``ValidationException`
291+
- `com.nerosoft.mediator.internal`
292+
- 内部支持类型(消息基类、流供应器、异常聚合等)
293+
294+
## 快速构建
295+
296+
```bash
297+
mvn clean test
298+
```
299+
300+
如果本地 JDK 版本与 `pom.xml` 不一致,请先调整 JDK 或修改 `maven.compiler.source/target`

pom.xml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,18 @@
1414
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
1515
</properties>
1616

17+
<dependencies>
18+
<dependency>
19+
<groupId>org.junit.jupiter</groupId>
20+
<artifactId>junit-jupiter</artifactId>
21+
<version>6.0.3</version>
22+
<scope>test</scope>
23+
</dependency>
24+
<dependency>
25+
<groupId>org.junit.jupiter</groupId>
26+
<artifactId>junit-jupiter-api</artifactId>
27+
<version>6.0.3</version>
28+
<scope>test</scope>
29+
</dependency>
30+
</dependencies>
1731
</project>
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.nerosoft.mediator;
2+
3+
import com.nerosoft.mediator.internal.ExceptionHandle;
4+
5+
import static java.util.concurrent.CompletableFuture.runAsync;
6+
7+
import java.util.List;
8+
import java.util.concurrent.CompletableFuture;
9+
import java.util.concurrent.ExecutorService;
10+
11+
class Executor {
12+
static void run(List<Runnable> tasks, ExecutorService concurrentPolicy, ExceptionHandle onException) {
13+
try {
14+
tasks.forEach(task -> runAsync(task, concurrentPolicy));
15+
} catch (Throwable e) {
16+
onException.handleException(e);
17+
}
18+
}
19+
20+
static void whenAll(List<Runnable> tasks, ExecutorService concurrentPolicy, ExceptionHandle onException) {
21+
CompletableFuture.allOf(tasks.stream()
22+
.map(task -> {
23+
return CompletableFuture.runAsync(task, concurrentPolicy)
24+
.exceptionally(ex -> {
25+
onException.handleException(ex);
26+
return null;
27+
});
28+
})
29+
.toArray(CompletableFuture[]::new))
30+
.join();
31+
}
32+
33+
static void whenAny(List<Runnable> tasks, ExecutorService concurrentPolicy, ExceptionHandle onException) {
34+
List<CompletableFuture<Void>> futures = tasks.stream()
35+
.map(task -> CompletableFuture.runAsync(task, concurrentPolicy)
36+
.exceptionally(ex -> {
37+
onException.handleException(ex);
38+
return null;
39+
}))
40+
.toList();
41+
CompletableFuture.anyOf(futures.toArray(new CompletableFuture[]{})).join();
42+
}
43+
}

0 commit comments

Comments
 (0)