目录
🍸前言
🍻一、Spring EL
1.1 定义
1.2 常见使用方式
🍺二、项目案例
2.1 实现一个简单的案例
2.2 创建注解
2.3 切面类实现
2.4 创建测试接口
2.5 测试
🍹三、章末
🍸前言
小伙伴们大家好,前段时间在网上无意间听到了一个很耳生的名词 “Spring EL 表达式”,这几天通过文章、视频的方式了解了些,然后再本地实操下
🍻一、Spring EL
1.1 定义
首先来看下全称 Spring Expression Language ,正如命名所言,是 Spring 框架中一种表达式语言。类似于其他编程语言中的表达式语言,用于在运行时计算值或执行特定任务,比如在运行时评估复杂的表达式,通过这些表达式可以访问和操作应用程序上下文中的对象
1.2 常见使用方式
其实在项目中这种语法应该挺常见的比如下面这些:
(1)通过 EL 表达式获取 bean 属性
"#{beanName.property}"
(2)通过 EL 表达式调用某个 bean 方法
"#{bean.myMethod(args)}"
(3)通过 EL 表达式进行运算
"#{num1 + num2}"
...
使用方式有很多,本地就挑最常用以及最常见的使用方式,结合注解使用,动态的处理注解参数
🍺二、项目案例
2.1 实现一个简单的案例
比如自定义一个注解,用于控制方法级别的日志记录,但日志级别是根据运行时条件动态决定的,通过 Spring EL 表达式实现如下
注:本地环境需要一个可以启动的 SpringBoot 项目,并且引入了 aop 依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>
2.2 创建注解
创建一个注解,声明作用域为运行时,并且是作用于方法上面的,另外声明了一个方法,默认值为 true,也就是默认打印日志
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @author HuangBen */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogExecution {String condition() default "true"; // 默认总是记录日志
}
2.3 切面类实现
实现了在目标方法执行时环绕通知,动态的获取方法上的注解值,解析比对,判断是否需要打印日志
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;/*** @author HuangBen */
@Aspect
@Component
public class LoggingAspect {private final SpelExpressionParser parser = new SpelExpressionParser();private final DefaultParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();@Around("@annotation(logExecution)")public Object logAround(ProceedingJoinPoint joinPoint, LogExecution logExecution) throws Throwable {boolean shouldLog = evaluateCondition(joinPoint, logExecution.condition());if (shouldLog) {System.out.println("Executing: " + joinPoint.getSignature().getName() + ", Log Level determined by SpEL.");}try {Object result = joinPoint.proceed();//到这里,目标方法执行完毕,要返回的内容先暂存,执行 finally 方法体return result;} finally {if (shouldLog) {System.out.println("Completed: " + joinPoint.getSignature().getName());}}}//用于解析 SpringEL 表达式并获取解析结果private boolean evaluateCondition(ProceedingJoinPoint joinPoint, String expression) {MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();Method method = methodSignature.getMethod();MethodBasedEvaluationContext context = new MethodBasedEvaluationContext(null,method,joinPoint.getArgs(),parameterNameDiscoverer);Expression exp = parser.parseExpression(expression);return exp.getValue(context, Boolean.class);}
}
2.4 创建测试接口
这里就简单的写个接口,通过传入的参数决定是否打印日志
注:这里的 "#input.equalsIgnoreCase('important')" 就是一个简单的表达式,input 表示提供的变量值,后面的是字符串方法,会忽略大小比较字符串是否等于目标值
@GetMapping("/springel")@LogExecution(condition = "#input.equalsIgnoreCase('important')")public void performAction(String input) {System.out.println("Performing action with input: " + input);// 实际业务逻辑...}
如果单纯的比较输入的参数,这里也可以在切面类中实现,但是如果每个接口的 SpringEl 比较的内容都不一样的,阁下又该如何应对, if else 大军吗,不如直接换一个表达式,每个接口都可以有自己的 SpringEL 表达式(这样,编码的可用性不就增强了),比如可以判断输入的字符的长度是否大于 5,来决定是否打印日志,结果如下,注解正常工作
2.5 测试
2.5.1 输入参数为打印日志:
2.4.2 输入参数为不需要打印
2.5 总结
这个案例只是简单演示了注解中的 SpringEL 表达式使用方式,通过在目标方法上加上注解,在注解里面写好自定义的 SpringEL 表达式,然后在切面类里面通过 SpringEL 解析工具处理表达式,获取结果即可
🍹三、章末
文章初步的了解了 SpringEL 表达式的使用方式,以及结合具体的案例了解 Spring AOP 注解的常见用法
文章到这里就结束了~