feat: Spring Boot示例项目

- 基础Spring Boot配置
- Docker支持
This commit is contained in:
likingcode
2026-03-07 05:43:15 +00:00
commit 539dc41868
47 changed files with 2746 additions and 0 deletions

View 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());
}
}

View 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;
}
}

View 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;
}
}

View 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 "请求过于频繁,请稍后再试";
}