Spring AOP 实战指南:从入门到实际应用
前言
AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架的核心功能之一,它提供了一种优雅的方式来处理横切关注点。本文将深入探讨Spring AOP的核心概念,并通过多个实际应用场景来展示其强大的功能。
技术环境
- JDK: 1.8
- Spring Boot: 2.7.14
- Spring AOP: 5.3.21(Spring Boot 2.7.14内置)
1. Spring AOP 基础概念
1.1 核心概念
- 切面(Aspect): 横切关注点的模块化,如日志、事务等
- 连接点(Join Point): 程序执行的某个特定位置,如方法调用
- 切点(Pointcut): 匹配连接点的表达式
- 通知(Advice): 在切点执行的代码
- 目标对象(Target Object): 被代理的对象
- AOP代理(AOP Proxy): 由AOP框架创建的对象
1.2 通知类型
- @Before: 前置通知,在目标方法执行前执行
- @After: 后置通知,在目标方法执行后执行
- @AfterReturning: 返回通知,在目标方法正常返回后执行
- @AfterThrowing: 异常通知,在目标方法抛出异常后执行
- @Around: 环绕通知,包围目标方法执行
2. 环境配置
2.1 Maven 依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.14</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.7.14</version>
</dependency>
</dependencies>
2.2 启用AOP
@SpringBootApplication
@EnableAspectJAutoProxy
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
3. 实际应用场景
3.1 日志记录
这是AOP最常见的应用场景之一,可以统一记录方法的执行信息。
@Aspect
@Component
@Slf4j
public class LoggingAspect {
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayer() {}
@Around("serviceLayer()")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
// 获取方法信息
String className = joinPoint.getTarget().getClass().getSimpleName();
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
log.info("开始执行: {}.{}(), 参数: {}", className, methodName, Arrays.toString(args));
try {
Object result = joinPoint.proceed();
long endTime = System.currentTimeMillis();
log.info("执行完成: {}.{}(), 耗时: {}ms, 结果: {}",
className, methodName, endTime - startTime, result);
return result;
} catch (Exception e) {
long endTime = System.currentTimeMillis();
log.error("执行异常: {}.{}(), 耗时: {}ms, 异常: {}",
className, methodName, endTime - startTime, e.getMessage());
throw e;
}
}
}
3.2 权限验证
通过AOP可以实现统一的权限控制,避免在每个方法中重复编写权限检查代码。
// 自定义权限注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequirePermission {
String value() default "";
String[] roles() default {};
}
// 权限校验切面
@Aspect
@Component
@Slf4j
public class PermissionAspect {
@Autowired
private UserService userService;
@Before("@annotation(requirePermission)")
public void checkPermission(JoinPoint joinPoint, RequirePermission requirePermission) {
// 获取当前用户
String currentUser = getCurrentUser();
// 检查角色权限
String[] requiredRoles = requirePermission.roles();
if (requiredRoles.length > 0) {
boolean hasRole = userService.hasAnyRole(currentUser, requiredRoles);
if (!hasRole) {
throw new PermissionDeniedException("权限不足,需要角色: " + Arrays.toString(requiredRoles));
}
}
// 检查具体权限
String permission = requirePermission.value();
if (StringUtils.isNotBlank(permission)) {
boolean hasPermission = userService.hasPermission(currentUser, permission);
if (!hasPermission) {
throw new PermissionDeniedException("权限不足,需要权限: " + permission);
}
}
log.info("用户: {} 通过权限验证", currentUser);
}
private String getCurrentUser() {
// 从SecurityContext或Session中获取当前用户
return "currentUser";
}
}
// 使用示例
@RestController
@RequestMapping("/admin")
public class AdminController {
@RequirePermission(roles = {"ADMIN", "MANAGER"})
@GetMapping("/users")
public List<User> getUsers() {
return userService.getAllUsers();
}
@RequirePermission(value = "user:delete", roles = {"ADMIN"})
@DeleteMapping("/users/{id}")
public void deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
}
}
3.3 缓存处理
实现方法级别的缓存,提高系统性能。
// 自定义缓存注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Cacheable {
String key() default "";
int expireSeconds() default 3600;
String condition() default "";
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CacheEvict {
String key() default "";
String[] keys() default {};
boolean allEntries() default false;
}
// 缓存切面
@Aspect
@Component
@Slf4j
public class CacheAspect {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Around("@annotation(cacheable)")
public Object handleCacheable(ProceedingJoinPoint joinPoint, Cacheable cacheable) throws Throwable {
String key = generateKey(joinPoint, cacheable.key());
// 检查条件
if (StringUtils.isNotBlank(cacheable.condition()) && !evaluateCondition(cacheable.condition(), joinPoint)) {
return joinPoint.proceed();
}
// 尝试从缓存获取
Object cached = redisTemplate.opsForValue().get(key);
if (cached != null) {
log.info("缓存命中: {}", key);
return cached;
}
// 执行方法并缓存结果
Object result = joinPoint.proceed();
if (result != null) {
redisTemplate.opsForValue().set(key, result, cacheable.expireSeconds(), TimeUnit.SECONDS);
log.info("缓存存储: {}, 过期时间: {}秒", key, cacheable.expireSeconds());
}
return result;
}
@After("@annotation(cacheEvict)")
public void handleCacheEvict(JoinPoint joinPoint, CacheEvict cacheEvict) {
if (cacheEvict.allEntries()) {
// 清空所有缓存
Set<String> keys = redisTemplate.keys("*");
if (keys != null && !keys.isEmpty()) {
redisTemplate.delete(keys);
log.info("清空所有缓存");
}
} else {
// 清空指定缓存
String[] keys = cacheEvict.keys();
if (keys.length == 0) {
String key = generateKey(joinPoint, cacheEvict.key());
redisTemplate.delete(key);
log.info("清空缓存: {}", key);
} else {
redisTemplate.delete(Arrays.asList(keys));
log.info("清空缓存: {}", Arrays.toString(keys));
}
}
}
private String generateKey(JoinPoint joinPoint, String keyExpression) {
if (StringUtils.isBlank(keyExpression)) {
// 默认key生成策略
return joinPoint.getSignature().toShortString() + ":" + Arrays.hashCode(joinPoint.getArgs());
}
// 可以实现SpEL表达式解析
return keyExpression;
}
private boolean evaluateCondition(String condition, JoinPoint joinPoint) {
// 简单条件评估,实际项目中可使用SpEL
return true;
}
}
// 使用示例
@Service
public class UserService {
@Cacheable(key = "user:${id}", expireSeconds = 1800)
public User getUserById(Long id) {
// 模拟数据库查询
return userRepository.findById(id);
}
@CacheEvict(key = "user:${user.id}")
public void updateUser(User user) {
userRepository.save(user);
}
@CacheEvict(allEntries = true)
public void clearAllUserCache() {
log.info("清空所有用户缓存");
}
}
3.4 异常处理
统一处理业务异常,提供更好的错误信息。
@Aspect
@Component
@Slf4j
public class ExceptionHandlingAspect {
@AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", throwing = "ex")
public void handleServiceException(JoinPoint joinPoint, Exception ex) {
String className = joinPoint.getTarget().getClass().getSimpleName();
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
// 记录异常日志
log.error("服务异常 - 类: {}, 方法: {}, 参数: {}, 异常: {}",
className, methodName, Arrays.toString(args), ex.getMessage(), ex);
// 发送异常通知
sendExceptionNotification(className, methodName, ex);
// 异常转换
if (ex instanceof DataAccessException) {
throw new ServiceException("数据访问异常", ex);
} else if (ex instanceof IllegalArgumentException) {
throw new ServiceException("参数错误: " + ex.getMessage(), ex);
}
}
private void sendExceptionNotification(String className, String methodName, Exception ex) {
// 发送邮件、短信或其他通知方式
log.info("发送异常通知: {}.{} - {}", className, methodName, ex.getMessage());
}
}
3.5 性能监控
监控方法执行时间,识别性能瓶颈。
@Aspect
@Component
@Slf4j
public class PerformanceMonitoringAspect {
private final MeterRegistry meterRegistry;
public PerformanceMonitoringAspect(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
@Around("execution(* com.example.service.*.*(..)) || execution(* com.example.controller.*.*(..))")
public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
String className = joinPoint.getTarget().getClass().getSimpleName();
String methodName = joinPoint.getSignature().getName();
String metricName = className + "." + methodName;
Timer.Sample sample = Timer.start(meterRegistry);
try {
Object result = joinPoint.proceed();
// 记录成功指标
sample.stop(Timer.builder("method.execution.time")
.tag("class", className)
.tag("method", methodName)
.tag("status", "success")
.register(meterRegistry));
return result;
} catch (Exception e) {
// 记录失败指标
sample.stop(Timer.builder("method.execution.time")
.tag("class", className)
.tag("method", methodName)
.tag("status", "error")
.register(meterRegistry));
// 增加错误计数
Counter.builder("method.execution.errors")
.tag("class", className)
.tag("method", methodName)
.tag("exception", e.getClass().getSimpleName())
.register(meterRegistry)
.increment();
throw e;
}
}
@AfterReturning("execution(* com.example.service.*.*(..))")
public void recordMethodExecution(JoinPoint joinPoint) {
String className = joinPoint.getTarget().getClass().getSimpleName();
String methodName = joinPoint.getSignature().getName();
// 记录方法调用次数
Counter.builder("method.execution.count")
.tag("class", className)
.tag("method", methodName)
.register(meterRegistry)
.increment();
}
}
3.6 参数验证
实现统一的参数验证。
// 自定义验证注解
@Target({ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ValidateParam {
String value() default "";
boolean required() default true;
int maxLength() default Integer.MAX_VALUE;
int minLength() default 0;
String pattern() default "";
}
@Aspect
@Component
@Slf4j
public class ValidationAspect {
@Before("@annotation(validateParam) || execution(* *(.., @ValidateParam (*), ..))")
public void validateParameters(JoinPoint joinPoint) {
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
Object[] args = joinPoint.getArgs();
Parameter[] parameters = method.getParameters();
for (int i = 0; i < parameters.length; i++) {
ValidateParam validateParam = parameters[i].getAnnotation(ValidateParam.class);
if (validateParam != null) {
validateParameter(parameters[i].getName(), args[i], validateParam);
}
}
}
private void validateParameter(String paramName, Object value, ValidateParam validateParam) {
// 必填验证
if (validateParam.required() && value == null) {
throw new IllegalArgumentException("参数 " + paramName + " 不能为空");
}
if (value != null && value instanceof String) {
String strValue = (String) value;
// 长度验证
if (strValue.length() < validateParam.minLength()) {
throw new IllegalArgumentException("参数 " + paramName + " 长度不能少于 " + validateParam.minLength());
}
if (strValue.length() > validateParam.maxLength()) {
throw new IllegalArgumentException("参数 " + paramName + " 长度不能超过 " + validateParam.maxLength());
}
// 正则验证
if (StringUtils.isNotBlank(validateParam.pattern()) && !strValue.matches(validateParam.pattern())) {
throw new IllegalArgumentException("参数 " + paramName + " 格式不正确");
}
}
log.debug("参数验证通过: {} = {}", paramName, value);
}
}
// 使用示例
@RestController
@RequestMapping("/api")
public class ApiController {
@PostMapping("/users")
public User createUser(@ValidateParam(minLength = 2, maxLength = 50) String name,
@ValidateParam(pattern = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$") String email) {
return userService.createUser(name, email);
}
}
3.7 审计日志
记录敏感操作的审计信息。
// 审计注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Audit {
String operation() default "";
String module() default "";
boolean logParams() default true;
boolean logResult() default false;
}
@Aspect
@Component
@Slf4j
public class AuditAspect {
@Autowired
private AuditLogService auditLogService;
@Around("@annotation(audit)")
public Object auditOperation(ProceedingJoinPoint joinPoint, Audit audit) throws Throwable {
String currentUser = getCurrentUser();
String operation = StringUtils.isNotBlank(audit.operation()) ? audit.operation() : joinPoint.getSignature().getName();
String module = audit.module();
AuditLog auditLog = AuditLog.builder()
.userId(currentUser)
.operation(operation)
.module(module)
.className(joinPoint.getTarget().getClass().getSimpleName())
.methodName(joinPoint.getSignature().getName())
.timestamp(LocalDateTime.now())
.build();
if (audit.logParams()) {
auditLog.setParameters(JSON.toJSONString(joinPoint.getArgs()));
}
try {
Object result = joinPoint.proceed();
auditLog.setStatus("SUCCESS");
if (audit.logResult()) {
auditLog.setResult(JSON.toJSONString(result));
}
log.info("审计日志 - 用户: {}, 操作: {}, 模块: {}, 状态: SUCCESS", currentUser, operation, module);
return result;
} catch (Exception e) {
auditLog.setStatus("FAILURE");
auditLog.setErrorMessage(e.getMessage());
log.error("审计日志 - 用户: {}, 操作: {}, 模块: {}, 状态: FAILURE, 错误: {}",
currentUser, operation, module, e.getMessage());
throw e;
} finally {
auditLogService.save(auditLog);
}
}
private String getCurrentUser() {
// 从SecurityContext获取当前用户
return "currentUser";
}
}
// 使用示例
@Service
public class UserService {
@Audit(operation = "删除用户", module = "用户管理", logParams = true)
public void deleteUser(Long userId) {
userRepository.deleteById(userId);
}
@Audit(operation = "更新用户权限", module = "权限管理", logParams = true, logResult = true)
public User updateUserPermissions(Long userId, List<String> permissions) {
User user = userRepository.findById(userId);
user.setPermissions(permissions);
return userRepository.save(user);
}
}
3.8 限流控制
实现方法级别的限流。
// 限流注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimit {
int count() default 10;
int period() default 60;
String key() default "";
String message() default "请求过于频繁,请稍后再试";
}
@Aspect
@Component
@Slf4j
public class RateLimitAspect {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Before("@annotation(rateLimit)")
public void checkRateLimit(JoinPoint joinPoint, RateLimit rateLimit) {
String key = generateKey(joinPoint, rateLimit.key());
int count = rateLimit.count();
int period = rateLimit.period();
// 获取当前计数
String countKey = "rate_limit:" + key;
Integer currentCount = (Integer) redisTemplate.opsForValue().get(countKey);
if (currentCount == null) {
// 第一次请求
redisTemplate.opsForValue().set(countKey, 1, period, TimeUnit.SECONDS);
log.debug("限流检查 - 第一次请求: {}", key);
} else if (currentCount < count) {
// 未达到限制
redisTemplate.opsForValue().increment(countKey);
log.debug("限流检查 - 当前请求数: {}/{}, key: {}", currentCount + 1, count, key);
} else {
// 达到限制
log.warn("限流触发 - 请求数: {}/{}, key: {}", currentCount, count, key);
throw new RateLimitExceededException(rateLimit.message());
}
}
private String generateKey(JoinPoint joinPoint, String keyExpression) {
if (StringUtils.isNotBlank(keyExpression)) {
return keyExpression;
}
// 默认key生成策略:IP + 类名 + 方法名
String ip = getClientIp();
String className = joinPoint.getTarget().getClass().getSimpleName();
String methodName = joinPoint.getSignature().getName();
return ip + ":" + className + ":" + methodName;
}
private String getClientIp() {
// 获取客户端IP
return "127.0.0.1";
}
}
// 使用示例
@RestController
@RequestMapping("/api")
public class ApiController {
@RateLimit(count = 5, period = 60, message = "获取用户信息过于频繁")
@GetMapping("/user/{id}")
public User getUser(@PathVariable Long id) {
return userService.getUserById(id);
}
@RateLimit(count = 3, period = 300, key = "send_sms:${phone}")
@PostMapping("/send-sms")
public void sendSms(@RequestParam String phone) {
smsService.sendSms(phone);
}
}
3.9 数据脱敏
对敏感数据进行脱敏处理。
// 脱敏注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataMask {
String[] fields() default {};
MaskType type() default MaskType.CUSTOM;
}
public enum MaskType {
PHONE, // 手机号脱敏
EMAIL, // 邮箱脱敏
ID_CARD, // 身份证脱敏
BANK_CARD, // 银行卡脱敏
CUSTOM // 自定义脱敏
}
@Aspect
@Component
@Slf4j
public class DataMaskAspect {
@AfterReturning(value = "@annotation(dataMask)", returning = "result")
public Object maskSensitiveData(JoinPoint joinPoint, DataMask dataMask, Object result) {
if (result == null) {
return result;
}
try {
// 深拷贝对象以避免修改原对象
Object maskedResult = deepCopy(result);
// 执行脱敏
maskFields(maskedResult, dataMask.fields(), dataMask.type());
log.debug("数据脱敏完成: {}", joinPoint.getSignature().getName());
return maskedResult;
} catch (Exception e) {
log.error("数据脱敏失败: {}", e.getMessage(), e);
return result;
}
}
private void maskFields(Object obj, String[] fields, MaskType type) throws Exception {
if (obj == null) return;
Class<?> clazz = obj.getClass();
// 处理集合类型
if (obj instanceof Collection) {
Collection<?> collection = (Collection<?>) obj;
for (Object item : collection) {
maskFields(item, fields, type);
}
return;
}
// 处理普通对象
for (String fieldName : fields) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
Object value = field.get(obj);
if (value instanceof String) {
String maskedValue = maskString((String) value, type);
field.set(obj, maskedValue);
}
} catch (NoSuchFieldException e) {
log.warn("字段不存在: {}", fieldName);
}
}
}
private String maskString(String value, MaskType type) {
if (StringUtils.isBlank(value)) {
return value;
}
switch (type) {
case PHONE:
return maskPhone(value);
case EMAIL:
return maskEmail(value);
case ID_CARD:
return maskIdCard(value);
case BANK_CARD:
return maskBankCard(value);
default:
return maskDefault(value);
}
}
private String maskPhone(String phone) {
if (phone.length() >= 11) {
return phone.substring(0, 3) + "****" + phone.substring(7);
}
return phone;
}
private String maskEmail(String email) {
int atIndex = email.indexOf('@');
if (atIndex > 0) {
String username = email.substring(0, atIndex);
String domain = email.substring(atIndex);
String maskedUsername = username.length() > 2 ?
username.substring(0, 2) + "****" : username;
return maskedUsername + domain;
}
return email;
}
private String maskIdCard(String idCard) {
if (idCard.length() >= 18) {
return idCard.substring(0, 6) + "********" + idCard.substring(14);
}
return idCard;
}
private String maskBankCard(String bankCard) {
if (bankCard.length() >= 16) {
return bankCard.substring(0, 4) + "********" + bankCard.substring(bankCard.length() - 4);
}
return bankCard;
}
private String maskDefault(String value) {
if (value.length() <= 2) {
return "*".repeat(value.length());
}
return value.substring(0, 1) + "*".repeat(value.length() - 2) + value.substring(value.length() - 1);
}
private Object deepCopy(Object obj) {
// 使用JSON序列化进行深拷贝
return JSON.parseObject(JSON.toJSONString(obj), obj.getClass());
}
}
// 使用示例
@RestController
@RequestMapping("/api")
public class UserController {
@DataMask(fields = {"phone", "email"}, type = MaskType.PHONE)
@GetMapping("/user/{id}")
public User getUser(@PathVariable Long id) {
return userService.getUserById(id);
}
@DataMask(fields = {"phone"}, type = MaskType.PHONE)
@GetMapping("/users")
public List<User> getUsers() {
return userService.getAllUsers();
}
}
3.10 重试机制
实现方法级别的重试机制。
// 重试注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Retry {
int times() default 3;
long delay() default 1000;
Class<? extends Exception>[] retryFor() default {Exception.class};
Class<? extends Exception>[] noRetryFor() default {};
}
@Aspect
@Component
@Slf4j
public class RetryAspect {
@Around("@annotation(retry)")
public Object retryOperation(ProceedingJoinPoint joinPoint, Retry retry) throws Throwable {
int maxAttempts = retry.times();
long delay = retry.delay();
Class<? extends Exception>[] retryFor = retry.retryFor();
Class<? extends Exception>[] noRetryFor = retry.noRetryFor();
String methodName = joinPoint.getSignature().getName();
for (int attempt = 1; attempt <= maxAttempts; attempt++) {
try {
Object result = joinPoint.proceed();
if (attempt > 1) {
log.info("方法 {} 在第 {} 次尝试后成功执行", methodName, attempt);
}
return result;
} catch (Exception e) {
// 检查是否需要重试
if (!shouldRetry(e, retryFor, noRetryFor)) {
log.error("方法 {} 抛出不可重试异常: {}", methodName, e.getMessage());
throw e;
}
if (attempt == maxAttempts) {
log.error("方法 {} 在 {} 次尝试后仍然失败: {}", methodName, maxAttempts, e.getMessage());
throw e;
}
log.warn("方法 {} 第 {} 次尝试失败,{}ms后重试: {}", methodName, attempt, delay, e.getMessage());
try {
Thread.sleep(delay);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new RuntimeException("重试被中断", ie);
}
}
}
throw new RuntimeException("重试机制异常");
}
private boolean shouldRetry(Exception e, Class<? extends Exception>[] retryFor, Class<? extends Exception>[] noRetryFor) {
// 检查不重试的异常
for (Class<? extends Exception> noRetryException : noRetryFor) {
if (noRetryException.isAssignableFrom(e.getClass())) {
return false;
}
}
// 检查重试的异常
for (Class<? extends Exception> retryException : retryFor) {
if (retryException.isAssignableFrom(e.getClass())) {
return true;
}
}
return false;
}
}
// 使用示例
@Service
public class ExternalService {
@Retry(times = 3, delay = 2000, retryFor = {ConnectException.class, SocketTimeoutException.class})
public String callExternalApi(String url) {
// 模拟外部API调用
RestTemplate restTemplate = new RestTemplate();
return restTemplate.getForObject(url, String.class);
}
@Retry(times = 5, delay = 1000, noRetryFor = {IllegalArgumentException.class})
public void processData(String data) {
if (StringUtils.isBlank(data)) {
throw new IllegalArgumentException("数据不能为空");
}
// 模拟可能失败的数据处理
if (Math.random() < 0.3) {
throw new RuntimeException("数据处理失败");
}
}
}
4. 最佳实践
4.1 切点表达式优化
@Component
@Aspect
public class OptimizedAspect {
// 使用命名切点,提高可读性和复用性
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayer() {}
@Pointcut("execution(* com.example.controller.*.*(..))")
public void controllerLayer() {}
@Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
public void transactionalMethod() {}
// 组合切点
@Pointcut("serviceLayer() || controllerLayer()")
public void applicationLayer() {}
// 排除某些方法
@Pointcut("applicationLayer() && !execution(* com.example.service.LogService.*(..))")
public void nonLoggingService() {}
}
4.2 性能考虑
@Aspect
@Component
public class PerformanceOptimizedAspect {
// 使用条件判断避免不必要的处理
@Around("@annotation(com.example.annotation.Monitor)")
public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
// 只在开发环境或特定条件下执行监控
if (!shouldMonitor()) {
return joinPoint.proceed();
}
// 执行监控逻辑
return doMonitoring(joinPoint);
}
private boolean shouldMonitor() {
// 检查环境变量或配置
return "dev".equals(System.getProperty("spring.profiles.active"));
}
private Object doMonitoring(ProceedingJoinPoint joinPoint) throws Throwable {
// 监控实现
return joinPoint.proceed();
}
}
4.3 异常处理
@Aspect
@Component
@Slf4j
public class RobustAspect {
@Around("@annotation(com.example.annotation.SafeExecute)")
public Object safeExecute(ProceedingJoinPoint joinPoint) throws Throwable {
try {
return joinPoint.proceed();
} catch (Exception e) {
// 记录异常但不影响主流程
log.error("切面执行异常: {}", e.getMessage(), e);
// 根据需要决定是否重新抛出异常
if (e instanceof RuntimeException) {
throw e;
}
// 返回默认值或继续执行
return getDefaultResult(joinPoint);
}
}
private Object getDefaultResult(ProceedingJoinPoint joinPoint) {
// 根据返回类型返回默认值
Class<?> returnType = ((MethodSignature) joinPoint.getSignature()).getReturnType();
if (returnType == void.class) {
return null;
} else if (returnType == boolean.class) {
return false;
} else if (returnType.isPrimitive()) {
return 0;
} else if (Collection.class.isAssignableFrom(returnType)) {
return Collections.emptyList();
}
return null;
}
}
5. 常见问题和解决方案
5.1 AOP不生效
问题: AOP切面没有被执行
解决方案:
- 确保添加了
@EnableAspectJAutoProxy注解 - 检查切点表达式是否正确
- 确保目标类被Spring管理(添加@Component等注解)
- 避免在同一个类中的方法调用(self-invocation问题)
5.2 性能问题
问题: AOP导致性能下降
解决方案:
- 优化切点表达式,避免过宽的匹配
- 在切面中避免复杂的逻辑
- 使用条件判断避免不必要的处理
- 考虑使用异步处理非关键功能
5.3 代理问题
问题: 内部方法调用时AOP不生效
解决方案:
@Service
public class UserService {
@Autowired
private UserService self; // 注入自己
public void publicMethod() {
// 通过代理调用内部方法
self.internalMethod();
}
@Transactional
public void internalMethod() {
// 事务方法
}
}
6. 总结
Spring AOP是一个强大的编程范式,它能够帮助我们优雅地处理横切关注点,提高代码的可维护性和可读性。通过本文介绍的多个实际应用场景,我们可以看到AOP在日志记录、权限控制、缓存处理、异常处理等方面的强大能力。
在实际使用中,需要注意以下几点:
- 合理使用: 不要滥用AOP,只在确实需要横切关注点时使用
- 性能考虑: 注意AOP可能带来的性能影响
- 调试困难: AOP会增加调试的复杂性,需要良好的日志记录
- 团队协作: 确保团队成员都理解AOP的工作原理
通过合理使用Spring AOP,我们可以构建出更加清晰、可维护的应用程序。