88 lines
4.4 KiB
Java
88 lines
4.4 KiB
Java
// Java 基础对照示例(不依赖 Spring,只用 JDK):record、接口 + 构造器注入、Optional、Stream、异常、并发
|
||
// 运行(JDK 21+):java OrderDemo.java —— 单文件源码直接运行,不需要先 javac
|
||
import java.math.BigDecimal;
|
||
import java.util.List;
|
||
import java.util.Map;
|
||
import java.util.Optional;
|
||
import java.util.concurrent.ConcurrentHashMap;
|
||
import java.util.concurrent.ExecutorService;
|
||
import java.util.concurrent.Executors;
|
||
import java.util.concurrent.atomic.AtomicInteger;
|
||
import java.util.stream.Collectors;
|
||
|
||
public class OrderDemo {
|
||
// record:不可变数据类,自动生成构造器、getter、equals/hashCode/toString(类似 Go struct / Python dataclass(frozen))
|
||
record Order(String id, String tenant, String status, BigDecimal amount) {}
|
||
|
||
// 接口 + 实现:Spring 中会用 @Repository 标注,由容器创建并注入
|
||
interface OrderRepository {
|
||
Optional<Order> findById(String id);
|
||
List<Order> findByTenant(String tenant);
|
||
}
|
||
|
||
static class InMemoryOrderRepository implements OrderRepository {
|
||
private final Map<String, Order> data = new ConcurrentHashMap<>();
|
||
InMemoryOrderRepository(List<Order> seed) { seed.forEach(o -> data.put(o.id(), o)); }
|
||
public Optional<Order> findById(String id) { return Optional.ofNullable(data.get(id)); }
|
||
public List<Order> findByTenant(String tenant) {
|
||
return data.values().stream().filter(o -> o.tenant().equals(tenant)).toList();
|
||
}
|
||
}
|
||
|
||
static class NotFoundException extends RuntimeException {
|
||
NotFoundException(String msg) { super(msg); }
|
||
}
|
||
|
||
// 构造器注入:Spring 推荐的方式(@Service),依赖是 final 的,测试时可以直接传入假实现
|
||
static class OrderService {
|
||
private final OrderRepository repo;
|
||
OrderService(OrderRepository repo) { this.repo = repo; }
|
||
|
||
Order get(String tenant, String id) {
|
||
return repo.findById(id)
|
||
.filter(o -> o.tenant().equals(tenant)) // 租户隔离:别的租户的订单当作不存在
|
||
.orElseThrow(() -> new NotFoundException("订单不存在: " + id));
|
||
}
|
||
|
||
Map<String, BigDecimal> sumByStatus(String tenant) { // Stream 分组汇总;金额用 BigDecimal,不用 double
|
||
return repo.findByTenant(tenant).stream()
|
||
.collect(Collectors.groupingBy(Order::status,
|
||
Collectors.reducing(BigDecimal.ZERO, Order::amount, BigDecimal::add)));
|
||
}
|
||
}
|
||
|
||
public static void main(String[] args) throws Exception {
|
||
var repo = new InMemoryOrderRepository(List.of(
|
||
new Order("A001", "t1", "paid", new BigDecimal("19.90")),
|
||
new Order("A002", "t1", "paid", new BigDecimal("0.10")),
|
||
new Order("A003", "t1", "refunded", new BigDecimal("5.00")),
|
||
new Order("B001", "t2", "paid", new BigDecimal("99.00"))));
|
||
var service = new OrderService(repo);
|
||
|
||
check(service.get("t1", "A001").amount().equals(new BigDecimal("19.90")), "查询订单");
|
||
try {
|
||
service.get("t1", "B001");
|
||
check(false, "跨租户应当查不到");
|
||
} catch (NotFoundException e) {
|
||
System.out.println("跨租户查询被拒绝: " + e.getMessage());
|
||
}
|
||
var sums = service.sumByStatus("t1");
|
||
check(sums.get("paid").equals(new BigDecimal("20.00")), "BigDecimal 精确求和");
|
||
System.out.println("t1 按状态汇总: " + new java.util.TreeMap<>(sums));
|
||
System.out.println("用 double 计算金额的问题: 0.1 + 0.2 = " + (0.1 + 0.2));
|
||
|
||
// 并发:虚拟线程(JDK 21 正式特性)+ 原子计数,类比 Go 的 goroutine
|
||
var counter = new AtomicInteger();
|
||
try (ExecutorService pool = Executors.newVirtualThreadPerTaskExecutor()) {
|
||
for (int i = 0; i < 1000; i++) pool.submit(counter::incrementAndGet);
|
||
} // try-with-resources:等待全部任务完成
|
||
check(counter.get() == 1000, "虚拟线程并发计数");
|
||
System.out.println("1000 个虚拟线程计数结果: " + counter.get());
|
||
System.out.println("全部检查通过");
|
||
}
|
||
|
||
static void check(boolean ok, String name) {
|
||
if (!ok) throw new AssertionError("检查失败: " + name);
|
||
}
|
||
}
|