SpringBoot集成Aviator實現(xiàn)參數(shù)校驗的代碼工程
1.什么是aviator?
Aviator是一個高性能、輕量級的java語言實現(xiàn)的表達式求值引擎,主要用于各種表達式的動態(tài)求值?,F(xiàn)在已經(jīng)有很多開源可用的java表達式求值引擎,為什么還需要Avaitor呢? Aviator的設(shè)計目標是輕量級和高性能 ,相比于Groovy、JRuby的笨重,Aviator非常小,加上依賴包也才450K,不算依賴包的話只有70K;當然,Aviator的語法是受限的,它不是一門完整的語言,而只是語言的一小部分集合。 其次,Aviator的實現(xiàn)思路與其他輕量級的求值器很不相同,其他求值器一般都是通過解釋的方式運行,而Aviator則是直接將表達式編譯成Java字節(jié)碼,交給JVM去執(zhí)行。簡單來說,Aviator的定位是介于Groovy這樣的重量級腳本語言和IKExpression這樣的輕量級表達式引擎之間
Aviator的特性
- 支持大部分運算操作符,包括算術(shù)操作符、關(guān)系運算符、邏輯操作符、位運算符、正則匹配操作符(=~)、三元表達式?: ,并且支持操作符的優(yōu)先級和括號強制優(yōu)先級,具體請看后面的操作符列表。
- 支持大整數(shù)和精度運算(2.3.0版本引入)
- 支持函數(shù)調(diào)用和自定義函數(shù)
- 內(nèi)置支持正則表達式匹配,類似Ruby、Perl的匹配語法,并且支持類Ruby的$digit指向匹配分組。
- 自動類型轉(zhuǎn)換,當執(zhí)行操作的時候,會自動判斷操作數(shù)類型并做相應轉(zhuǎn)換,無法轉(zhuǎn)換即拋異常。
- 支持傳入變量,支持類似a.b.c的嵌套變量訪問。
- 函數(shù)式風格的seq庫,操作集合和數(shù)組
- 性能優(yōu)秀
Aviator的限制
- 沒有if else、do while等語句,沒有賦值語句,僅支持邏輯表達式、算術(shù)表達式、三元表達式和正則匹配。
- 不支持八進制數(shù)字字面量,僅支持十進制和十六進制數(shù)字字面量。
使用場景
- 規(guī)則判斷以及規(guī)則引擎
- 公式計算
- 動態(tài)腳本控制
2.代碼工程
實驗目的
利用aviator+aop實現(xiàn)參數(shù)校驗
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校驗規(guī)則
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
單個規(guī)則注解
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 "";
}多個規(guī)則注解
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);
}
}以上只是一些關(guān)鍵代碼,所有代碼請參見下面代碼倉庫
代碼倉庫
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實現(xiàn)參數(shù)校驗的代碼工程的詳細內(nèi)容,更多關(guān)于SpringBoot Aviator參數(shù)校驗的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
多數(shù)據(jù)源@DS和@Transactional實戰(zhàn)
這篇文章主要介紹了多數(shù)據(jù)源@DS和@Transactional實戰(zhàn),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09
區(qū)分java中String+String和String+char
這篇文章主要向大家詳細區(qū)分了java中String+String和String+char,感興趣的小伙伴們可以參考一下2016-01-01
springboot+redis自定義注解實現(xiàn)發(fā)布訂閱的實現(xiàn)代碼
在Redis中客戶端可以通過訂閱特定的頻道來接收發(fā)送至該頻道的消息,本文主要介紹了springboot+redis自定義注解實現(xiàn)發(fā)布訂閱,具有一定的參考價值,感興趣的可以了解一下2023-08-08
idea2019版Plugins中搜索不到任何插件的問題解決
本文主要介紹了idea2019版Plugins中搜索不到任何插件的問題解決,插件搜不出來的主要原因是plugins.jetbrains.com ping不通,下面就來介紹一下解決方法,感興趣的可以了解一下2023-09-09
Java instanceof關(guān)鍵字用法詳解及注意事項
instanceof 是 Java 的保留關(guān)鍵字。它的作用是測試它左邊的對象是否是它右邊的類的實例,返回 boolean 的數(shù)據(jù)類型。本文重點給大家介紹Java instanceof關(guān)鍵字用法詳解及注意事項,需要的朋友參考下吧2021-09-09
Java通過SMS短信平臺實現(xiàn)發(fā)短信功能 含多語言
這篇文章主要為大家詳細介紹了Java通過SMS短信平臺實現(xiàn)發(fā)短信功能的相關(guān)資料,感興趣的小伙伴們可以參考一下2016-07-07
Java兩整數(shù)相除向上取整的方式詳解(Math.ceil())
在調(diào)外部接口獲取列表數(shù)據(jù)時,需要判斷是否已經(jīng)取完了所有的值,因此需要用到向上取整,下面這篇文章主要給大家介紹了關(guān)于Java兩整數(shù)相除向上取整的相關(guān)資料,需要的朋友可以參考下2022-06-06

