forked from admin/springboot-demo
feat: Spring Boot示例项目
- 基础Spring Boot配置 - Docker支持
This commit is contained in:
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 "请求过于频繁,请稍后再试";
|
||||
}
|
||||
Reference in New Issue
Block a user