Spring Boot项目集成Aviator实现成本计算模块
Spring Boot项目集成Aviator实现表达式动态成本计算功能的完整解决方案
一、Maven依赖配置
<!-- aviator核心库 -->
<dependency><groupId>com.googlecode.aviator</groupId> <artifactId>aviator</artifactId><version>5.3.3</version>
</dependency><!-- 校验框架 -->
<dependency><groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId>
</dependency>
二、配置类设计
1. Aviator引擎配置
@Configuration
public class AviatorConfig {@Bean public AviatorEvaluatorInstance aviatorEvaluator() {AviatorEvaluatorInstance instance = AviatorEvaluator.newInstance(); // 配置引擎参数 instance.setOption(Options.USE_USER_ENV_AS_TOP_ENV_DIRECTLY, true);instance.setOption(Options.MAX_CACHE_SIZE, 1000); // 表达式缓存数量 instance.setOption(Options.TRACE_EVAL, false); // 生产环境关闭调试 return instance;}
}
2. 成本规则加载配置
@Configuration
@ConfigurationProperties(prefix = "cost.rules")
public class CostRuleConfig {// 规则文件存储路径 private String location = "classpath:rules/cost";// 规则映射表(示例:{ruleKey: 规则文件名})private Map<String, String> mapping = new HashMap<>();// Getter/Setter
}
三、领域模型设计
1. 成本计算请求DTO
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CostCalculateRequest {@NotBlank(message = "成本类型不能为空")private String costType; // 对应规则类型如:hardware/software @NotNull(message = "计算参数不能为空")private Map<String, Object> params; // 输入参数如数量、单价等
}
2. 统一响应DTO
@Data
@AllArgsConstructor
public class ApiResponse<T> {private int code;private String message;private T data;public static <T> ApiResponse<T> success(T data) {return new ApiResponse<>(200, "Success", data);}
}
四、业务层实现
1. 表达式加载策略接口
public interface ExpressionLoader {/*** 加载指定规则表达式 * @param ruleKey 规则标识 * @return 编译后的表达式 */Expression loadExpression(String ruleKey);
}
2. 文件系统实现(示例)
@Component
@RequiredArgsConstructor
public class FileExpressionLoader implements ExpressionLoader {private final CostRuleConfig ruleConfig;private final AviatorEvaluatorInstance evaluator;@Override public Expression loadExpression(String ruleKey) {String fileName = ruleConfig.getMapping().get(ruleKey); try {Resource resource = new ClassPathResource(ruleConfig.getLocation() + "/" + fileName);return evaluator.compile(resource.getFile().getAbsolutePath(), true);} catch (IOException e) {throw new BusinessException("规则文件加载失败: " + fileName);}}
}
3. 核心计算服务
@Service
@RequiredArgsConstructor
public class CostCalculatorService {private final ExpressionLoader loader;private final AviatorEvaluatorInstance evaluator;/*** 执行成本计算 * @param request 计算请求 * @return 计算结果(包含明细)*/public BigDecimal calculateCost(CostCalculateRequest request) {// 1. 加载表达式 Expression exp = loader.loadExpression(request.getCostType()); // 2. 构建计算环境 Map<String, Object> env = new HashMap<>(request.getParams()); env.put("log", Math::log); // 注入数学函数 // 3. 执行计算 Object result = exp.execute(env); // 4. 结果转换 if (result instanceof Number) {return new BigDecimal(result.toString()).setScale(2, RoundingMode.HALF_UP);}throw new BusinessException("计算结果类型异常");}
}
五、控制层实现
@RestController
@RequestMapping("/api/cost")
@RequiredArgsConstructor
public class CostController {private final CostCalculatorService calculator;@PostMapping("/calculate")public ApiResponse<BigDecimal> calculate(@Valid @RequestBody CostCalculateRequest request) {try {BigDecimal result = calculator.calculateCost(request); return ApiResponse.success(result); } catch (BusinessException e) {return new ApiResponse<>(400, e.getMessage(), null);}}
}
六、规则文件示例
创建 resources/rules/cost/hardware.av
:
## 硬件成本计算规则
let {quantity = params.quantity, unit_price = params.unitPrice, discount = params.discount ?: 1.0 # 默认不打折
};# 计算逻辑:数量*单价*折扣 + 固定运输成本
total = quantity * unit_price * discount + 500;# 返回结果
return total;
七、异常处理增强
@ControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(BusinessException.class) @ResponseStatus(HttpStatus.BAD_REQUEST)@ResponseBody public ApiResponse<Void> handleBusinessException(BusinessException ex) {return new ApiResponse<>(400, ex.getMessage(), null);}@ExceptionHandler({ExpressionSyntaxErrorException.class, ExpressionRuntimeException.class}) @ResponseStatus(HttpStatus.BAD_REQUEST)@ResponseBody public ApiResponse<Void> handleAviatorException(Exception ex) {return new ApiResponse<>(400, "规则执行错误: " + ex.getMessage(), null);}
}
八、安全增强配置
在 application.yml
中添加:
yaml复制aviator:security:max-loop-count: 10000 # 防止死循环 forbidden-classes: # 禁用危险类 - java.lang.System - java.lang.Runtime max-expression-length: 5000 # 限制表达式长度
九、性能优化建议
- 表达式缓存:利用Aviator内置的编译缓存(已通过
compile
方法的第二个参数启用) - 预热加载:在应用启动时预加载高频规则
java复制@EventListener(ApplicationReadyEvent.class)
public void preloadHotRules() {List<String> hotRules = Arrays.asList("hardware", "software");hotRules.forEach(rule -> calculator.calculateCost(new CostCalculateRequest(rule, new HashMap<>())));
}
- 监控埋点:通过Spring Actuator暴露指标
@Bean
public MeterRegistryCustomizer<MeterRegistry> aviatorMetrics() {return registry -> registry.config().commonTags("module", "cost-calculation");
}
十、验证测试用例
@SpringBootTest
public class CostCalculatorTest {@Autowired private CostCalculatorService calculator;@Test void testHardwareCost() {Map<String, Object> params = Map.of( "quantity", 100,"unitPrice", 250.5,"discount", 0.9 );BigDecimal result = calculator.calculateCost( new CostCalculateRequest("hardware", params));assertEquals(new BigDecimal("23045.00"), result);}
}