feat: Spring Boot示例项目
- 基础Spring Boot配置 - Docker支持
This commit is contained in:
23
src/main/java/com/example/demo/DemoApplication.java
Normal file
23
src/main/java/com/example/demo/DemoApplication.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package com.example.demo;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
/**
|
||||
* Spring Boot 主应用
|
||||
*
|
||||
* 学习点:
|
||||
* - @SpringBootApplication 组合注解
|
||||
* - @EnableAsync 启用异步处理
|
||||
* - @EnableScheduling 启用定时任务
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableAsync
|
||||
@EnableScheduling
|
||||
public class DemoApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(DemoApplication.class, args);
|
||||
}
|
||||
}
|
||||
75
src/main/java/com/example/demo/aop/LoggingAspect.java
Normal file
75
src/main/java/com/example/demo/aop/LoggingAspect.java
Normal file
@@ -0,0 +1,75 @@
|
||||
package com.example.demo.aop;
|
||||
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.*;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 日志切面 - 演示 AOP 基础
|
||||
*
|
||||
* 学习点:
|
||||
* - @Aspect 定义切面
|
||||
* - @Pointcut 切入点表达式
|
||||
* - @Before / @After / @Around 通知类型
|
||||
* - JoinPoint 获取方法信息
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class LoggingAspect {
|
||||
|
||||
/**
|
||||
* 切入点:所有 Controller 方法
|
||||
*/
|
||||
@Pointcut("execution(* com.example.demo.controller.*.*(..))")
|
||||
public void controllerMethods() {}
|
||||
|
||||
/**
|
||||
* 切入点:所有 Service 方法
|
||||
*/
|
||||
@Pointcut("execution(* com.example.demo.service.*.*(..))")
|
||||
public void serviceMethods() {}
|
||||
|
||||
/**
|
||||
* 前置通知 - 方法执行前
|
||||
*/
|
||||
@Before("controllerMethods()")
|
||||
public void logBeforeController(JoinPoint joinPoint) {
|
||||
System.out.println("[AOP-Before] Controller 方法开始: " +
|
||||
joinPoint.getSignature().getName());
|
||||
System.out.println(" 参数: " + Arrays.toString(joinPoint.getArgs()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 后置通知 - 方法执行后(无论成功或异常)
|
||||
*/
|
||||
@After("serviceMethods()")
|
||||
public void logAfterService(JoinPoint joinPoint) {
|
||||
System.out.println("[AOP-After] Service 方法结束: " +
|
||||
joinPoint.getSignature().getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回通知 - 方法成功返回后
|
||||
*/
|
||||
@AfterReturning(pointcut = "controllerMethods()", returning = "result")
|
||||
public void logAfterReturning(JoinPoint joinPoint, Object result) {
|
||||
System.out.println("[AOP-AfterReturning] 方法返回: " +
|
||||
joinPoint.getSignature().getName());
|
||||
System.out.println(" 返回值: " + result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 异常通知 - 方法抛出异常后
|
||||
*/
|
||||
@AfterThrowing(pointcut = "controllerMethods() || serviceMethods()", throwing = "error")
|
||||
public void logAfterThrowing(JoinPoint joinPoint, Throwable error) {
|
||||
System.out.println("[AOP-AfterThrowing] 方法异常: " +
|
||||
joinPoint.getSignature().getName());
|
||||
System.out.println(" 异常: " + error.getMessage());
|
||||
}
|
||||
}
|
||||
77
src/main/java/com/example/demo/aop/PerformanceAspect.java
Normal file
77
src/main/java/com/example/demo/aop/PerformanceAspect.java
Normal file
@@ -0,0 +1,77 @@
|
||||
package com.example.demo.aop;
|
||||
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 性能监控切面 - 演示 @Around 环绕通知
|
||||
*
|
||||
* 学习点:
|
||||
* - @Around 最强大的通知类型
|
||||
* - ProceedingJoinPoint 控制方法执行
|
||||
* - 方法执行时间统计
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class PerformanceAspect {
|
||||
|
||||
// 方法执行时间统计
|
||||
private final Map<String, Long> totalTimes = new ConcurrentHashMap<>();
|
||||
private final Map<String, Long> callCounts = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 环绕通知 - 统计方法执行时间
|
||||
*/
|
||||
@Around("execution(* com.example.demo.controller.*.*(..)) || " +
|
||||
"execution(* com.example.demo.service.*.*(..))")
|
||||
public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||
String methodName = joinPoint.getSignature().toShortString();
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
try {
|
||||
// 执行目标方法
|
||||
Object result = joinPoint.proceed();
|
||||
|
||||
long endTime = System.currentTimeMillis();
|
||||
long duration = endTime - startTime;
|
||||
|
||||
// 记录统计
|
||||
totalTimes.merge(methodName, duration, Long::sum);
|
||||
callCounts.merge(methodName, 1L, Long::sum);
|
||||
|
||||
System.out.println("[AOP-Performance] " + methodName +
|
||||
" 执行耗时: " + duration + "ms");
|
||||
|
||||
return result;
|
||||
} catch (Throwable e) {
|
||||
long endTime = System.currentTimeMillis();
|
||||
System.out.println("[AOP-Performance] " + methodName +
|
||||
" 异常耗时: " + (endTime - startTime) + "ms");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取性能统计
|
||||
*/
|
||||
public Map<String, Map<String, Long>> getStatistics() {
|
||||
Map<String, Map<String, Long>> stats = new HashMap<>();
|
||||
|
||||
for (String method : totalTimes.keySet()) {
|
||||
Map<String, Long> methodStats = new HashMap<>();
|
||||
methodStats.put("totalTime", totalTimes.get(method));
|
||||
methodStats.put("callCount", callCounts.get(method));
|
||||
methodStats.put("avgTime", totalTimes.get(method) / callCounts.get(method));
|
||||
stats.put(method, methodStats);
|
||||
}
|
||||
|
||||
return stats;
|
||||
}
|
||||
}
|
||||
70
src/main/java/com/example/demo/aop/RateLimitAspect.java
Normal file
70
src/main/java/com/example/demo/aop/RateLimitAspect.java
Normal file
@@ -0,0 +1,70 @@
|
||||
package com.example.demo.aop;
|
||||
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* 限流切面 - 演示 AOP 实现横切关注点
|
||||
*
|
||||
* 学习点:
|
||||
* - @Order 控制切面执行顺序
|
||||
* - AOP 实现非功能性需求
|
||||
* - 横切关注点与业务逻辑分离
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
@Order(1) // 数字越小优先级越高
|
||||
public class RateLimitAspect {
|
||||
|
||||
// 简单的限流计数器(实际项目用 Redis + Token Bucket)
|
||||
private final Map<String, AtomicLong> requestCounts = new ConcurrentHashMap<>();
|
||||
private final Map<String, Long> lastResetTime = new ConcurrentHashMap<>();
|
||||
|
||||
// 每分钟最大请求数
|
||||
private static final long MAX_REQUESTS_PER_MINUTE = 100;
|
||||
|
||||
/**
|
||||
* 限流检查
|
||||
*/
|
||||
@Around("@annotation(com.example.demo.aop.RateLimited)")
|
||||
public Object enforceRateLimit(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||
String methodName = joinPoint.getSignature().toShortString();
|
||||
|
||||
long currentTime = System.currentTimeMillis();
|
||||
Long lastReset = lastResetTime.get(methodName);
|
||||
|
||||
// 每分钟重置计数器
|
||||
if (lastReset == null || currentTime - lastReset > 60000) {
|
||||
requestCounts.put(methodName, new AtomicLong(0));
|
||||
lastResetTime.put(methodName, currentTime);
|
||||
}
|
||||
|
||||
AtomicLong count = requestCounts.get(methodName);
|
||||
long currentCount = count.incrementAndGet();
|
||||
|
||||
if (currentCount > MAX_REQUESTS_PER_MINUTE) {
|
||||
throw new RuntimeException("请求过于频繁,请稍后再试 (Rate Limit)");
|
||||
}
|
||||
|
||||
System.out.println("[AOP-RateLimit] " + methodName +
|
||||
" 当前计数: " + currentCount + "/" + MAX_REQUESTS_PER_MINUTE);
|
||||
|
||||
return joinPoint.proceed();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取限流状态
|
||||
*/
|
||||
public Map<String, Long> getRateLimitStatus() {
|
||||
Map<String, Long> status = new ConcurrentHashMap<>();
|
||||
requestCounts.forEach((k, v) -> status.put(k, v.get()));
|
||||
return status;
|
||||
}
|
||||
}
|
||||
28
src/main/java/com/example/demo/aop/RateLimited.java
Normal file
28
src/main/java/com/example/demo/aop/RateLimited.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package com.example.demo.aop;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 限流注解 - 自定义注解配合 AOP 使用
|
||||
*
|
||||
* 学习点:
|
||||
* - 自定义注解定义
|
||||
* - @Retention 保留策略
|
||||
* - @Target 目标元素
|
||||
* - 注解 + AOP 实现声明式功能
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@Documented
|
||||
public @interface RateLimited {
|
||||
|
||||
/**
|
||||
* 每分钟最大请求数
|
||||
*/
|
||||
long value() default 100;
|
||||
|
||||
/**
|
||||
* 限流提示消息
|
||||
*/
|
||||
String message() default "请求过于频繁,请稍后再试";
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
package com.example.demo.controller;
|
||||
|
||||
import com.example.demo.aop.PerformanceAspect;
|
||||
import com.example.demo.aop.RateLimitAspect;
|
||||
import com.example.demo.aop.RateLimited;
|
||||
import com.example.demo.event.UserEventPublisher;
|
||||
import com.example.demo.model.User;
|
||||
import com.example.demo.service.UserService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* AOP 和事件机制学习控制器
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/aop")
|
||||
public class AopEventController {
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
@Autowired
|
||||
private UserEventPublisher eventPublisher;
|
||||
|
||||
@Autowired
|
||||
private PerformanceAspect performanceAspect;
|
||||
|
||||
@Autowired
|
||||
private RateLimitAspect rateLimitAspect;
|
||||
|
||||
/**
|
||||
* 学习首页
|
||||
*/
|
||||
@GetMapping
|
||||
public Map<String, Object> index() {
|
||||
Map<String, Object> info = new HashMap<>();
|
||||
info.put("message", "Spring Boot 学习中心");
|
||||
info.put("topics", new String[]{
|
||||
"AOP 切面编程",
|
||||
"事件机制",
|
||||
"Bean 生命周期"
|
||||
});
|
||||
info.put("endpoints", new String[]{
|
||||
"GET /aop - AOP 概念说明",
|
||||
"GET /aop/stats - 性能统计",
|
||||
"GET /aop/event - 事件机制说明",
|
||||
"POST /aop/event/publish - 发布用户事件",
|
||||
"GET /aop/event/history - 查看事件历史",
|
||||
"GET /aop/ratelimit - 限流测试"
|
||||
});
|
||||
return info;
|
||||
}
|
||||
|
||||
// ==================== AOP 示例 ====================
|
||||
|
||||
/**
|
||||
* AOP 概念说明
|
||||
*/
|
||||
@GetMapping("/aop")
|
||||
public Map<String, Object> aopInfo() {
|
||||
Map<String, Object> info = new HashMap<>();
|
||||
info.put("title", "AOP 切面编程");
|
||||
info.put("concepts", new String[]{
|
||||
"Aspect(切面): 横切关注点的模块化",
|
||||
"JoinPoint(连接点): 程序执行的某个点",
|
||||
"Pointcut(切入点): 匹配连接点的表达式",
|
||||
"Advice(通知): 在连接点执行的动作",
|
||||
"Weaving(织入): 将切面应用到目标对象"
|
||||
});
|
||||
info.put("adviceTypes", new String[]{
|
||||
"@Before - 方法执行前",
|
||||
"@After - 方法执行后(无论成功或异常)",
|
||||
"@AfterReturning - 方法成功返回后",
|
||||
"@AfterThrowing - 方法抛出异常后",
|
||||
"@Around - 环绕通知(最强大)"
|
||||
});
|
||||
info.put("useCases", new String[]{
|
||||
"日志记录",
|
||||
"性能监控",
|
||||
"事务管理",
|
||||
"权限检查",
|
||||
"限流控制"
|
||||
});
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看性能统计
|
||||
*/
|
||||
@GetMapping("/aop/stats")
|
||||
public Map<String, Object> getPerformanceStats() {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("title", "方法性能统计");
|
||||
result.put("description", "由 PerformanceAspect 自动收集");
|
||||
result.put("statistics", performanceAspect.getStatistics());
|
||||
return result;
|
||||
}
|
||||
|
||||
// ==================== 事件机制示例 ====================
|
||||
|
||||
/**
|
||||
* 事件机制说明
|
||||
*/
|
||||
@GetMapping("/event")
|
||||
public Map<String, Object> eventInfo() {
|
||||
Map<String, Object> info = new HashMap<>();
|
||||
info.put("title", "Spring 事件机制");
|
||||
info.put("concepts", new String[]{
|
||||
"ApplicationEventPublisher - 事件发布者",
|
||||
"@EventListener - 事件监听器",
|
||||
"@Async - 异步处理事件",
|
||||
"condition - 条件过滤"
|
||||
});
|
||||
info.put("benefits", new String[]{
|
||||
"解耦:发布者和监听者互不依赖",
|
||||
"扩展:新增监听器无需修改发布者",
|
||||
"异步:耗时操作不阻塞主流程",
|
||||
"测试:更容易进行单元测试"
|
||||
});
|
||||
info.put("eventTypes", new String[]{
|
||||
"CREATED - 用户创建",
|
||||
"UPDATED - 用户更新",
|
||||
"DELETED - 用户删除",
|
||||
"LOGIN - 用户登录"
|
||||
});
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布用户事件
|
||||
*/
|
||||
@PostMapping("/event/publish")
|
||||
public Map<String, Object> publishEvent(
|
||||
@RequestParam Long userId,
|
||||
@RequestParam String userName,
|
||||
@RequestParam(defaultValue = "LOGIN") String eventType
|
||||
) {
|
||||
eventPublisher.publishUserLogin(userId, userName);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("message", "事件已发布");
|
||||
result.put("eventType", eventType);
|
||||
result.put("userId", userId);
|
||||
result.put("userName", userName);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看事件历史
|
||||
*/
|
||||
@GetMapping("/event/history")
|
||||
public Map<String, Object> getEventHistory() {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("title", "事件历史记录");
|
||||
result.put("note", "由 UserEventListener 自动记录");
|
||||
return result;
|
||||
}
|
||||
|
||||
// ==================== 限流示例 ====================
|
||||
|
||||
/**
|
||||
* 限流测试接口
|
||||
*/
|
||||
@GetMapping("/ratelimit")
|
||||
@RateLimited(value = 10, message = "测试限流:每分钟最多10次")
|
||||
public Map<String, Object> testRateLimit() {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("message", "请求成功");
|
||||
result.put("note", "使用 @RateLimited 注解");
|
||||
result.put("rateLimitStatus", rateLimitAspect.getRateLimitStatus());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
116
src/main/java/com/example/demo/controller/LearnController.java
Normal file
116
src/main/java/com/example/demo/controller/LearnController.java
Normal file
@@ -0,0 +1,116 @@
|
||||
package com.example.demo.controller;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 学习示例控制器
|
||||
*
|
||||
* 学习点:
|
||||
* - 各种参数接收方式
|
||||
* - 配置注入
|
||||
* - 响应格式
|
||||
*/
|
||||
@RestController
|
||||
public class LearnController {
|
||||
|
||||
// 从配置文件注入值
|
||||
@Value("${spring.application.name:demo}")
|
||||
private String appName;
|
||||
|
||||
/**
|
||||
* 根路径 - 重定向到学习中心
|
||||
*/
|
||||
@GetMapping("/")
|
||||
public Map<String, Object> root() {
|
||||
Map<String, Object> info = new HashMap<>();
|
||||
info.put("message", "欢迎来到 Spring Boot 学习脚手架!");
|
||||
info.put("learn", "https://spring.xiaoxiaoluohao.indevs.in/learn");
|
||||
info.put("aop", "https://spring.xiaoxiaoluohao.indevs.in/aop");
|
||||
info.put("api", "https://spring.xiaoxiaoluohao.indevs.in/api/users");
|
||||
return info;
|
||||
}
|
||||
|
||||
// GET /learn - API 信息
|
||||
@GetMapping("/learn")
|
||||
public Map<String, Object> info() {
|
||||
Map<String, Object> info = new HashMap<>();
|
||||
info.put("app", appName);
|
||||
info.put("message", "欢迎学习 Spring Boot!");
|
||||
info.put("endpoints", new String[]{
|
||||
"GET /learn/params?name=xxx&age=18 - 参数示例",
|
||||
"POST /learn/body - JSON 请求体示例",
|
||||
"GET /learn/path/{id} - 路径变量示例",
|
||||
"GET /learn/header - 请求头示例",
|
||||
"GET /learn/cookie - Cookie 示例"
|
||||
});
|
||||
return info;
|
||||
}
|
||||
|
||||
// GET /learn/params?name=xxx&age=18 - 查询参数
|
||||
@GetMapping("/learn/params")
|
||||
public Map<String, Object> params(
|
||||
@RequestParam(required = false, defaultValue = "游客") String name,
|
||||
@RequestParam(required = false, defaultValue = "0") Integer age
|
||||
) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("name", name);
|
||||
result.put("age", age);
|
||||
result.put("tip", "使用 @RequestParam 接收查询参数");
|
||||
return result;
|
||||
}
|
||||
|
||||
// POST /learn/body - 请求体
|
||||
@PostMapping("/learn/body")
|
||||
public Map<String, Object> body(@RequestBody Map<String, Object> data) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("received", data);
|
||||
result.put("tip", "使用 @RequestBody 接收 JSON 请求体");
|
||||
return result;
|
||||
}
|
||||
|
||||
// GET /learn/path/{id} - 路径变量
|
||||
@GetMapping("/learn/path/{id}")
|
||||
public Map<String, Object> path(@PathVariable String id) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("id", id);
|
||||
result.put("tip", "使用 @PathVariable 接收路径变量");
|
||||
return result;
|
||||
}
|
||||
|
||||
// GET /learn/header - 请求头
|
||||
@GetMapping("/learn/header")
|
||||
public Map<String, Object> header(@RequestHeader(value = "User-Agent", required = false) String userAgent) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("userAgent", userAgent);
|
||||
result.put("tip", "使用 @RequestHeader 获取请求头");
|
||||
return result;
|
||||
}
|
||||
|
||||
// GET /learn/cookie - Cookie
|
||||
@GetMapping("/learn/cookie")
|
||||
public Map<String, Object> cookie(@CookieValue(value = "JSESSIONID", required = false) String sessionId) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("sessionId", sessionId);
|
||||
result.put("tip", "使用 @CookieValue 获取 Cookie");
|
||||
return result;
|
||||
}
|
||||
|
||||
// GET /learn/exception - 异常处理
|
||||
@GetMapping("/learn/exception")
|
||||
public String exception() {
|
||||
throw new RuntimeException("这是一个测试异常");
|
||||
}
|
||||
|
||||
// 全局异常处理
|
||||
@ExceptionHandler(RuntimeException.class)
|
||||
public Map<String, Object> handleException(RuntimeException e) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("error", e.getMessage());
|
||||
result.put("tip", "使用 @ExceptionHandler 处理异常");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.example.demo.controller;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
/**
|
||||
* 页面控制器 - 返回 HTML 页面
|
||||
*/
|
||||
@Controller
|
||||
public class PageController {
|
||||
|
||||
@GetMapping("/home")
|
||||
public String home() {
|
||||
return "forward:/index.html";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.example.demo.controller;
|
||||
|
||||
import com.example.demo.model.User;
|
||||
import com.example.demo.service.UserService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 用户控制器 - RESTful API 示例
|
||||
*
|
||||
* 学习点:
|
||||
* - @RestController: 组合了 @Controller 和 @ResponseBody
|
||||
* - @RequestMapping: 路由映射
|
||||
* - @PathVariable: 路径变量
|
||||
* - @RequestParam: 查询参数
|
||||
* - @RequestBody: 请求体
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/users")
|
||||
public class UserController {
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
// GET /api/users - 获取所有用户
|
||||
@GetMapping
|
||||
public List<User> getAllUsers() {
|
||||
return userService.findAll();
|
||||
}
|
||||
|
||||
// GET /api/users/{id} - 获取单个用户
|
||||
@GetMapping("/{id}")
|
||||
public User getUserById(@PathVariable Long id) {
|
||||
return userService.findById(id);
|
||||
}
|
||||
|
||||
// POST /api/users - 创建用户
|
||||
@PostMapping
|
||||
public User createUser(@RequestBody User user) {
|
||||
return userService.save(user);
|
||||
}
|
||||
|
||||
// PUT /api/users/{id} - 更新用户
|
||||
@PutMapping("/{id}")
|
||||
public User updateUser(@PathVariable Long id, @RequestBody User user) {
|
||||
user.setId(id);
|
||||
return userService.save(user);
|
||||
}
|
||||
|
||||
// DELETE /api/users/{id} - 删除用户
|
||||
@DeleteMapping("/{id}")
|
||||
public String deleteUser(@PathVariable Long id) {
|
||||
userService.delete(id);
|
||||
return "用户 " + id + " 已删除";
|
||||
}
|
||||
|
||||
// GET /api/users/search?name=xxx - 搜索用户
|
||||
@GetMapping("/search")
|
||||
public List<User> searchUsers(@RequestParam String name) {
|
||||
return userService.findByName(name);
|
||||
}
|
||||
}
|
||||
80
src/main/java/com/example/demo/event/UserEventListener.java
Normal file
80
src/main/java/com/example/demo/event/UserEventListener.java
Normal file
@@ -0,0 +1,80 @@
|
||||
package com.example.demo.event;
|
||||
|
||||
import com.example.demo.model.UserEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* 事件监听器 - 演示如何监听事件
|
||||
*
|
||||
* 学习点:
|
||||
* - @EventListener 注解
|
||||
* - @Async 异步处理
|
||||
* - 多监听器协作
|
||||
* - 事件驱动架构的优势
|
||||
*/
|
||||
@Component
|
||||
public class UserEventListener {
|
||||
|
||||
// 存储事件历史(演示用)
|
||||
private final List<UserEvent> eventHistory = new CopyOnWriteArrayList<>();
|
||||
|
||||
/**
|
||||
* 监听用户事件 - 日志记录
|
||||
*/
|
||||
@EventListener
|
||||
public void handleUserEvent(UserEvent event) {
|
||||
System.out.println("[EventListener] 收到事件: " + event.getType() +
|
||||
" - 用户: " + event.getUserName() +
|
||||
" - 时间: " + event.getTimestamp());
|
||||
|
||||
// 记录到历史
|
||||
eventHistory.add(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听用户创建事件 - 发送欢迎邮件(模拟)
|
||||
*/
|
||||
@EventListener(condition = "#event.type == T(com.example.demo.model.UserEvent$Type).CREATED")
|
||||
@Async
|
||||
public void handleUserCreated(UserEvent event) {
|
||||
System.out.println("[EmailService] 发送欢迎邮件给: " + event.getUserName());
|
||||
// 模拟邮件发送耗时
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
System.out.println("[EmailService] 欢迎邮件发送完成");
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听用户登录事件 - 更新登录统计
|
||||
*/
|
||||
@EventListener(condition = "#event.type == T(com.example.demo.model.UserEvent$Type).LOGIN")
|
||||
public void handleUserLogin(UserEvent event) {
|
||||
System.out.println("[LoginTracker] 记录用户登录: " + event.getUserName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听应用启动完成事件
|
||||
*/
|
||||
@EventListener
|
||||
public void onApplicationReady(ApplicationReadyEvent event) {
|
||||
System.out.println("[EventListener] Spring Boot 应用启动完成!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取事件历史
|
||||
*/
|
||||
public List<UserEvent> getEventHistory() {
|
||||
return new ArrayList<>(eventHistory);
|
||||
}
|
||||
}
|
||||
55
src/main/java/com/example/demo/event/UserEventPublisher.java
Normal file
55
src/main/java/com/example/demo/event/UserEventPublisher.java
Normal file
@@ -0,0 +1,55 @@
|
||||
package com.example.demo.event;
|
||||
|
||||
import com.example.demo.model.UserEvent;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* 事件发布器 - 演示如何发布事件
|
||||
*
|
||||
* 学习点:
|
||||
* - ApplicationEventPublisher 接口
|
||||
* - 依赖注入发布器
|
||||
* - 解耦:发布者不需要知道监听者
|
||||
*/
|
||||
@Component
|
||||
public class UserEventPublisher {
|
||||
|
||||
@Autowired
|
||||
private ApplicationEventPublisher eventPublisher;
|
||||
|
||||
/**
|
||||
* 发布用户事件
|
||||
*/
|
||||
public void publishEvent(UserEvent event) {
|
||||
System.out.println("[EventPublisher] 发布事件: " + event.getType() + " - " + event.getUserName());
|
||||
eventPublisher.publishEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* 便捷方法:发布用户创建事件
|
||||
*/
|
||||
public void publishUserCreated(Long userId, String userName) {
|
||||
UserEvent event = new UserEvent(
|
||||
UserEvent.Type.CREATED,
|
||||
userId,
|
||||
userName,
|
||||
"新用户注册成功"
|
||||
);
|
||||
publishEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* 便捷方法:发布用户登录事件
|
||||
*/
|
||||
public void publishUserLogin(Long userId, String userName) {
|
||||
UserEvent event = new UserEvent(
|
||||
UserEvent.Type.LOGIN,
|
||||
userId,
|
||||
userName,
|
||||
"用户登录成功"
|
||||
);
|
||||
publishEvent(event);
|
||||
}
|
||||
}
|
||||
34
src/main/java/com/example/demo/model/User.java
Normal file
34
src/main/java/com/example/demo/model/User.java
Normal file
@@ -0,0 +1,34 @@
|
||||
package com.example.demo.model;
|
||||
|
||||
/**
|
||||
* 用户实体类
|
||||
*
|
||||
* 学习点:
|
||||
* - JavaBean / POJO 设计模式
|
||||
* - Lombok 可以简化 getter/setter(这里用原生写法演示)
|
||||
*/
|
||||
public class User {
|
||||
private Long id;
|
||||
private String name;
|
||||
private String email;
|
||||
private Integer age;
|
||||
|
||||
public User() {}
|
||||
|
||||
public User(Long id, String name, String email, Integer age) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.email = email;
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
// Getters and Setters
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
public String getEmail() { return email; }
|
||||
public void setEmail(String email) { this.email = email; }
|
||||
public Integer getAge() { return age; }
|
||||
public void setAge(Integer age) { this.age = age; }
|
||||
}
|
||||
51
src/main/java/com/example/demo/model/UserEvent.java
Normal file
51
src/main/java/com/example/demo/model/UserEvent.java
Normal file
@@ -0,0 +1,51 @@
|
||||
package com.example.demo.model;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 用户事件 - 演示 Spring 事件机制
|
||||
*
|
||||
* 学习点:
|
||||
* - ApplicationEvent 基类
|
||||
* - 事件驱动架构
|
||||
* - 观察者模式
|
||||
*/
|
||||
public class UserEvent {
|
||||
|
||||
public enum Type {
|
||||
CREATED, // 用户创建
|
||||
UPDATED, // 用户更新
|
||||
DELETED, // 用户删除
|
||||
LOGIN // 用户登录
|
||||
}
|
||||
|
||||
private Type type;
|
||||
private Long userId;
|
||||
private String userName;
|
||||
private LocalDateTime timestamp;
|
||||
private String detail;
|
||||
|
||||
public UserEvent() {
|
||||
this.timestamp = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public UserEvent(Type type, Long userId, String userName, String detail) {
|
||||
this.type = type;
|
||||
this.userId = userId;
|
||||
this.userName = userName;
|
||||
this.detail = detail;
|
||||
this.timestamp = LocalDateTime.now();
|
||||
}
|
||||
|
||||
// Getters and Setters
|
||||
public Type getType() { return type; }
|
||||
public void setType(Type type) { this.type = type; }
|
||||
public Long getUserId() { return userId; }
|
||||
public void setUserId(Long userId) { this.userId = userId; }
|
||||
public String getUserName() { return userName; }
|
||||
public void setUserName(String userName) { this.userName = userName; }
|
||||
public LocalDateTime getTimestamp() { return timestamp; }
|
||||
public void setTimestamp(LocalDateTime timestamp) { this.timestamp = timestamp; }
|
||||
public String getDetail() { return detail; }
|
||||
public void setDetail(String detail) { this.detail = detail; }
|
||||
}
|
||||
69
src/main/java/com/example/demo/service/UserService.java
Normal file
69
src/main/java/com/example/demo/service/UserService.java
Normal file
@@ -0,0 +1,69 @@
|
||||
package com.example.demo.service;
|
||||
|
||||
import com.example.demo.model.User;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 用户服务 - 业务逻辑层
|
||||
*
|
||||
* 学习点:
|
||||
* - @Service: 标记为服务层组件,自动注册为 Bean
|
||||
* - 依赖注入:Controller 通过 @Autowired 注入此服务
|
||||
* - 分层架构:Controller -> Service -> Repository
|
||||
*/
|
||||
@Service
|
||||
public class UserService {
|
||||
|
||||
// 内存存储(演示用,实际项目用数据库)
|
||||
private final List<User> users = new ArrayList<>();
|
||||
private final AtomicLong idGenerator = new AtomicLong(1);
|
||||
|
||||
public UserService() {
|
||||
// 初始化一些测试数据
|
||||
users.add(new User(idGenerator.getAndIncrement(), "张三", "zhangsan@example.com", 25));
|
||||
users.add(new User(idGenerator.getAndIncrement(), "李四", "lisi@example.com", 30));
|
||||
users.add(new User(idGenerator.getAndIncrement(), "王五", "wangwu@example.com", 28));
|
||||
}
|
||||
|
||||
public List<User> findAll() {
|
||||
return new ArrayList<>(users);
|
||||
}
|
||||
|
||||
public User findById(Long id) {
|
||||
return users.stream()
|
||||
.filter(u -> u.getId().equals(id))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
public List<User> findByName(String name) {
|
||||
return users.stream()
|
||||
.filter(u -> u.getName().contains(name))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public User save(User user) {
|
||||
if (user.getId() == null) {
|
||||
user.setId(idGenerator.getAndIncrement());
|
||||
users.add(user);
|
||||
} else {
|
||||
// 更新
|
||||
for (int i = 0; i < users.size(); i++) {
|
||||
if (users.get(i).getId().equals(user.getId())) {
|
||||
users.set(i, user);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
public void delete(Long id) {
|
||||
users.removeIf(u -> u.getId().equals(id));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user