自定义注解中使用SpEL表达式,动态获取方法参数或执行方法

您所在的位置:网站首页 el表达式的值 自定义注解中使用SpEL表达式,动态获取方法参数或执行方法

自定义注解中使用SpEL表达式,动态获取方法参数或执行方法

2023-07-20 13:25| 来源: 网络整理| 查看: 265

自定义注解中使用SpEL表达式,动态获取方法参数或执行方法 1 SpEL的常见用法1.1 获取变量1.2 执行方法1.3 其他用法 2 自己实现表达式的运行2.1 获取变量2.2 执行方法 3 自定义注解并通过SpEL获取参值 SpEL表达式很早就接触过,感觉很高大上,但是没有了解过更多的使用方法。基本都是一些开源项目包装好的。

1 SpEL的常见用法 1.1 获取变量

获取变量的功能,比较常见的用法是spring中缓存注解的功能。可以动态获取方法中参数的值。如下:

@Cacheable(value = "cacheKey",key = "#key") public String getName(String key) { return "name"; } 1.2 执行方法

这个例子用过spring security的或者类似的授权框架的可能比较熟悉。如下

@PreAuthorize("hasRole('ROLE_ADMIN')") public void addUser(User user) { System.out.println("addUser................" + user); }

这里其实就是通过表达式去执行某个类的hasRole方法,参数为’ROLE_ADMIN’,然后获得返回值再进行后续操作。

1.3 其他用法

表达式不仅支持获取属性和执行方法,还支持各种运算符操作,能查到很多其他大神写的。这里就不写了

2 自己实现表达式的运行

写demo之前先简单介绍一下执行表达式时比较重要的几个类

类名描述ExpressionParser表达式的解析器,主要用来生产表达式Expression表达式对象EvaluationContext表达式运行的上下文。包含了表达式运行时的参数,以及需要执行方法的类 2.1 获取变量 import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; /** * * @Author: dong * @Date: 2022/6/23 10:25 */ public class Demo { public static void main(String[] args) { // 表达式解析器 ExpressionParser parser = new SpelExpressionParser(); // 解析出一个表达式 Expression expression = parser.parseExpression("#user.name"); // 开始准备表达式运行环境 EvaluationContext ctx = new StandardEvaluationContext(); ctx.setVariable("user", new User("三侃")); String value = expression.getValue(ctx, String.class); System.out.println(value); } public static class User{ private String name; public User(String name){ this.name = name; } public String getName(){ return this.name; } } }

最后获得的value就是传入的值,这个demo已经可以通过表达式获取到user的name属性值了。

2.2 执行方法 import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; /** * * @Author: dong * @Date: 2022/6/23 10:25 */ public class Demo { public static void main(String[] args) { ExpressionParser parser = new SpelExpressionParser(); Expression expression = parser.parseExpression("getInput()"); StandardEvaluationContext ctx = new StandardEvaluationContext(); User user = new User("三侃"); // 设置需要执行方法的类 ctx.setRootObject(user); String value = expression.getValue(ctx, String.class); System.out.println(value); } public static class User{ private String name; public User(String name){ this.name = name; } public String getName(){ return this.name; } public String getInput(){ return "我叫:"+this.name; } } }

上面demo的运行结果是“我叫:三侃”,也就是调用getInput方法的结果

3 自定义注解并通过SpEL获取参值

demo是用java的动态代理实现的,所以中间会多出一个需要将接口方法转换为实现类方法的步骤,springAOP的话可以忽略。 User类

public class User { private String name; public User(String name) { this.name = name; } public String getName() { return this.name; } }

自定义注解

import java.lang.annotation.*; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface GetVal{ String spel(); }

接口类和其实现类

public interface Run { void run(User user); } public class RunImpl implements Run { @GetVal(spel = "#user.name") @Override public void run(User user) { System.out.println(user.getName() + "正在跑"); } }

代理类

import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * * @Author: dong * @Date: 2022/6/23 13:46 */ public class RunProxy implements InvocationHandler { private Object target; public RunProxy(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 由于demo的动态代理使用接口无法获取到形参名称,所以转换为实现类的方法对象 method = target.getClass().getMethod(method.getName(), method.getParameterTypes()); Object res = method.invoke(target, args); DefaultParameterNameDiscoverer defaultParameterNameDiscoverer = new DefaultParameterNameDiscoverer(); // 这里参考的org.springframework.context.expression.MethodBasedEvaluationContext.lazyLoadArguments String[] parameterNames = defaultParameterNameDiscoverer.getParameterNames(method); GetVal annotation = method.getAnnotation(GetVal.class); ExpressionParser parser = new SpelExpressionParser(); Expression expression = parser.parseExpression(annotation.spel()); StandardEvaluationContext ctx = new StandardEvaluationContext(); // 填充表达式上下文环境 for(int i=0;i User user = new User("三侃"); RunImpl runImpl = new RunImpl(); RunProxy proxy = new RunProxy(runImpl); Run run = (Run) Proxy.newProxyInstance(runImpl.getClass().getClassLoader(), runImpl.getClass().getInterfaces(), proxy); run.run(user); }

可以看到控制台输处如下信息:

Connected to the target VM, address: '127.0.0.1:54442', transport: 'socket' 三侃正在跑 三侃执行了run Disconnected from the target VM, address: '127.0.0.1:54442', transport: 'socket' Process finished with exit code 0

结合springAOP+SpEL就能实现更加丰富的功能了



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3