Ruoyi-vue-plus-5.x第五篇Spring框架核心技术:5.3 Spring AOP切面编程
·
👋 大家好,我是 阿问学长!专注于分享优质开源项目解析、毕业设计项目指导支持、幼小初高的教辅资料推荐等,欢迎关注交流!🚀
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切面编程,包括:
- 切面定义与配置:日志切面、数据权限切面、性能监控切面、缓存切面
- 通知类型详解:前置通知、后置通知、返回通知、异常通知、环绕通知
- 切点表达式语法:execution、within、this、target、args、@annotation等表达式
- 事务管理实现:声明式事务管理、自定义事务注解、事务切面实现
Spring AOP为应用提供了强大的横切关注点处理能力,是企业级应用开发的重要技术。
在下一篇文章中,我们将探讨Spring事件机制。
参考资料
更多推荐


所有评论(0)