Spring AOP拦截 EL 表达式实现方法日志记录

需积分: 50 4 下载量 104 浏览量 更新于2024-09-10 收藏 194KB DOCX 举报
在AOP(面向切面编程)中,EL表达式是一种强大的工具,特别是在Spring框架中用于动态地根据业务规则进行决策。本文档探讨了如何利用自定义注解和切面来实现对Service层方法中特定条件的拦截,如当方法参数的值大于0.5时,执行日志输出。以下详细介绍了实现这一功能的步骤和注意事项。 首先,自定义一个名为`Log`的注解,它有两个属性:`spel`用于存储SpEL(Spring Expression Language)表达式,该表达式用于判断是否满足日志记录的条件;`desc`是一个可选的描述字段,用于提供额外的信息。注解的目标是`ElementType.METHOD`,表示它用于修饰方法,且`RetentionPolicy.RUNTIME`确保注解在运行时也能被访问。 ```java @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Log { String spel() required; // 必须提供SpEL表达式 String desc() default "描述"; // 默认描述信息 } ``` 在Service层,我们可以在需要进行日志拦截的方法上添加`@Log`注解,例如: ```java @Service public class MyService { @Log(spel="#param.value > 0.5", desc="参数值大于0.5时输出日志") public void doSomething(double param) { // 方法体... } } ``` 接下来,我们需要创建一个切面类`LogAspect`,这是一个`@Aspect`注解的组件,它将在Spring容器中自动注册。在这个切面中,我们将使用`SpelExpressionParser`解析`spel`属性中的表达式,并使用`LocalVariableTableParameterNameDiscoverer`来获取当前方法的参数名,以便在表达式中引用。 ```java @Component @Aspect public class LogAspect { private final ExpressionParser parser = new SpelExpressionParser(); private final LocalVariableTableParameterNameDiscoverer discoverer; public LogAspect() { this.discoverer = new LocalVariableTableParameterNameDiscoverer(); } @Around("@annotation(log)") public Object logExecution(ProceedingJoinPoint joinPoint, Log log) throws Throwable { // 解析SpEL表达式 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); String paramName = discoverer.getParameterName(signature); Object paramValue = joinPoint.getArgs()[signature.getParameterIndex(paramName)]; // 获取表达式的值 boolean shouldLog = parser.parseExpression(log.spel()).getValue(Boolean.class, paramValue); if (shouldLog) { // 输出日志 System.out.println("日志记录:调用方法[" + signature.getMethod().getName() + "],参数[" + paramValue + "]大于0.5"); } // 执行原始方法 return joinPoint.proceed(); } } ``` 难点在于如何在切面中获取到具体的方法名,这里通过`LocalVariableTableParameterNameDiscoverer`实现了这一点。在切面的`around`方法中,首先解析SpEL表达式,然后检查其结果是否为`true`,如果是,则执行日志记录。 总结起来,实现AOP拦截EL表达式的关键在于理解SpEL语法并将其与自定义注解、切面和参数名称关联起来。通过这种方式,我们可以灵活地在不改变原有代码结构的情况下,动态地控制业务逻辑的执行流程。