Spring spel获取自定义注解参数值方式
spel获取自定义注解参数值
1.注解类
package com.xxx.mall.order.service.component; import java.lang.annotation.*; /** * 库存不足等信息监控 * created by xdc on 2019/4/16 15:43 */ @retention(retentionpolicy.runtime) @target({elementtype.method}) @documented public @interface stockwarncollect { /** 客户id */ string customerid(); /** 来源 */ string source(); /** 请求类型 1:详情页 2:购物车去结算 3:提交订单 */ string pagetype(); }
2.注解使用
@override @stockwarncollect(customerid = "#customerid", source = "#source", pagetype = "2") public map<string, object> validatecarts(long customerid, set<long> userselectedids, short source, jsonarray couponinfo){ // 省略 }
3.aop中处理
import lombok.extern.slf4j.slf4j; import org.apache.commons.lang.exception.exceptionutils; import org.apache.commons.lang3.stringutils; import org.aspectj.lang.proceedingjoinpoint; import org.aspectj.lang.signature; import org.aspectj.lang.annotation.around; import org.aspectj.lang.annotation.aspect; import org.aspectj.lang.annotation.pointcut; import org.aspectj.lang.reflect.methodsignature; import org.springframework.core.localvariabletableparameternamediscoverer; 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; import org.springframework.stereotype.component; import java.lang.reflect.method; /** * 下单失败、库存监控 * created by xdc on 2019/4/16 15:45 */ @aspect @component @slf4j public class stockwarncollectaop { @pointcut(value = "@annotation(com.xxx.mall.order.service.component.stockwarncollect)") public void collectstockwarn(){} @around(value = "collectstockwarn()") public object around(proceedingjoinpoint pjp) throws throwable { method targetmethod = this.gettargetmethod(pjp); stockwarncollect stockwarncollect = targetmethod.getannotation(stockwarncollect.class); // spel信息 string customeridspel = stockwarncollect.customerid(); string sourcespel = stockwarncollect.source(); integer pagetype = null; // 操作类型,纯字符串 if (stringutils.isnotblank(stockwarncollect.pagetype())) { pagetype = integer.valueof(stockwarncollect.pagetype()); } // 客户id、来源解析 expressionparser parser = new spelexpressionparser(); localvariabletableparameternamediscoverer discoverer = new localvariabletableparameternamediscoverer(); string[] params = discoverer.getparameternames(targetmethod); object[] args = pjp.getargs(); evaluationcontext context = new standardevaluationcontext(); for (int len = 0; len < params.length; len++) { context.setvariable(params[len], args[len]); } expression expression = parser.parseexpression(customeridspel); long customerid = expression.getvalue(context, long.class); expression = parser.parseexpression(sourcespel); short source = expression.getvalue(context, short.class); log.info("collectstockwarn customerid:{}, source:{}", customerid, source); // 业务逻辑处理 object result = null; try { result = pjp.proceed(); } catch (throwable e) { log.info("collectstockwarn watchs creating order errormsg:{}", exceptionutils.getstacktrace(e)); if (e instanceof mallexception) { } else { // 未知错误 } throw e; } try { if (result != null) { } } catch (exception e) { log.error("collectstockwarn process error, errormsg:{}", exceptionutils.getstacktrace(e)); } return result; } /** * 获取目标方法 */ private method gettargetmethod(proceedingjoinpoint pjp) throws nosuchmethodexception { signature signature = pjp.getsignature(); methodsignature methodsignature = (methodsignature)signature; method agentmethod = methodsignature.getmethod(); return pjp.gettarget().getclass().getmethod(agentmethod.getname(),agentmethod.getparametertypes()); } }
spel在注解中的使用
spel(spring expression language),即spring表达式语言,是比jsp的el更强大的一种表达式语言。为什么要总结spel,因为它可以在运行时查询和操作数据,尤其是数组列表型数据,因此可以缩减代码量,优化代码结构。个人认为很有用。
1 语法说明
1.1 spel 字面量:
- 整数:#{8}
- 小数:#{8.8}
- 科学计数法:#{1e4}
- string:可以使用单引号或者双引号作为字符串的定界符号。
- boolean:#{true}
1.2 spel引用bean , 属性和方法:
- 引用其他对象:#{car}
- 引用其他对象的属性:#{car.brand}
- 调用其它方法 , 还可以链式操作:#{car.tostring()}
- 调用静态方法静态属性:#{t(java.lang.math).pi}
1.3 spel支持的运算符号:
- 算术运算符:+,-,*,/,%,^(加号还可以用作字符串连接)
- 比较运算符:< , > , == , >= , <= , lt , gt , eg , le , ge
- 逻辑运算符:and , or , not , |
- if-else 运算符(类似三目运算符):?:(temary), ?:(elvis)
- 正则表达式:#{admin.email matches '[a-za-z0-9._%+-]+@[a-za-z0-9.-]+\\.[a-za-z]{2,4}'}
2. 基本用法
spel有三种用法:
1. 是在注解@value中;
2. 在xml配置中;
3. 代码块中使用expression。
2.1 @value
//@value能修饰成员变量和方法形参 //#{}内就是表达式的内容 @value("#{表达式}") public string arg;
2.2 <bean>配置
<bean id="xxx" class="com.java.xxxxx.xx"> <!-- 同@value,#{}内是表达式的值,可放在property或constructor-arg内 --> <property name="arg" value="#{表达式}"> </bean>
2.3 代码块中使用
public class speltest { public static void main(string[] args) { //创建expressionparser解析表达式 expressionparser parser = new spelexpressionparser(); //表达式放置 expression exp = parser.parseexpression("表达式"); //执行表达式,默认容器是spring本身的容器:applicationcontext object value = exp.getvalue(); /**如果使用其他的容器,则用下面的方法*/ //创建一个虚拟的容器evaluationcontext standardevaluationcontext ctx = new standardevaluationcontext(); //向容器内添加bean beana beana = new beana(); ctx.setvariable("bean_id", beana); //setrootobject并非必须;一个evaluationcontext只能有一个rootobject,引用它的属性时,可以不加前缀 ctx.setrootobject(xxx); //getvalue有参数ctx,从新的容器中根据spel表达式获取所需的值 object value = exp.getvalue(ctx); } }
4 #{…}和${…}
- #{…} 用于执行spel表达式,并将内容赋值给属性
- ${…} 主要用于加载外部属性文件中的值
- #{…} 和${…} 可以混合使用,但是必须#{}外面,${}在里面
4.1 ${…}用法
{}里面的内容必须符合spel表达式,通过@value(“${speldefault.value}”)可以获取属性文件中对应的值,但是如果属性文件中没有这个属性,则会报错。可以通过赋予默认值解决这个问题,如@value("${speldefault.value:127.0.0.1}")
// 如果属性文件没有speldefault.value,则会报错 // @value("${speldefault.value}") // private string speldefault2; // 使用default.value设置值,如果不存在则使用默认值 @value("${speldefault.value:127.0.0.1}") private string speldefault;
4.2 #{…}用法
这里只演示简单用法
// spel:调用字符串hello world的concat方法 @value("#{'hello world'.concat('!')}") private string helloworld; // spel: 调用字符串的getbytes方法,然后调用length属性 @value("#{'hello world'.bytes.length}") private string helloworldbytes;
4.3 ${…}和#{…}混合使用
${...}和#{...}可以混合使用,如下文代码执行顺序:通过${server.name}从属性文件中获取值并进行替换,然后就变成了 执行spel表达式{‘server1,server2,server3’.split(‘,’)}。
// spel: 传入一个字符串,根据","切分后插入列表中, #{}和${}配置使用(注意单引号,注意不能反过来${}在外面,#{}在里面) @value("#{'${server.name}'.split(',')}") private list<string> servers;
在上文中在#{}外面,${}在里面可以执行成功,那么反过来是否可以呢${}在外面,#{}在里面,如代码
// spel: 注意不能反过来${}在外面,#{}在里面,这个会执行失败 @value("${#{'helloworld'.concat('_')}}") private list<string> servers2;
答案是不能。因为spring执行${}是时机要早于#{}。在本例中,spring会尝试从属性中查找#{‘helloworld’.concat(‘_’)},那么肯定找不到,由上文已知如果找不到,然后报错。所以${}在外面,#{}在里面是非法操作
以上为个人经验,希望能给大家一个参考,也希望大家多多支持。