此文是 【Spring 容器详解】-> 【ApplicationContext 做了哪些企业化的增强?】的支节点。
Spring的AOP支持(Aspect-Oriented Programming)是ApplicationContext相对于BeanFactory的重要企业级增强功能。AOP允许开发者将横切关注点(如日志、事务、安全等)从业务逻辑中分离出来,实现更好的代码组织和维护性。
1. BeanFactory的AOP支持限制
1.1 BeanFactory的局限性
BeanFactory作为Spring的核心容器,在AOP支持方面存在以下限制:
- 无内置AOP支持:BeanFactory本身不提供AOP功能
- 缺乏切面管理:无法管理切面(Aspect)的生命周期
- 无AOP注解支持:不支持@Aspect、@Before等AOP注解
- AOP配置复杂:需要手动配置代理工厂和切面
1.2 示例代码
java
// BeanFactory缺乏AOP支持
public class BeanFactoryAopExample {
public static void main(String[] args) {
// 创建BeanFactory
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// BeanFactory本身不提供AOP功能
// 无法使用@Aspect注解
// 无法自动配置AOP代理
// 需要通过其他方式实现横切关注点
// 手动实现日志记录
UserService userService = new UserService();
// 手动添加日志
System.out.println("方法开始执行: getUserById");
try {
User user = userService.getUserById(1L);
System.out.println("方法执行成功: getUserById");
return user;
} catch (Exception e) {
System.out.println("方法执行异常: getUserById, 异常: " + e.getMessage());
throw e;
}
}
}
class UserService {
public User getUserById(Long id) {
// 业务逻辑
return new User(id, "User " + id);
}
}
2. ApplicationContext的AOP支持扩展
2.1 核心注解:@EnableAspectJAutoProxy
ApplicationContext通过@EnableAspectJAutoProxy启用AOP功能:
java
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
@Bean
public LoggingAspect loggingAspect() {
return new LoggingAspect();
}
@Bean
public TransactionAspect transactionAspect() {
return new TransactionAspect();
}
@Bean
public SecurityAspect securityAspect() {
return new SecurityAspect();
}
}
2.2 AOP支持的核心功能
2.2.1 切面(Aspect)管理
Spring自动管理切面的生命周期,包括创建、初始化和销毁:
java
@Aspect
@Component
public class LoggingAspect {
private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
// 前置通知
@Before("execution(* com.example.service.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
String className = joinPoint.getTarget().getClass().getSimpleName();
Object[] args = joinPoint.getArgs();
logger.info("执行方法: {}.{}, 参数: {}", className, methodName, Arrays.toString(args));
}
// 后置通知
@After("execution(* com.example.service.*.*(..))")
public void logAfter(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
String className = joinPoint.getTarget().getClass().getSimpleName();
logger.info("方法执行完成: {}.{}", className, methodName);
}
// 返回通知
@AfterReturning(pointcut = "execution(* com.example.service.*.*(..))", returning = "result")
public void logAfterReturning(JoinPoint joinPoint, Object result) {
String methodName = joinPoint.getSignature().getName();
String className = joinPoint.getTarget().getClass().getSimpleName();
logger.info("方法执行成功: {}.{}, 返回值: {}", className, methodName, result);
}
// 异常通知
@AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", throwing = "error")
public void logAfterThrowing(JoinPoint joinPoint, Throwable error) {
String methodName = joinPoint.getSignature().getName();
String className = joinPoint.getTarget().getClass().getSimpleName();
logger.error("方法执行异常: {}.{}, 异常: {}", className, methodName, error.getMessage());
}
// 环绕通知
@Around("execution(* com.example.service.*.*(..))")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
String methodName = joinPoint.getSignature().getName();
String className = joinPoint.getTarget().getClass().getSimpleName();
long startTime = System.currentTimeMillis();
logger.info("开始执行方法: {}.{}", className, methodName);
try {
Object result = joinPoint.proceed();
long endTime = System.currentTimeMillis();
logger.info("方法执行完成: {}.{}, 耗时: {}ms", className, methodName, (endTime - startTime));
return result;
} catch (Throwable e) {
long endTime = System.currentTimeMillis();
logger.error("方法执行异常: {}.{}, 耗时: {}ms, 异常: {}",
className, methodName, (endTime - startTime), e.getMessage());
throw e;
}
}
}
2.2.2 切点表达式(Pointcut Expression)
Spring支持丰富的切点表达式语法:
java
@Aspect
@Component
public class PointcutExample {
// 1. 方法执行切点
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {}
// 2. 方法参数切点
@Pointcut("execution(* *.*(Long, String))")
public void methodsWithLongAndString() {}
// 3. 注解切点
@Pointcut("@annotation(com.example.annotation.Logged)")
public void loggedMethods() {}
// 4. 类切点
@Pointcut("within(com.example.service.*)")
public void serviceClasses() {}
// 5. 组合切点
@Pointcut("serviceMethods() && loggedMethods()")
public void loggedServiceMethods() {}
// 6. 参数切点
@Pointcut("args(Long)")
public void methodsWithLongParam() {}
// 7. 返回值切点
@Pointcut("execution(* *.*(..)) && !execution(void *.*(..))")
public void methodsWithReturnValue() {}
// 8. 异常切点
@Pointcut("execution(* *.*(..)) throws Exception")
public void methodsThrowingException() {}
// 使用切点
@Before("serviceMethods()")
public void beforeServiceMethod(JoinPoint joinPoint) {
System.out.println("服务方法执行前: " + joinPoint.getSignature().getName());
}
@Before("loggedMethods()")
public void beforeLoggedMethod(JoinPoint joinPoint) {
System.out.println("日志方法执行前: " + joinPoint.getSignature().getName());
}
@Before("loggedServiceMethods()")
public void beforeLoggedServiceMethod(JoinPoint joinPoint) {
System.out.println("日志服务方法执行前: " + joinPoint.getSignature().getName());
}
}
3. AOP通知类型详解
3.1 前置通知(@Before)
java
@Aspect
@Component
public class BeforeAdviceExample {
// 基本前置通知
@Before("execution(* com.example.service.UserService.*(..))")
public void beforeUserServiceMethod(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
System.out.println("用户服务方法执行前: " + methodName);
System.out.println("方法参数: " + Arrays.toString(args));
}
// 带条件的前置通知
@Before("execution(* com.example.service.*.*(..)) && args(id,..)")
public void beforeMethodWithId(Long id, JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
if (id > 0) {
System.out.println("方法 " + methodName + " 执行前,ID: " + id);
} else {
System.out.println("警告:方法 " + methodName + " 使用了无效ID: " + id);
}
}
// 权限检查前置通知
@Before("execution(* com.example.service.*.*(..)) && @annotation(secured)")
public void checkPermission(Secured secured, JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
String[] roles = secured.roles();
// 检查当前用户是否有权限
if (!hasPermission(roles)) {
throw new SecurityException("用户没有权限执行方法: " + methodName);
}
System.out.println("权限检查通过,执行方法: " + methodName);
}
private boolean hasPermission(String[] roles) {
// 实现权限检查逻辑
return true; // 简化实现
}
}
// 安全注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Secured {
String[] roles() default {};
}
3.2 后置通知(@After)
java
@Aspect
@Component
public class AfterAdviceExample {
// 基本后置通知
@After("execution(* com.example.service.*.*(..))")
public void afterMethod(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
String className = joinPoint.getTarget().getClass().getSimpleName();
System.out.println("方法执行完成: " + className + "." + methodName);
}
// 资源清理后置通知
@After("execution(* com.example.service.*.*(..))")
public void cleanupResources(JoinPoint joinPoint) {
// 清理资源
System.out.println("清理方法执行后的资源");
}
// 审计日志后置通知
@After("execution(* com.example.service.*.*(..)) && @annotation(audited)")
public void auditLog(Audited audited, JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
String operation = audited.operation();
System.out.println("审计日志: 操作=" + operation + ", 方法=" + methodName);
}
}
// 审计注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Audited {
String operation() default "";
}
3.3 返回通知(@AfterReturning)
java
@Aspect
@Component
public class AfterReturningAdviceExample {
// 基本返回通知
@AfterReturning(pointcut = "execution(* com.example.service.*.*(..))", returning = "result")
public void afterReturning(JoinPoint joinPoint, Object result) {
String methodName = joinPoint.getSignature().getName();
System.out.println("方法 " + methodName + " 执行成功,返回值: " + result);
}
// 带类型的返回通知
@AfterReturning(pointcut = "execution(* com.example.service.UserService.getUserById(..))",
returning = "user")
public void afterReturningUser(JoinPoint joinPoint, User user) {
if (user != null) {
System.out.println("成功获取用户: " + user.getName());
} else {
System.out.println("用户不存在");
}
}
// 结果验证返回通知
@AfterReturning(pointcut = "execution(* com.example.service.*.*(..))",
returning = "result")
public void validateResult(JoinPoint joinPoint, Object result) {
if (result instanceof Collection) {
Collection<?> collection = (Collection<?>) result;
if (collection.isEmpty()) {
System.out.println("警告:方法返回空集合");
}
}
}
// 缓存更新返回通知
@AfterReturning(pointcut = "execution(* com.example.service.*.*(..))",
returning = "result")
public void updateCache(JoinPoint joinPoint, Object result) {
// 更新缓存
System.out.println("更新缓存,方法: " + joinPoint.getSignature().getName());
}
}
3.4 异常通知(@AfterThrowing)
java
@Aspect
@Component
public class AfterThrowingAdviceExample {
// 基本异常通知
@AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", throwing = "error")
public void afterThrowing(JoinPoint joinPoint, Throwable error) {
String methodName = joinPoint.getSignature().getName();
String className = joinPoint.getTarget().getClass().getSimpleName();
System.err.println("方法执行异常: " + className + "." + methodName);
System.err.println("异常类型: " + error.getClass().getSimpleName());
System.err.println("异常消息: " + error.getMessage());
}
// 特定异常通知
@AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))",
throwing = "sqlException")
public void handleSqlException(JoinPoint joinPoint, SQLException sqlException) {
String methodName = joinPoint.getSignature().getName();
System.err.println("SQL异常: " + methodName + ", 错误代码: " + sqlException.getErrorCode());
// 记录SQL异常到日志
logSqlException(methodName, sqlException);
}
// 异常分类处理
@AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))",
throwing = "error")
public void categorizeException(JoinPoint joinPoint, Throwable error) {
if (error instanceof RuntimeException) {
handleRuntimeException(joinPoint, (RuntimeException) error);
} else if (error instanceof Exception) {
handleCheckedException(joinPoint, (Exception) error);
} else {
handleError(joinPoint, (Error) error);
}
}
private void handleRuntimeException(JoinPoint joinPoint, RuntimeException e) {
System.err.println("运行时异常: " + e.getMessage());
}
private void handleCheckedException(JoinPoint joinPoint, Exception e) {
System.err.println("检查异常: " + e.getMessage());
}
private void handleError(JoinPoint joinPoint, Error e) {
System.err.println("严重错误: " + e.getMessage());
}
private void logSqlException(String methodName, SQLException e) {
// 实现SQL异常日志记录
System.err.println("记录SQL异常: " + methodName + " - " + e.getMessage());
}
}
3.5 环绕通知(@Around)
java
@Aspect
@Component
public class AroundAdviceExample {
// 性能监控环绕通知
@Around("execution(* com.example.service.*.*(..))")
public Object performanceMonitor(ProceedingJoinPoint joinPoint) throws Throwable {
String methodName = joinPoint.getSignature().getName();
String className = joinPoint.getTarget().getClass().getSimpleName();
long startTime = System.currentTimeMillis();
System.out.println("开始执行方法: " + className + "." + methodName);
try {
Object result = joinPoint.proceed();
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
System.out.println("方法执行成功: " + className + "." + methodName + ", 耗时: " + duration + "ms");
// 记录性能指标
if (duration > 1000) {
System.out.println("警告:方法执行时间过长: " + duration + "ms");
}
return result;
} catch (Throwable e) {
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
System.err.println("方法执行异常: " + className + "." + methodName + ", 耗时: " + duration + "ms");
System.err.println("异常: " + e.getMessage());
throw e;
}
}
// 重试机制环绕通知
@Around("execution(* com.example.service.*.*(..)) && @annotation(retryable)")
public Object retryOperation(ProceedingJoinPoint joinPoint, Retryable retryable) throws Throwable {
int maxAttempts = retryable.maxAttempts();
long delay = retryable.delay();
for (int attempt = 1; attempt <= maxAttempts; attempt++) {
try {
System.out.println("尝试执行方法,第 " + attempt + " 次");
return joinPoint.proceed();
} catch (Exception e) {
if (attempt == maxAttempts) {
System.err.println("重试次数已达上限,抛出异常");
throw e;
}
System.out.println("执行失败,等待 " + delay + "ms 后重试");
Thread.sleep(delay);
}
}
throw new RuntimeException("重试失败");
}
// 缓存环绕通知
@Around("execution(* com.example.service.*.*(..)) && @annotation(cached)")
public Object cacheOperation(ProceedingJoinPoint joinPoint, Cached cached) throws Throwable {
String cacheKey = generateCacheKey(joinPoint);
// 尝试从缓存获取
Object cachedResult = getFromCache(cacheKey);
if (cachedResult != null) {
System.out.println("从缓存获取结果: " + cacheKey);
return cachedResult;
}
// 执行方法
Object result = joinPoint.proceed();
// 存入缓存
putToCache(cacheKey, result, cached.ttl());
System.out.println("结果已缓存: " + cacheKey);
return result;
}
private String generateCacheKey(ProceedingJoinPoint joinPoint) {
return joinPoint.getSignature().getName() + "_" + Arrays.toString(joinPoint.getArgs());
}
private Object getFromCache(String key) {
// 实现缓存获取逻辑
return null;
}
private void putToCache(String key, Object value, long ttl) {
// 实现缓存存储逻辑
}
}
// 重试注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Retryable {
int maxAttempts() default 3;
long delay() default 1000;
}
// 缓存注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Cached {
long ttl() default 300000; // 5分钟
}
4. 实际应用场景
4.1 事务管理
java
@Aspect
@Component
public class TransactionAspect {
@Autowired
private PlatformTransactionManager transactionManager;
@Around("@annotation(transactional)")
public Object manageTransaction(ProceedingJoinPoint joinPoint, Transactional transactional) throws Throwable {
TransactionStatus status = null;
try {
// 开启事务
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(transactional.propagation().value());
def.setIsolationLevel(transactional.isolation().value());
def.setTimeout(transactional.timeout());
def.setReadOnly(transactional.readOnly());
status = transactionManager.getTransaction(def);
// 执行方法
Object result = joinPoint.proceed();
// 提交事务
transactionManager.commit(status);
System.out.println("事务提交成功");
return result;
} catch (Throwable e) {
// 回滚事务
if (status != null) {
transactionManager.rollback(status);
System.err.println("事务回滚,异常: " + e.getMessage());
}
throw e;
}
}
}
@Service
public class TransactionalUserService {
@Transactional(rollbackFor = Exception.class)
public void createUserWithEmail(String username, String email) {
// 创建用户
createUser(username);
// 发送欢迎邮件
sendWelcomeEmail(email);
}
private void createUser(String username) {
// 创建用户逻辑
}
private void sendWelcomeEmail(String email) {
// 发送邮件逻辑
}
}
4.2 安全控制
java
@Aspect
@Component
public class SecurityAspect {
@Autowired
private SecurityContext securityContext;
@Before("@annotation(secured)")
public void checkSecurity(Secured secured, JoinPoint joinPoint) {
String[] requiredRoles = secured.roles();
String currentUser = getCurrentUser();
String[] userRoles = getUserRoles(currentUser);
if (!hasRequiredRole(userRoles, requiredRoles)) {
throw new SecurityException("用户 " + currentUser + " 没有权限执行此操作");
}
System.out.println("安全检查通过,用户: " + currentUser + ", 角色: " + Arrays.toString(userRoles));
}
@Before("@annotation(rateLimited)")
public void checkRateLimit(RateLimited rateLimited, JoinPoint joinPoint) {
String key = generateRateLimitKey(joinPoint);
int limit = rateLimited.limit();
int window = rateLimited.window();
if (isRateLimitExceeded(key, limit, window)) {
throw new RateLimitExceededException("请求频率超限");
}
System.out.println("频率限制检查通过");
}
private String getCurrentUser() {
// 获取当前用户
return "currentUser";
}
private String[] getUserRoles(String user) {
// 获取用户角色
return new String[]{"USER"};
}
private boolean hasRequiredRole(String[] userRoles, String[] requiredRoles) {
// 检查用户是否有所需角色
return Arrays.stream(requiredRoles)
.anyMatch(required -> Arrays.asList(userRoles).contains(required));
}
private String generateRateLimitKey(ProceedingJoinPoint joinPoint) {
return joinPoint.getSignature().getName() + "_" + getCurrentUser();
}
private boolean isRateLimitExceeded(String key, int limit, int window) {
// 实现频率限制检查
return false;
}
}
// 安全注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Secured {
String[] roles();
}
// 频率限制注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimited {
int limit() default 100;
int window() default 3600; // 1小时
}
class RateLimitExceededException extends RuntimeException {
public RateLimitExceededException(String message) {
super(message);
}
}
4.3 日志记录
java
@Aspect
@Component
public class LoggingAspect {
private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
@Around("@annotation(logged)")
public Object logMethod(ProceedingJoinPoint joinPoint, Logged logged) throws Throwable {
String methodName = joinPoint.getSignature().getName();
String className = joinPoint.getTarget().getClass().getSimpleName();
Object[] args = joinPoint.getArgs();
// 记录方法调用
logger.info("调用方法: {}.{}, 参数: {}", className, methodName, Arrays.toString(args));
long startTime = System.currentTimeMillis();
try {
Object result = joinPoint.proceed();
long duration = System.currentTimeMillis() - startTime;
// 记录方法成功
logger.info("方法执行成功: {}.{}, 耗时: {}ms, 返回值: {}",
className, methodName, duration, result);
return result;
} catch (Throwable e) {
long duration = System.currentTimeMillis() - startTime;
// 记录方法异常
logger.error("方法执行异常: {}.{}, 耗时: {}ms, 异常: {}",
className, methodName, duration, e.getMessage(), e);
throw e;
}
}
@AfterThrowing(pointcut = "@annotation(logged)", throwing = "error")
public void logException(JoinPoint joinPoint, Logged logged, Throwable error) {
String methodName = joinPoint.getSignature().getName();
String className = joinPoint.getTarget().getClass().getSimpleName();
// 记录异常详情
logger.error("方法异常详情: {}.{}, 异常类型: {}, 异常消息: {}",
className, methodName, error.getClass().getSimpleName(), error.getMessage());
// 记录异常堆栈
logger.error("异常堆栈:", error);
}
}
// 日志注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Logged {
String value() default "";
boolean logArgs() default true;
boolean logResult() default true;
boolean logException() default true;
}
@Service
public class LoggedUserService {
@Logged("获取用户信息")
public User getUserById(Long id) {
return new User(id, "User " + id);
}
@Logged("创建用户")
public User createUser(String name) {
return new User(System.currentTimeMillis(), name);
}
}
4.4 性能监控
java
@Aspect
@Component
public class PerformanceMonitorAspect {
private final Map<String, MethodStats> methodStats = new ConcurrentHashMap<>();
@Around("@annotation(monitored)")
public Object monitorPerformance(ProceedingJoinPoint joinPoint, Monitored monitored) throws Throwable {
String methodName = joinPoint.getSignature().getName();
String className = joinPoint.getTarget().getClass().getSimpleName();
String key = className + "." + methodName;
long startTime = System.nanoTime();
long startMemory = getCurrentMemoryUsage();
try {
Object result = joinPoint.proceed();
long duration = System.nanoTime() - startTime;
long memoryUsed = getCurrentMemoryUsage() - startMemory;
// 更新统计信息
updateStats(key, duration, memoryUsed, true);
// 检查性能阈值
checkPerformanceThreshold(key, duration, memoryUsed, monitored);
return result;
} catch (Throwable e) {
long duration = System.nanoTime() - startTime;
long memoryUsed = getCurrentMemoryUsage() - startMemory;
// 更新统计信息
updateStats(key, duration, memoryUsed, false);
throw e;
}
}
private void updateStats(String key, long duration, long memoryUsed, boolean success) {
methodStats.compute(key, (k, stats) -> {
if (stats == null) {
stats = new MethodStats();
}
stats.incrementCallCount();
stats.addDuration(duration);
stats.addMemoryUsage(memoryUsed);
if (success) {
stats.incrementSuccessCount();
} else {
stats.incrementFailureCount();
}
return stats;
});
}
private void checkPerformanceThreshold(String key, long duration, long memoryUsed, Monitored monitored) {
long durationMs = duration / 1_000_000; // 转换为毫秒
if (durationMs > monitored.durationThreshold()) {
System.err.println("性能警告: 方法 " + key + " 执行时间 " + durationMs + "ms 超过阈值 " + monitored.durationThreshold() + "ms");
}
if (memoryUsed > monitored.memoryThreshold()) {
System.err.println("内存警告: 方法 " + key + " 内存使用 " + memoryUsed + " bytes 超过阈值 " + monitored.memoryThreshold() + " bytes");
}
}
private long getCurrentMemoryUsage() {
Runtime runtime = Runtime.getRuntime();
return runtime.totalMemory() - runtime.freeMemory();
}
// 获取性能统计信息
public Map<String, MethodStats> getMethodStats() {
return new HashMap<>(methodStats);
}
// 重置统计信息
public void resetStats() {
methodStats.clear();
}
// 方法统计信息类
public static class MethodStats {
private long callCount = 0;
private long successCount = 0;
private long failureCount = 0;
private long totalDuration = 0;
private long totalMemoryUsage = 0;
private long minDuration = Long.MAX_VALUE;
private long maxDuration = 0;
public void incrementCallCount() { callCount++; }
public void incrementSuccessCount() { successCount++; }
public void incrementFailureCount() { failureCount++; }
public void addDuration(long duration) {
totalDuration += duration;
minDuration = Math.min(minDuration, duration);
maxDuration = Math.max(maxDuration, duration);
}
public void addMemoryUsage(long memoryUsage) {
totalMemoryUsage += memoryUsage;
}
// getter方法
public long getCallCount() { return callCount; }
public long getSuccessCount() { return successCount; }
public long getFailureCount() { return failureCount; }
public double getAverageDuration() { return callCount > 0 ? (double) totalDuration / callCount : 0; }
public long getMinDuration() { return minDuration == Long.MAX_VALUE ? 0 : minDuration; }
public long getMaxDuration() { return maxDuration; }
public double getAverageMemoryUsage() { return callCount > 0 ? (double) totalMemoryUsage / callCount : 0; }
public double getSuccessRate() { return callCount > 0 ? (double) successCount / callCount : 0; }
}
}
// 性能监控注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Monitored {
long durationThreshold() default 1000; // 1秒
long memoryThreshold() default 1024 * 1024; // 1MB
}
@Service
public class MonitoredUserService {
@Monitored(durationThreshold = 500, memoryThreshold = 512 * 1024)
public User getUserById(Long id) {
// 模拟耗时操作
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return new User(id, "User " + id);
}
}
5. 高级特性
5.1 自定义切点
java
@Aspect
@Component
public class CustomPointcutAspect {
// 自定义切点:业务方法
@Pointcut("execution(* com.example.business.*.*(..))")
public void businessMethods() {}
// 自定义切点:数据访问方法
@Pointcut("execution(* com.example.repository.*.*(..))")
public void dataAccessMethods() {}
// 自定义切点:服务方法
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {}
// 自定义切点:控制器方法
@Pointcut("execution(* com.example.controller.*.*(..))")
public void controllerMethods() {}
// 组合切点:业务逻辑层
@Pointcut("businessMethods() || serviceMethods()")
public void businessLayer() {}
// 组合切点:数据访问层
@Pointcut("dataAccessMethods()")
public void dataLayer() {}
// 组合切点:表现层
@Pointcut("controllerMethods()")
public void presentationLayer() {}
// 使用自定义切点
@Before("businessLayer()")
public void beforeBusinessMethod(JoinPoint joinPoint) {
System.out.println("业务方法执行前: " + joinPoint.getSignature().getName());
}
@After("dataLayer()")
public void afterDataAccessMethod(JoinPoint joinPoint) {
System.out.println("数据访问方法执行后: " + joinPoint.getSignature().getName());
}
@Around("presentationLayer()")
public Object aroundControllerMethod(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("控制器方法执行前");
Object result = joinPoint.proceed();
System.out.println("控制器方法执行后");
return result;
}
}
5.2 切面优先级
java
@Aspect
@Component
@Order(1) // 最高优先级
public class HighPriorityAspect {
@Before("execution(* com.example.service.*.*(..))")
public void highPriorityAdvice(JoinPoint joinPoint) {
System.out.println("高优先级切面执行: " + joinPoint.getSignature().getName());
}
}
@Aspect
@Component
@Order(2) // 中等优先级
public class MediumPriorityAspect {
@Before("execution(* com.example.service.*.*(..))")
public void mediumPriorityAdvice(JoinPoint joinPoint) {
System.out.println("中等优先级切面执行: " + joinPoint.getSignature().getName());
}
}
@Aspect
@Component
@Order(3) // 最低优先级
public class LowPriorityAspect {
@Before("execution(* com.example.service.*.*(..))")
public void lowPriorityAdvice(JoinPoint joinPoint) {
System.out.println("低优先级切面执行: " + joinPoint.getSignature().getName());
}
}
5.3 切面组合
java
@Aspect
@Component
public class CompositeAspect {
// 组合多个切点
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {}
@Pointcut("execution(* com.example.repository.*.*(..))")
public void repositoryMethods() {}
@Pointcut("serviceMethods() || repositoryMethods()")
public void serviceOrRepositoryMethods() {}
// 组合多个通知
@Around("serviceOrRepositoryMethods()")
public Object compositeAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
// 前置处理
System.out.println("组合切面前置处理");
// 执行方法
Object result = joinPoint.proceed();
// 后置处理
System.out.println("组合切面后置处理");
return result;
}
// 使用@Caching组合多个缓存操作
@Caching(
cacheable = {
@Cacheable(value = "users", key = "#id")
},
put = {
@CachePut(value = "userNames", key = "#result.name")
},
evict = {
@CacheEvict(value = "userList", allEntries = true)
}
)
public User getUserById(Long id) {
// 获取用户逻辑
return new User(id, "User " + id);
}
}
6. 最佳实践
6.1 切面设计原则
java
@Aspect
@Component
public class WellDesignedAspect {
// 1. 单一职责原则:每个切面只负责一个横切关注点
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {}
// 2. 明确的切点表达式:避免过于宽泛的切点
@Before("serviceMethods() && !execution(* *.get*(..))") // 排除getter方法
public void beforeServiceMethod(JoinPoint joinPoint) {
// 只处理非getter的服务方法
}
// 3. 合理的通知类型选择
@Around("serviceMethods()") // 需要控制方法执行流程时使用@Around
public Object aroundServiceMethod(ProceedingJoinPoint joinPoint) throws Throwable {
// 复杂的横切逻辑
return joinPoint.proceed();
}
@Before("serviceMethods()") // 简单的前置处理使用@Before
public void beforeServiceMethodSimple(JoinPoint joinPoint) {
// 简单的前置逻辑
}
// 4. 异常处理
@AfterThrowing(pointcut = "serviceMethods()", throwing = "error")
public void handleServiceException(JoinPoint joinPoint, Throwable error) {
// 统一的异常处理逻辑
logException(joinPoint, error);
notifyAdministrator(joinPoint, error);
}
private void logException(JoinPoint joinPoint, Throwable error) {
// 记录异常日志
}
private void notifyAdministrator(JoinPoint joinPoint, Throwable error) {
// 通知管理员
}
}
6.2 性能优化
java
@Aspect
@Component
public class OptimizedAspect {
// 1. 缓存切点表达式编译结果
private final Pointcut serviceMethods =
new AspectJExpressionPointcut("execution(* com.example.service.*.*(..))");
// 2. 使用条件表达式避免不必要的通知执行
@Before("serviceMethods && args(id,..)")
public void beforeServiceMethodWithId(Long id, JoinPoint joinPoint) {
if (id > 0) { // 条件检查
// 只处理有效的ID
processValidId(id);
}
}
// 3. 异步处理非关键通知
@Async
@AfterReturning("serviceMethods")
public void asyncAfterReturning(JoinPoint joinPoint, Object result) {
// 异步处理,不阻塞主流程
processResultAsync(result);
}
// 4. 批量处理
@AfterReturning("serviceMethods")
public void batchProcess(JoinPoint joinPoint, Object result) {
// 批量处理结果
batchProcessor.add(result);
if (batchProcessor.isFull()) {
batchProcessor.process();
}
}
private void processValidId(Long id) {
// 处理有效ID的逻辑
}
private void processResultAsync(Object result) {
// 异步处理结果
}
}
@Component
public class BatchProcessor {
private final List<Object> items = new ArrayList<>();
private static final int BATCH_SIZE = 100;
public void add(Object item) {
items.add(item);
}
public boolean isFull() {
return items.size() >= BATCH_SIZE;
}
public void process() {
// 批量处理逻辑
System.out.println("批量处理 " + items.size() + " 个项目");
items.clear();
}
}
6.3 测试策略
java
@SpringBootTest
class AopTest {
@Autowired
private UserService userService;
@Autowired
private ApplicationContext applicationContext;
@Test
void testAopFunctionality() {
// 测试AOP功能
User user = userService.getUserById(1L);
assertNotNull(user);
// 验证切面是否被正确应用
// 可以通过日志输出或其他方式验证
}
@Test
void testAspectOrder() {
// 测试切面执行顺序
userService.getUserById(1L);
// 验证切面按正确顺序执行
}
@Test
void testPointcutExpression() {
// 测试切点表达式
userService.getUserById(1L);
userService.createUser("Test User");
// 验证切点表达式是否正确匹配
}
}
// 测试用的切面
@Aspect
@Component
@TestConfiguration
public class TestAspect {
private final List<String> executedAdvice = new ArrayList<>();
@Before("execution(* com.example.service.*.*(..))")
public void testAdvice(JoinPoint joinPoint) {
executedAdvice.add(joinPoint.getSignature().getName());
}
public List<String> getExecutedAdvice() {
return new ArrayList<>(executedAdvice);
}
public void clear() {
executedAdvice.clear();
}
}
总结
ApplicationContext在AOP支持方面相比BeanFactory提供了以下重要扩展,其核心特性:
- 切面(Aspect):横切关注点的模块化
- 切点(Pointcut):定义在哪些连接点应用通知
- 通知(Advice):在切点处要执行的代码
- 连接点(Join Point):程序执行过程中的某个特定点
- 目标对象(Target Object):被代理的对象
这些特性使得Spring应用能够优雅地处理横切关注点,实现更好的代码组织和维护性,是Spring框架企业级特性的重要组成部分。