1.什么是aviator?
aviator是一个高性能、轻量级的java语言实现的表达式求值引擎,主要用于各种表达式的动态求值。现在已经有很多开源可用的java表达式求值引擎,为什么还需要avaitor呢? aviator的设计目标是轻量级
和高性能
,相比于groovy、jruby的笨重,aviator非常小,加上依赖包也才450k,不算依赖包的话只有70k;当然,aviator的语法是受限的,它不是一门完整的语言,而只是语言的一小部分集合。 其次,aviator的实现思路与其他轻量级的求值器很不相同,其他求值器一般都是通过解释的方式运行,而aviator则是直接将表达式编译成java字节码
,交给jvm去执行。简单来说,aviator的定位是介于groovy这样的重量级脚本语言和ikexpression这样的轻量级表达式引擎之间
aviator的特性
- 支持大部分运算操作符,包括算术操作符、关系运算符、逻辑操作符、位运算符、正则匹配操作符(=~)、三元表达式?: ,并且支持操作符的优先级和括号强制优先级,具体请看后面的操作符列表。
- 支持大整数和精度运算(2.3.0版本引入)
- 支持函数调用和自定义函数
- 内置支持正则表达式匹配,类似ruby、perl的匹配语法,并且支持类ruby的$digit指向匹配分组。
- 自动类型转换,当执行操作的时候,会自动判断操作数类型并做相应转换,无法转换即抛异常。
- 支持传入变量,支持类似a.b.c的嵌套变量访问。
- 函数式风格的seq库,操作集合和数组
- 性能优秀
aviator的限制
- 没有if else、do while等语句,没有赋值语句,仅支持逻辑表达式、算术表达式、三元表达式和正则匹配。
- 不支持八进制数字字面量,仅支持十进制和十六进制数字字面量。
使用场景
- 规则判断以及规则引擎
- 公式计算
- 动态脚本控制
2.代码工程
实验目的
利用aviator+aop实现参数校验
pom.xml
<?xml version="1.0" encoding="utf-8"?> <project xmlns="http://maven.apache.org/pom/4.0.0" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xsi:schemalocation="http://maven.apache.org/pom/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactid>springboot-demo</artifactid> <groupid>com.et</groupid> <version>1.0-snapshot</version> </parent> <modelversion>4.0.0</modelversion> <artifactid>aviator</artifactid> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-web</artifactid> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-autoconfigure</artifactid> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-test</artifactid> <scope>test</scope> </dependency> <dependency> <groupid>org.projectlombok</groupid> <artifactid>lombok</artifactid> <optional>true</optional> </dependency> <!--aop--> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-aop</artifactid> </dependency> <!--aviator--> <dependency> <groupid>com.googlecode.aviator</groupid> <artifactid>aviator</artifactid> <version>3.3.0</version> </dependency> <dependency> <groupid>com.alibaba</groupid> <artifactid>fastjson</artifactid> <version>1.2.56</version> </dependency> <dependency> <groupid>org.apache.commons</groupid> <artifactid>commons-lang3</artifactid> <version>3.8.1</version> </dependency> </dependencies> </project>
controller
在方法上加伤aviator校验规则
package com.et.controller; import com.et.annotation.check; import com.et.exception.httpresult; import org.springframework.web.bind.annotation.getmapping; import org.springframework.web.bind.annotation.requestmapping; import org.springframework.web.bind.annotation.restcontroller; import java.util.hashmap; import java.util.map; @restcontroller public class helloworldcontroller { @requestmapping("/hello") public map<string, object> showhelloworld(){ map<string, object> map = new hashmap<>(); map.put("msg", "helloworld"); return map; } @getmapping("/simple") @check(ex = "name != null", msg = "name cannot be empty") @check(ex = "age != null", msg = "age cannot be empty") @check(ex = "age > 18", msg = "age must be over 18 years old") @check(ex = "phone != null", msg = "phone cannot be empty") @check(ex = "phone =~ /^(1)[0-9]{10}$/", msg = "the phone number format is incorrect") @check(ex = "string.startswith(phone,\"1\")", msg = "the phone number must start with 1") @check(ex = "idcard != null", msg = "id number cannot be empty") @check(ex = "idcard =~ /^[1-9]\\d{5}[1-9]\\d{3}((0[1-9])||(1[0-2]))((0[1-9])||(1\\d)||(2\\d)||(3[0-1]))\\d{3}([0-9]||x)$/", msg = "id number format is incorrect") @check(ex = "gender == 1", msg = "sex") @check(ex = "date =~ /^[1-9][0-9]{3}-((0)[1-9]|(1)[0-2])-((0)[1-9]|[1,2][0-9]|(3)[0,1])$/", msg = "wrong date format") @check(ex = "date > '2019-12-20 00:00:00:00'", msg = "the date must be greater than 2019-12-20") public httpresult simple(string name, integer age, string phone, string idcard, string date) { system.out.println("name = " + name); system.out.println("age = " + age); system.out.println("phone = " + phone); system.out.println("idcard = " + idcard); system.out.println("date = " + date); return httpresult.success(); } }
annotation
单个规则注解
package com.et.annotation; import java.lang.annotation.*; @target({elementtype.field, elementtype.method, elementtype.parameter}) @retention(retentionpolicy.runtime) //add more on a method @repeatable(checkcontainer.class) public @interface check { string ex() default ""; string msg() default ""; }
多个规则注解
package com.et.annotation; import java.lang.annotation.elementtype; import java.lang.annotation.retention; import java.lang.annotation.retentionpolicy; import java.lang.annotation.target; @target({elementtype.field, elementtype.method, elementtype.parameter}) @retention(retentionpolicy.runtime) public @interface checkcontainer { check[] value(); }
aop拦截注解
package com.et.annotation; import com.et.exception.userfriendlyexception; import com.googlecode.aviator.aviatorevaluator; import org.aspectj.lang.joinpoint; import org.aspectj.lang.annotation.aspect; import org.aspectj.lang.annotation.before; import org.aspectj.lang.annotation.pointcut; import org.aspectj.lang.reflect.methodsignature; import org.springframework.context.annotation.configuration; import org.springframework.core.localvariabletableparameternamediscoverer; import org.springframework.util.stringutils; import java.lang.reflect.method; import java.util.*; @aspect @configuration public class aopconfig { /** * aspects monitor multiple annotations, because one annotation is check and multiple annotations are compiled to checkcontainer */ @pointcut("@annotation(com.et.annotation.checkcontainer) || @annotation(com.et.annotation.check)") public void pointcut() { } @before("pointcut()") public object before(joinpoint point) { //get params object[] args = point.getargs(); //get param name method method = ((methodsignature) point.getsignature()).getmethod(); localvariabletableparameternamediscoverer u = new localvariabletableparameternamediscoverer(); string[] paramnames = u.getparameternames(method); checkcontainer checkcontainer = method.getdeclaredannotation(checkcontainer.class); list<check> value = new arraylist<>(); if (checkcontainer != null) { value.addall(arrays.aslist(checkcontainer.value())); } else { check check = method.getdeclaredannotation(check.class); value.add(check); } for (int i = 0; i < value.size(); i++) { check check = value.get(i); string ex = check.ex(); //in the rule engine, null is represented by nil ex = ex.replaceall("null", "nil"); string msg = check.msg(); if (stringutils.isempty(msg)) { msg = "server exception..."; } map<string, object> map = new hashmap<>(16); for (int j = 0; j < paramnames.length; j++) { //prevent index out of bounds if (j > args.length) { continue; } map.put(paramnames[j], args[j]); } boolean result = (boolean) aviatorevaluator.execute(ex, map); if (!result) { throw new userfriendlyexception(msg); } } return null; } }
全局异常拦截
package com.et.exception; import com.alibaba.fastjson.json; import org.slf4j.logger; import org.slf4j.loggerfactory; import org.springframework.context.annotation.configuration; import org.springframework.http.httpheaders; import org.springframework.http.httpstatus; import org.springframework.http.responseentity; import org.springframework.lang.nullable; import org.springframework.web.bind.annotation.controlleradvice; import org.springframework.web.bind.annotation.exceptionhandler; import org.springframework.web.context.request.webrequest; import org.springframework.web.servlet.mvc.method.annotation.responseentityexceptionhandler; import javax.servlet.http.httpservletrequest; @configuration @controlleradvice public class defaultglobalexceptionhandler extends responseentityexceptionhandler { private static final logger logger = loggerfactory.getlogger(defaultglobalexceptionhandler.class); @override protected responseentity<object> handleexceptioninternal(exception ex, @nullable object body, httpheaders headers, httpstatus status, webrequest request) { httpresult httpresult = httpresult.failure(status.is5xxservererror() ? errorcode.servererror.getdesc() : errorcode.paramerror.getdesc()); logger.error("handleexception, ex caught, contextpath={}, httpresult={}, ex.msg={}", request.getcontextpath(), json.tojsonstring(httpresult), ex.getmessage()); return super.handleexceptioninternal(ex, httpresult, headers, status, request); } @exceptionhandler(exception.class) protected responseentity handleexception(httpservletrequest request, exception ex) { boolean is5xxservererror; httpstatus httpstatus; httpresult httpresult; if (ex instanceof userfriendlyexception) { userfriendlyexception userfriendlyexception = (userfriendlyexception) ex; is5xxservererror = userfriendlyexception.gethttpstatuscode() >= 500; httpstatus = httpstatus.valueof(userfriendlyexception.gethttpstatuscode()); httpresult = httpresult.failure(userfriendlyexception.geterrorcode(), userfriendlyexception.getmessage()); } else if (ex instanceof illegalargumentexception) { // spring assertions are used in parameter judgment. requiretrue will throw an illegalargumentexception. the client cannot handle 5xx exceptions, so 200 is still returned. httpstatus = httpstatus.ok; is5xxservererror = false; httpresult = httpresult.failure("parameter verification error or data abnormality!"); } else { httpstatus = httpstatus.internal_server_error; is5xxservererror = true; httpresult = httpresult.failure(errorcode.servererror.getdesc()); } if (is5xxservererror) { logger.error("handleexception, ex caught, uri={}, httpresult={}", request.getrequesturi(), json.tojsonstring(httpresult), ex); } else { logger.error("handleexception, ex caught, uri={}, httpresult={}, ex.msg={}", request.getrequesturi(), json.tojsonstring(httpresult), ex.getmessage()); } return new responseentity<>(httpresult, httpstatus); } }
以上只是一些关键代码,所有代码请参见下面代码仓库
代码仓库
3.测试
- 启动spring boot应用
- 访问 http://127.0.0.1:8088/simple?name=jack&age=12
- 返回校验信息
{"status":false,"code":4,"message":"age must be over 18 years old","entry":null}
4.引用
https://github.com/killme2008/aviatorscript/blob/master/readme-en.md
以上就是springboot集成aviator实现参数校验的代码工程的详细内容,更多关于springboot aviator参数校验的资料请关注代码网其它相关文章!
发表评论