spring中的切面类实践

目标功能:自定义一个在方法执行前后做日志记录的切面方法,需要考虑到方法执行成功与失败的情况,并记录方法名与意义与参数。

自定义的切面类需要配套一个自定义注解执行

java 复制代码
import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
@Documented
public @interface LogRec {
    String value() default ""; // 接口名称描述
}

切面类的类层面

java 复制代码
@Aspect
@Component
public class LogRecordAspect {
    private static final Logger logger = LoggerFactory.getLogger(LogRecordAspect.class);

    @Autowired
    private SystemService systemService;

    @Pointcut("@annotation(com.example.config.aspect.LogRec)")
    public void logRecMethods() { }
    ...
}

类里面环绕目标方法的增强方法

java 复制代码
    /**
     * 环绕通知并记录日志
     * @param joinPoint
     * @param logRec
     * @return
     * @throws Throwable
     */
    @Around("logRecMethods() && @annotation(logRec)")
    public Object logAround(ProceedingJoinPoint joinPoint, LogRec logRec) throws Throwable {
        long startTime = System.currentTimeMillis();

        // 获取注解值
        String methodDesc = logRec.value();
        Object result = null;
        Object logRes = null;

        try {
            // 获取 request
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

            // 执行原方法
            result = joinPoint.proceed();
            logRes = result;
            //logRes可以用来做额外处理,保留最终返回的result的完整性,避免误修改
            //正常日志记录
            ...
            return result;
        } catch (Exception e) {
            //异常日志记录
            logger.error("接口调用异常:{}", e.getMessage(), e);
            throw e;
        } finally {
            //收尾代码
            ...
        }
    }

起初考虑不全时,只想到before和after,这里也记录一下。特别提示:这俩和around不能共存。

java 复制代码
    @Before("logRecMethods() && @annotation(logRec)")
    public void logBefore(JoinPoint joinPoint,LogRec logRec){
        String methodName = joinPoint.getSignature().getName();
        String methodParams = logRec.value();
        String interfaceName = joinPoint.getTarget().getClass().getName();
        logger.info("【"+interfaceName+"】"+methodName+"("+methodParams+")");
        Object[] args = joinPoint.getArgs();
        for (Object arg : args){
            if (arg instanceof HttpServletRequest){
                HttpServletRequest request = (HttpServletRequest) arg;
                systemService.writeReqLog(request,methodParams+":开始执行");
            }
        }
    }
java 复制代码
    @After("logRecMethods() && @annotation(logRec)")
    public void logAfter(JoinPoint joinPoint,LogRec logRec){
        String methodName = joinPoint.getSignature().getName();
        String methodParams = logRec.value();;
        String interfaceName = joinPoint.getTarget().getClass().getName();
        logger.info("【"+interfaceName+"】"+methodName+"("+methodParams+")");
        Object[] args = joinPoint.getArgs();
        for (Object arg : args){
            if (arg instanceof HttpServletRequest){
                HttpServletRequest request = (HttpServletRequest) arg;
                systemService.writeReqLog(request,methodParams+":结束执行");
            }
        }
    }

除了环绕方法,还有出现异常时的处理方法

java 复制代码
    /**
     * 异常通知:记录失败日志
     */
    @AfterThrowing(pointcut = "logRecMethods()", throwing = "ex")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable ex) {
        String methodName = joinPoint.getSignature().getName();
        String methodParams = ex.getMessage();
        String interfaceName = joinPoint.getTarget().getClass().getName();
        logger.info("【"+interfaceName+"】"+methodName+"("+methodParams+")");
        Object[] args = joinPoint.getArgs();
        for (Object arg : args){
            if (arg instanceof HttpServletRequest){
                HttpServletRequest request = (HttpServletRequest) arg;
                systemService.writeReqLog(request,"发生异常:"+ex.getMessage());
            }
        }
    }
相关推荐
一线大码1 小时前
Gradle 基础篇之基础知识的介绍和使用
后端·gradle
Java猿_1 小时前
Spring Boot 集成 Sa-Token 实现登录认证与 RBAC 权限控制(实战)
android·spring boot·后端
小王师傅662 小时前
【轻松入门SpringBoot】actuator健康检查(上)
java·spring boot·后端
码事漫谈2 小时前
C++高并发编程核心技能解析
后端
码事漫谈2 小时前
C++与浏览器交织-从Chrome插件到WebAssembly,开启性能之门
后端
源代码•宸3 小时前
goframe框架签到系统项目(BITFIELD 命令详解、Redis Key 设计、goframe 框架教程、安装MySQL)
开发语言·数据库·经验分享·redis·后端·mysql·golang
⑩-3 小时前
SpringCloud-Nacos 配置中心实战
后端·spring·spring cloud
java1234_小锋4 小时前
[免费]SpringBoot+Vue勤工助学管理系统【论文+源码+SQL脚本】
spring boot·后端·mybatis·勤工助学
踏浪无痕5 小时前
从 Guava ListenableFuture 学习生产级并发调用实践
后端·面试·架构