👋 大家好,我是 阿问学长!专注于分享优质开源项目解析、毕业设计项目指导支持、幼小初高教辅资料推荐等,欢迎关注交流!🚀

Spring AOP切面编程

前言

Spring AOP(面向切面编程)是Spring框架的核心特性之一,它提供了一种优雅的方式来处理横切关注点,如日志记录、事务管理、安全检查、性能监控等。RuoYi-Vue-Plus框架广泛使用了AOP技术来实现各种功能。本文将详细介绍Spring AOP的切面定义与配置、通知类型详解、切点表达式语法以及事务管理实现等内容。

切面定义与配置

基础切面配置

/**
 * 日志切面
 */
@Aspect
@Component
@Slf4j
public class LogAspect {
    
    /**
     * 配置切入点
     */
    @Pointcut("@annotation(com.ruoyi.common.annotation.Log)")
    public void logPointCut() {
        // 切入点方法体为空,仅用于定义切入点
    }
    
    /**
     * 处理完请求后执行
     */
    @AfterReturning(pointcut = "logPointCut()", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) {
        handleLog(joinPoint, null, jsonResult);
    }
    
    /**
     * 拦截异常操作
     */
    @AfterThrowing(value = "logPointCut()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
        handleLog(joinPoint, e, null);
    }
    
    /**
     * 处理日志记录
     */
    protected void handleLog(final JoinPoint joinPoint, final Exception e, Object jsonResult) {
        try {
            // 获取注解
            Log controllerLog = getAnnotationLog(joinPoint);
            if (controllerLog == null) {
                return;
            }
            
            // 获取当前用户
            LoginUser loginUser = getLoginUser();
            
            // 构建操作日志对象
            SysOperLog operLog = new SysOperLog();
            operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
            
            // 请求的地址
            String ip = IpUtils.getIpAddr();
            operLog.setOperIp(ip);
            operLog.setOperLocation(AddressUtils.getRealAddressByIP(ip));
            
            operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255));
            
            if (loginUser != null) {
                operLog.setOperName(loginUser.getUsername());
            }
            
            if (e != null) {
                operLog.setStatus(BusinessStatus.FAIL.ordinal());
                operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
            }
            
            // 设置方法名称
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            operLog.setMethod(className + "." + methodName + "()");
            
            // 设置请求方式
            operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
            
            // 处理设置注解上的参数
            getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);
            
            // 保存数据库
            AsyncManager.me().execute(AsyncFactory.recordOper(operLog));
            
        } catch (Exception exp) {
            // 记录本地异常日志
            log.error("异常信息:{}", exp.getMessage());
            exp.printStackTrace();
        }
    }
    
    /**
     * 获取注解中对方法的描述信息
     */
    public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog, Object jsonResult) throws Exception {
        // 设置action动作
        operLog.setBusinessType(log.businessType().ordinal());
        // 设置标题
        operLog.setTitle(log.title());
        // 设置操作人类别
        operLog.setOperatorType(log.operatorType().ordinal());
        
        // 是否需要保存request,参数和值
        if (log.isSaveRequestData()) {
            // 获取参数的信息,传入到数据库中。
            setRequestValue(joinPoint, operLog, log.excludeParamNames());
        }
        
        // 是否需要保存response,参数和值
        if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult)) {
            operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000));
        }
    }
    
    /**
     * 获取请求的参数,放到log中
     */
    private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog, String[] excludeParamNames) throws Exception {
        Map<?, ?> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest());
        String requestMethod = operLog.getRequestMethod();
        
        if (StringUtils.isEmpty(paramsMap) 
            && (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod))) {
            String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames);
            operLog.setOperParam(StringUtils.substring(params, 0, 2000));
        } else {
            operLog.setOperParam(StringUtils.substring(JSON.toJSONString(paramsMap, excludePropertyPreFilter(excludeParamNames)), 0, 2000));
        }
    }
    
    /**
     * 是否存在注解,如果存在就获取
     */
    private Log getAnnotationLog(JoinPoint joinPoint) throws Exception {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        
        if (method != null) {
            return method.getAnnotation(Log.class);
        }
        return null;
    }
    
    /**
     * 参数拼装
     */
    private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames) {
        String params = "";
        if (paramsArray != null && paramsArray.length > 0) {
            for (Object o : paramsArray) {
                if (StringUtils.isNotNull(o) && !isFilterObject(o)) {
                    try {
                        String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter(excludeParamNames));
                        params += jsonObj.toString() + " ";
                    } catch (Exception e) {
                        // 忽略序列化异常
                    }
                }
            }
        }
        return params.trim();
    }
    
    /**
     * 忽略敏感属性
     */
    public PropertyPreExcludeFilter excludePropertyPreFilter(String[] excludeParamNames) {
        return new PropertyPreExcludeFilter().addExcludes(excludeParamNames);
    }
    
    /**
     * 判断是否需要过滤的对象
     */
    @SuppressWarnings("rawtypes")
    public boolean isFilterObject(final Object o) {
        Class<?> clazz = o.getClass();
        if (clazz.isArray()) {
            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
        } else if (Collection.class.isAssignableFrom(clazz)) {
            Collection collection = (Collection) o;
            for (Object value : collection) {
                return value instanceof MultipartFile;
            }
        } else if (Map.class.isAssignableFrom(clazz)) {
            Map map = (Map) o;
            for (Object value : map.entrySet()) {
                Map.Entry entry = (Map.Entry) value;
                return entry.getValue() instanceof MultipartFile;
            }
        }
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
                || o instanceof BindingResult;
    }
    
    /**
     * 获取当前登录用户
     */
    private LoginUser getLoginUser() {
        try {
            return LoginHelper.getLoginUser();
        } catch (Exception e) {
            return null;
        }
    }
}

/**
 * 数据权限切面
 */
@Aspect
@Component
@Slf4j
public class DataScopeAspect {
    
    /**
     * 全部数据权限
     */
    public static final String DATA_SCOPE_ALL = "1";
    
    /**
     * 自定数据权限
     */
    public static final String DATA_SCOPE_CUSTOM = "2";
    
    /**
     * 部门数据权限
     */
    public static final String DATA_SCOPE_DEPT = "3";
    
    /**
     * 部门及以下数据权限
     */
    public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";
    
    /**
     * 仅本人数据权限
     */
    public static final String DATA_SCOPE_SELF = "5";
    
    /**
     * 数据权限过滤关键字
     */
    public static final String DATA_SCOPE = "dataScope";
    
    @Pointcut("@annotation(com.ruoyi.common.annotation.DataScope)")
    public void dataScopePointCut() {
        
    }
    
    @Before("dataScopePointCut()")
    public void doBefore(JoinPoint point) throws Throwable {
        clearDataScope(point);
        handleDataScope(point);
    }
    
    protected void handleDataScope(final JoinPoint joinPoint) {
        // 获取注解
        DataScope controllerDataScope = getAnnotationLog(joinPoint);
        if (controllerDataScope == null) {
            return;
        }
        
        // 获取当前的用户
        LoginUser loginUser = LoginHelper.getLoginUser();
        if (StringUtils.isNotNull(loginUser)) {
            SysUser currentUser = loginUser.getSysUser();
            // 如果是超级管理员,则不过滤数据
            if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin()) {
                String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), PermissionConstants.VIEW_BASE_DATA);
                dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),
                        controllerDataScope.userAlias(), permission);
            }
        }
    }
    
    /**
     * 数据范围过滤
     */
    public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias, String permission) {
        StringBuilder sqlString = new StringBuilder();
        List<String> conditions = new ArrayList<>();
        
        for (SysRole role : user.getRoles()) {
            String dataScope = role.getDataScope();
            if (!LoginHelper.isTenantAdmin() && conditions.contains(dataScope)) {
                continue;
            }
            if (StringUtils.isNotEmpty(permission) && StringUtils.isNotEmpty(role.getPermissions())
                    && !StringUtils.containsAnyIgnoreCase(role.getPermissions(), Convert.toStrArray(permission))) {
                continue;
            }
            if (DATA_SCOPE_ALL.equals(dataScope)) {
                sqlString = new StringBuilder();
                conditions.add(dataScope);
                break;
            } else if (DATA_SCOPE_CUSTOM.equals(dataScope)) {
                if (StringUtils.isNotEmpty(deptAlias)) {
                    sqlString.append(StringUtils.format(
                            " OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias,
                            role.getRoleId()));
                } else {
                    sqlString.append(StringUtils.format(
                            " OR dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ",
                            role.getRoleId()));
                }
            } else if (DATA_SCOPE_DEPT.equals(dataScope)) {
                if (StringUtils.isNotEmpty(deptAlias)) {
                    sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId()));
                } else {
                    sqlString.append(StringUtils.format(" OR dept_id = {} ", user.getDeptId()));
                }
            } else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) {
                if (StringUtils.isNotEmpty(deptAlias)) {
                    sqlString.append(StringUtils.format(
                            " OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",
                            deptAlias, user.getDeptId(), user.getDeptId()));
                } else {
                    sqlString.append(StringUtils.format(
                            " OR dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",
                            user.getDeptId(), user.getDeptId()));
                }
            } else if (DATA_SCOPE_SELF.equals(dataScope)) {
                if (StringUtils.isNotEmpty(userAlias)) {
                    sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId()));
                } else {
                    // 数据权限为仅本人且没有userAlias别名不查询任何数据
                    sqlString.append(StringUtils.format(" OR user_id = {} ", user.getUserId()));
                }
            }
            conditions.add(dataScope);
        }
        
        if (StringUtils.isNotEmpty(sqlString.toString())) {
            Object params = joinPoint.getArgs()[0];
            if (StringUtils.isNotNull(params) && params instanceof BaseEntity) {
                BaseEntity baseEntity = (BaseEntity) params;
                baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")");
            }
        }
    }
    
    /**
     * 拼接权限sql前先清空params.dataScope参数防止注入
     */
    private void clearDataScope(final JoinPoint joinPoint) {
        Object params = joinPoint.getArgs()[0];
        if (StringUtils.isNotNull(params) && params instanceof BaseEntity) {
            BaseEntity baseEntity = (BaseEntity) params;
            baseEntity.getParams().put(DATA_SCOPE, "");
        }
    }
    
    /**
     * 是否存在注解,如果存在就获取
     */
    private DataScope getAnnotationLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        
        if (method != null) {
            return method.getAnnotation(DataScope.class);
        }
        return null;
    }
}

性能监控切面

/**
 * 性能监控切面
 */
@Aspect
@Component
@Slf4j
public class PerformanceAspect {
    
    @Autowired
    private MeterRegistry meterRegistry;
    
    /**
     * 监控所有Controller方法
     */
    @Pointcut("execution(* com.ruoyi.*.controller.*.*(..))")
    public void controllerPointcut() {
        
    }
    
    /**
     * 监控所有Service方法
     */
    @Pointcut("execution(* com.ruoyi.*.service.*.*(..))")
    public void servicePointcut() {
        
    }
    
    /**
     * 环绕通知 - 性能监控
     */
    @Around("controllerPointcut() || servicePointcut()")
    public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        String fullMethodName = className + "." + methodName;
        
        // 开始计时
        Timer.Sample sample = Timer.start(meterRegistry);
        
        long startTime = System.currentTimeMillis();
        Object result = null;
        Throwable exception = null;
        
        try {
            result = joinPoint.proceed();
            return result;
        } catch (Throwable e) {
            exception = e;
            throw e;
        } finally {
            long endTime = System.currentTimeMillis();
            long duration = endTime - startTime;
            
            // 记录执行时间
            sample.stop(Timer.builder("method.execution.time")
                .description("方法执行时间")
                .tag("class", className)
                .tag("method", methodName)
                .tag("status", exception == null ? "success" : "error")
                .register(meterRegistry));
            
            // 记录方法调用次数
            Counter.builder("method.execution.count")
                .description("方法调用次数")
                .tag("class", className)
                .tag("method", methodName)
                .tag("status", exception == null ? "success" : "error")
                .register(meterRegistry)
                .increment();
            
            // 慢方法告警
            if (duration > 5000) { // 超过5秒
                log.warn("慢方法检测: {} 执行时间: {}ms", fullMethodName, duration);
                
                // 发送告警
                sendSlowMethodAlert(fullMethodName, duration);
            }
            
            // 记录详细日志
            if (log.isDebugEnabled()) {
                log.debug("方法执行监控: {} 执行时间: {}ms, 状态: {}", 
                         fullMethodName, duration, exception == null ? "成功" : "失败");
            }
        }
    }
    
    /**
     * 发送慢方法告警
     */
    private void sendSlowMethodAlert(String methodName, long duration) {
        // 实现告警逻辑,如发送邮件、短信等
        AsyncManager.me().execute(() -> {
            // 异步发送告警
            log.info("发送慢方法告警: {} 执行时间: {}ms", methodName, duration);
        });
    }
}

/**
 * 缓存切面
 */
@Aspect
@Component
@Slf4j
public class CacheAspect {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    /**
     * 缓存注解切点
     */
    @Pointcut("@annotation(com.ruoyi.common.annotation.Cache)")
    public void cachePointcut() {
        
    }
    
    /**
     * 环绕通知 - 缓存处理
     */
    @Around("cachePointcut()")
    public Object handleCache(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取缓存注解
        Cache cacheAnnotation = getCacheAnnotation(joinPoint);
        if (cacheAnnotation == null) {
            return joinPoint.proceed();
        }
        
        // 生成缓存键
        String cacheKey = generateCacheKey(joinPoint, cacheAnnotation);
        
        try {
            // 尝试从缓存获取
            Object cachedResult = redisTemplate.opsForValue().get(cacheKey);
            if (cachedResult != null) {
                log.debug("缓存命中: {}", cacheKey);
                return cachedResult;
            }
            
            // 缓存未命中,执行方法
            Object result = joinPoint.proceed();
            
            // 将结果存入缓存
            if (result != null) {
                Duration ttl = Duration.ofSeconds(cacheAnnotation.ttl());
                redisTemplate.opsForValue().set(cacheKey, result, ttl);
                log.debug("缓存存储: {} TTL: {}s", cacheKey, cacheAnnotation.ttl());
            }
            
            return result;
            
        } catch (Exception e) {
            log.error("缓存处理异常: {}", cacheKey, e);
            // 缓存异常时直接执行方法
            return joinPoint.proceed();
        }
    }
    
    /**
     * 生成缓存键
     */
    private String generateCacheKey(ProceedingJoinPoint joinPoint, Cache cacheAnnotation) {
        String prefix = cacheAnnotation.prefix();
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        
        StringBuilder keyBuilder = new StringBuilder();
        keyBuilder.append(prefix).append(":").append(className).append(":").append(methodName);
        
        // 添加参数到键中
        Object[] args = joinPoint.getArgs();
        if (args != null && args.length > 0) {
            for (Object arg : args) {
                if (arg != null) {
                    keyBuilder.append(":").append(arg.toString().hashCode());
                }
            }
        }
        
        return keyBuilder.toString();
    }
    
    /**
     * 获取缓存注解
     */
    private Cache getCacheAnnotation(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        
        if (method != null) {
            return method.getAnnotation(Cache.class);
        }
        return null;
    }
}

通知类型详解

五种通知类型

/**
 * 通知类型示例切面
 */
@Aspect
@Component
@Slf4j
public class AdviceTypeAspect {
    
    /**
     * 切入点定义
     */
    @Pointcut("execution(* com.ruoyi.demo.service.*.*(..))")
    public void servicePointcut() {
        
    }
    
    /**
     * 前置通知 - 在目标方法执行前执行
     */
    @Before("servicePointcut()")
    public void beforeAdvice(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        
        log.info("前置通知: 方法 {} 即将执行,参数: {}", methodName, Arrays.toString(args));
        
        // 可以在这里进行参数校验、权限检查等
        validateParameters(args);
    }
    
    /**
     * 后置通知 - 在目标方法执行后执行(无论是否抛出异常)
     */
    @After("servicePointcut()")
    public void afterAdvice(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        
        log.info("后置通知: 方法 {} 执行完成", methodName);
        
        // 可以在这里进行资源清理等操作
        cleanupResources();
    }
    
    /**
     * 返回通知 - 在目标方法正常返回后执行
     */
    @AfterReturning(pointcut = "servicePointcut()", returning = "result")
    public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
        
        log.info("返回通知: 方法 {} 正常返回,结果: {}", methodName, result);
        
        // 可以在这里进行结果处理、缓存更新等
        processResult(result);
    }
    
    /**
     * 异常通知 - 在目标方法抛出异常后执行
     */
    @AfterThrowing(pointcut = "servicePointcut()", throwing = "exception")
    public void afterThrowingAdvice(JoinPoint joinPoint, Throwable exception) {
        String methodName = joinPoint.getSignature().getName();
        
        log.error("异常通知: 方法 {} 抛出异常: {}", methodName, exception.getMessage(), exception);
        
        // 可以在这里进行异常处理、告警发送等
        handleException(methodName, exception);
    }
    
    /**
     * 环绕通知 - 包围目标方法执行
     */
    @Around("servicePointcut()")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        long startTime = System.currentTimeMillis();
        
        log.info("环绕通知开始: 方法 {} 开始执行", methodName);
        
        try {
            // 执行前的处理
            preProcess(joinPoint);
            
            // 执行目标方法
            Object result = joinPoint.proceed();
            
            // 执行后的处理
            postProcess(result);
            
            long endTime = System.currentTimeMillis();
            log.info("环绕通知结束: 方法 {} 执行成功,耗时: {}ms", methodName, endTime - startTime);
            
            return result;
            
        } catch (Throwable e) {
            long endTime = System.currentTimeMillis();
            log.error("环绕通知异常: 方法 {} 执行失败,耗时: {}ms", methodName, endTime - startTime, e);
            
            // 异常处理
            handleException(methodName, e);
            
            throw e;
        }
    }
    
    /**
     * 参数校验
     */
    private void validateParameters(Object[] args) {
        // 实现参数校验逻辑
        for (Object arg : args) {
            if (arg == null) {
                throw new IllegalArgumentException("参数不能为空");
            }
        }
    }
    
    /**
     * 资源清理
     */
    private void cleanupResources() {
        // 实现资源清理逻辑
        log.debug("执行资源清理");
    }
    
    /**
     * 结果处理
     */
    private void processResult(Object result) {
        // 实现结果处理逻辑
        if (result != null) {
            log.debug("处理方法返回结果: {}", result.getClass().getSimpleName());
        }
    }
    
    /**
     * 异常处理
     */
    private void handleException(String methodName, Throwable exception) {
        // 实现异常处理逻辑
        AsyncManager.me().execute(() -> {
            // 异步发送异常告警
            log.info("发送异常告警: 方法 {} 发生异常", methodName);
        });
    }
    
    /**
     * 前置处理
     */
    private void preProcess(ProceedingJoinPoint joinPoint) {
        // 实现前置处理逻辑
        log.debug("执行前置处理");
    }
    
    /**
     * 后置处理
     */
    private void postProcess(Object result) {
        // 实现后置处理逻辑
        log.debug("执行后置处理");
    }
}

通知执行顺序

/**
 * 通知执行顺序演示切面
 */
@Aspect
@Component
@Order(1) // 设置切面执行顺序
@Slf4j
public class OrderedAspect {
    
    @Pointcut("execution(* com.ruoyi.demo.service.OrderService.*(..))")
    public void orderServicePointcut() {
        
    }
    
    @Before("orderServicePointcut()")
    public void before(JoinPoint joinPoint) {
        log.info("Order 1 - Before: {}", joinPoint.getSignature().getName());
    }
    
    @Around("orderServicePointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("Order 1 - Around Before: {}", joinPoint.getSignature().getName());
        
        try {
            Object result = joinPoint.proceed();
            log.info("Order 1 - Around After: {}", joinPoint.getSignature().getName());
            return result;
        } catch (Throwable e) {
            log.info("Order 1 - Around Exception: {}", joinPoint.getSignature().getName());
            throw e;
        }
    }
    
    @After("orderServicePointcut()")
    public void after(JoinPoint joinPoint) {
        log.info("Order 1 - After: {}", joinPoint.getSignature().getName());
    }
    
    @AfterReturning("orderServicePointcut()")
    public void afterReturning(JoinPoint joinPoint) {
        log.info("Order 1 - AfterReturning: {}", joinPoint.getSignature().getName());
    }
    
    @AfterThrowing("orderServicePointcut()")
    public void afterThrowing(JoinPoint joinPoint) {
        log.info("Order 1 - AfterThrowing: {}", joinPoint.getSignature().getName());
    }
}

/**
 * 第二个切面 - 演示多切面执行顺序
 */
@Aspect
@Component
@Order(2)
@Slf4j
public class SecondOrderedAspect {
    
    @Pointcut("execution(* com.ruoyi.demo.service.OrderService.*(..))")
    public void orderServicePointcut() {
        
    }
    
    @Before("orderServicePointcut()")
    public void before(JoinPoint joinPoint) {
        log.info("Order 2 - Before: {}", joinPoint.getSignature().getName());
    }
    
    @Around("orderServicePointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("Order 2 - Around Before: {}", joinPoint.getSignature().getName());
        
        try {
            Object result = joinPoint.proceed();
            log.info("Order 2 - Around After: {}", joinPoint.getSignature().getName());
            return result;
        } catch (Throwable e) {
            log.info("Order 2 - Around Exception: {}", joinPoint.getSignature().getName());
            throw e;
        }
    }
    
    @After("orderServicePointcut()")
    public void after(JoinPoint joinPoint) {
        log.info("Order 2 - After: {}", joinPoint.getSignature().getName());
    }
}

切点表达式语法

切点表达式详解

/**
 * 切点表达式示例
 */
@Aspect
@Component
@Slf4j
public class PointcutExpressionAspect {
    
    /**
     * execution表达式 - 最常用的切点表达式
     * 语法:execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
     */
    
    // 匹配所有public方法
    @Pointcut("execution(public * *(..))")
    public void publicMethods() {}
    
    // 匹配特定包下的所有方法
    @Pointcut("execution(* com.ruoyi.system.service.*.*(..))")
    public void systemServiceMethods() {}
    
    // 匹配特定类的所有方法
    @Pointcut("execution(* com.ruoyi.system.service.ISysUserService.*(..))")
    public void userServiceMethods() {}
    
    // 匹配特定方法名
    @Pointcut("execution(* com.ruoyi.system.service.*.select*(..))")
    public void selectMethods() {}
    
    // 匹配特定参数类型的方法
    @Pointcut("execution(* com.ruoyi.system.service.*.*(Long))")
    public void methodsWithLongParam() {}
    
    // 匹配返回特定类型的方法
    @Pointcut("execution(com.ruoyi.common.core.domain.AjaxResult com.ruoyi.system.service.*.*(..))")
    public void methodsReturningAjaxResult() {}
    
    /**
     * within表达式 - 匹配特定类型内的方法
     */
    
    // 匹配特定包内的所有方法
    @Pointcut("within(com.ruoyi.system.service.*)")
    public void withinSystemService() {}
    
    // 匹配特定类内的所有方法
    @Pointcut("within(com.ruoyi.system.service.impl.SysUserServiceImpl)")
    public void withinUserServiceImpl() {}
    
    // 匹配实现特定接口的类的所有方法
    @Pointcut("within(com.ruoyi.system.service.ISysUserService+)")
    public void withinUserServiceInterface() {}
    
    /**
     * this表达式 - 匹配代理对象是指定类型的方法
     */
    @Pointcut("this(com.ruoyi.system.service.ISysUserService)")
    public void thisUserService() {}
    
    /**
     * target表达式 - 匹配目标对象是指定类型的方法
     */
    @Pointcut("target(com.ruoyi.system.service.impl.SysUserServiceImpl)")
    public void targetUserServiceImpl() {}
    
    /**
     * args表达式 - 匹配参数类型的方法
     */
    @Pointcut("args(Long, String)")
    public void methodsWithLongAndStringArgs() {}
    
    /**
     * @annotation表达式 - 匹配有特定注解的方法
     */
    @Pointcut("@annotation(com.ruoyi.common.annotation.Log)")
    public void logAnnotatedMethods() {}
    
    @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
    public void getMappingMethods() {}
    
    /**
     * @within表达式 - 匹配类上有特定注解的所有方法
     */
    @Pointcut("@within(org.springframework.stereotype.Service)")
    public void serviceAnnotatedClasses() {}
    
    /**
     * @target表达式 - 匹配目标对象的类有特定注解的方法
     */
    @Pointcut("@target(org.springframework.stereotype.Repository)")
    public void repositoryAnnotatedTargets() {}
    
    /**
     * @args表达式 - 匹配参数上有特定注解的方法
     */
    @Pointcut("@args(org.springframework.web.bind.annotation.RequestBody)")
    public void methodsWithRequestBodyArgs() {}
    
    /**
     * 组合切点表达式
     */
    
    // AND操作
    @Pointcut("execution(* com.ruoyi.system.service.*.*(..)) && @annotation(com.ruoyi.common.annotation.Log)")
    public void systemServiceWithLog() {}
    
    // OR操作
    @Pointcut("execution(* com.ruoyi.system.service.*.select*(..)) || execution(* com.ruoyi.system.service.*.get*(..))")
    public void queryMethods() {}
    
    // NOT操作
    @Pointcut("execution(* com.ruoyi.system.service.*.*(..)) && !execution(* com.ruoyi.system.service.*.toString())")
    public void systemServiceExceptToString() {}
    
    /**
     * 复杂切点表达式示例
     */
    @Pointcut("(execution(* com.ruoyi.*.controller.*.*(..)) || execution(* com.ruoyi.*.service.*.*(..))) " +
              "&& !execution(* com.ruoyi.*.controller.*.get*(..)) " +
              "&& @annotation(com.ruoyi.common.annotation.Log)")
    public void complexPointcut() {}
    
    /**
     * 使用切点表达式的通知
     */
    @Before("systemServiceWithLog()")
    public void beforeSystemServiceWithLog(JoinPoint joinPoint) {
        log.info("执行带日志注解的系统服务方法: {}", joinPoint.getSignature().getName());
    }
    
    @Around("queryMethods()")
    public Object aroundQueryMethods(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("执行查询方法: {}", joinPoint.getSignature().getName());
        
        long startTime = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long endTime = System.currentTimeMillis();
        
        log.info("查询方法执行完成,耗时: {}ms", endTime - startTime);
        
        return result;
    }
    
    /**
     * 动态切点表达式
     */
    @Pointcut("execution(* com.ruoyi.system.service.*.*(..)) && @annotation(log) && args(param)")
    public void dynamicPointcut(Log log, Object param) {}
    
    @Before("dynamicPointcut(log, param)")
    public void beforeDynamicPointcut(JoinPoint joinPoint, Log log, Object param) {
        log.info("动态切点 - 方法: {}, 注解: {}, 参数: {}", 
                joinPoint.getSignature().getName(), log.title(), param);
    }
}

事务管理实现

声明式事务管理

/**
 * 事务管理切面
 */
@Aspect
@Component
@Slf4j
public class TransactionAspect {
    
    @Autowired
    private PlatformTransactionManager transactionManager;
    
    /**
     * 事务切点 - 匹配所有Service层的修改方法
     */
    @Pointcut("execution(* com.ruoyi.*.service.*.*(..)) && " +
              "(execution(* *.save*(..)) || execution(* *.update*(..)) || " +
              "execution(* *.delete*(..)) || execution(* *.insert*(..)) || " +
              "execution(* *.remove*(..)))")
    public void transactionalMethods() {}
    
    /**
     * 环绕通知 - 事务管理
     */
    @Around("transactionalMethods()")
    public Object manageTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        
        // 创建事务定义
        DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
        definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        definition.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);
        definition.setTimeout(30); // 30秒超时
        
        // 开始事务
        TransactionStatus status = transactionManager.getTransaction(definition);
        
        try {
            log.debug("开始事务: {}", methodName);
            
            Object result = joinPoint.proceed();
            
            // 提交事务
            transactionManager.commit(status);
            log.debug("提交事务: {}", methodName);
            
            return result;
            
        } catch (Throwable e) {
            // 回滚事务
            transactionManager.rollback(status);
            log.error("回滚事务: {} - 异常: {}", methodName, e.getMessage());
            
            throw e;
        }
    }
    
    /**
     * 批量操作事务管理
     */
    @Around("execution(* com.ruoyi.*.service.*.batch*(..))")
    public Object manageBatchTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        
        // 批量操作使用更长的超时时间
        DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
        definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        definition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
        definition.setTimeout(300); // 5分钟超时
        
        TransactionStatus status = transactionManager.getTransaction(definition);
        
        try {
            log.info("开始批量事务: {}", methodName);
            
            Object result = joinPoint.proceed();
            
            transactionManager.commit(status);
            log.info("提交批量事务: {}", methodName);
            
            return result;
            
        } catch (Throwable e) {
            transactionManager.rollback(status);
            log.error("回滚批量事务: {} - 异常: {}", methodName, e.getMessage());
            
            throw e;
        }
    }
}

/**
 * 自定义事务注解
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CustomTransactional {
    
    /**
     * 事务传播行为
     */
    Propagation propagation() default Propagation.REQUIRED;
    
    /**
     * 事务隔离级别
     */
    Isolation isolation() default Isolation.DEFAULT;
    
    /**
     * 事务超时时间(秒)
     */
    int timeout() default -1;
    
    /**
     * 是否只读事务
     */
    boolean readOnly() default false;
    
    /**
     * 回滚的异常类型
     */
    Class<? extends Throwable>[] rollbackFor() default {};
    
    /**
     * 不回滚的异常类型
     */
    Class<? extends Throwable>[] noRollbackFor() default {};
}

/**
 * 自定义事务管理切面
 */
@Aspect
@Component
@Slf4j
public class CustomTransactionAspect {
    
    @Autowired
    private PlatformTransactionManager transactionManager;
    
    @Around("@annotation(customTransactional)")
    public Object manageCustomTransaction(ProceedingJoinPoint joinPoint, 
                                        CustomTransactional customTransactional) throws Throwable {
        
        String methodName = joinPoint.getSignature().getName();
        
        // 根据注解配置创建事务定义
        DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
        definition.setPropagationBehavior(customTransactional.propagation().value());
        definition.setIsolationLevel(customTransactional.isolation().value());
        definition.setTimeout(customTransactional.timeout());
        definition.setReadOnly(customTransactional.readOnly());
        
        TransactionStatus status = transactionManager.getTransaction(definition);
        
        try {
            log.debug("开始自定义事务: {} - 配置: {}", methodName, customTransactional);
            
            Object result = joinPoint.proceed();
            
            transactionManager.commit(status);
            log.debug("提交自定义事务: {}", methodName);
            
            return result;
            
        } catch (Throwable e) {
            // 检查是否需要回滚
            if (shouldRollback(e, customTransactional)) {
                transactionManager.rollback(status);
                log.error("回滚自定义事务: {} - 异常: {}", methodName, e.getMessage());
            } else {
                transactionManager.commit(status);
                log.warn("异常但不回滚事务: {} - 异常: {}", methodName, e.getMessage());
            }
            
            throw e;
        }
    }
    
    /**
     * 判断是否需要回滚
     */
    private boolean shouldRollback(Throwable e, CustomTransactional customTransactional) {
        // 检查不回滚的异常类型
        for (Class<? extends Throwable> noRollbackClass : customTransactional.noRollbackFor()) {
            if (noRollbackClass.isAssignableFrom(e.getClass())) {
                return false;
            }
        }
        
        // 检查回滚的异常类型
        if (customTransactional.rollbackFor().length > 0) {
            for (Class<? extends Throwable> rollbackClass : customTransactional.rollbackFor()) {
                if (rollbackClass.isAssignableFrom(e.getClass())) {
                    return true;
                }
            }
            return false;
        }
        
        // 默认RuntimeException和Error回滚
        return e instanceof RuntimeException || e instanceof Error;
    }
}

总结

本文详细介绍了Spring AOP切面编程,包括:

  1. 切面定义与配置:日志切面、数据权限切面、性能监控切面、缓存切面
  2. 通知类型详解:前置通知、后置通知、返回通知、异常通知、环绕通知
  3. 切点表达式语法:execution、within、this、target、args、@annotation等表达式
  4. 事务管理实现:声明式事务管理、自定义事务注解、事务切面实现

Spring AOP为应用提供了强大的横切关注点处理能力,是企业级应用开发的重要技术。

在下一篇文章中,我们将探讨Spring事件机制。

参考资料

更多推荐