首页 > 其他分享 >聊聊我们那些年用过的表达式引擎组件

聊聊我们那些年用过的表达式引擎组件

时间:2024-11-12 10:30:02浏览次数:1  
标签:map return 年用 String 聊聊 组件 expression 表达式 name

前言

我们在设计一些表单或者流程引擎时,可能我们会设计各种各样的表达式或者规则,我们通过各种表达式或者规则来实现我们的业务流转。今天就来盘点一下我们经常会使用到的表达式引擎

常用表达式引擎

1、spring el

官方文档

https://docs.spring.io/spring-framework/reference/core/expressions.html

官方示例
https://github.com/spring-projects/spring-framework/tree/master/spring-expression

Spring Expression Language (SpEL) 是Spring框架中的一个强大的表达式语言,用于在运行时查询和操作对象图。以下是关于Spring EL的几个关键点:

动态查询和操作: SpEL允许你在运行时执行复杂的查询和操作数据,比如读取bean的属性值、调用方法、进行算术运算、逻辑判断等。
集成于Spring框架: SpEL广泛应用于Spring的各种模块中,如Spring Security的访问控制表达式、Spring Data的查询条件定义、Spring Integration的消息路由等。
基本语法: SpEL表达式通常被包含在#{...}中,例如#{property}用来获取一个bean的属性值。它支持字符串、布尔、算术、关系、逻辑运算符,以及方法调用、数组和列表索引访问等。
上下文感知: SpEL能够访问Spring应用上下文中的Bean,这意味着你可以直接在表达式中引用配置的bean,实现高度灵活的配置和运行时行为调整。
类型转换: SpEL提供了内置的类型转换服务,可以自动或显式地将一种类型的值转换为另一种类型。
安全考量: 使用SpEL时需要注意安全性,避免注入攻击。Spring提供了ExpressionParser的配置来限制表达式的执行能力,如禁用方法调用或属性访问等。
例子:

  • 访问Bean属性: #
  • 方法调用: #
  • 三元运算符: #
  • 列表和数组访问: #
  • 算术运算: #

spel工具类

public class SpringExpressionUtil {

    private static final SpelExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();

    private SpringExpressionUtil(){}

    /**
     * Evaluates the given Spring EL expression against the provided root object.
     * 
     * @param rootObject The object to use as the root of the expression evaluation.
     * @param expressionString The Spring EL expression to evaluate.
     * @param returnType The expected return type.
     * @return The result of the expression evaluation.
     */
    public static <T> T evaluateExpression(Map<String, Object> rootObject, String expressionString, Class<T> returnType) {
        StandardEvaluationContext context = new StandardEvaluationContext(rootObject);
        rootObject.forEach(context::setVariable);
        return EXPRESSION_PARSER.parseExpression(expressionString).getValue(context,returnType);
    }



    public static void main(String[] args) {
        Map<String,Object> map = new HashMap<>();
        map.put("name","lybgeek");
        map.put("hello","world");
        System.out.println(evaluateExpression(map,"#root.get('name')",String.class));

    }
}

2、ognl

官方文档
https://ognl.orphan.software/language-guide

官方示例
https://github.com/orphan-oss/ognl

OGNL (Object-Graph Navigation Language) 是一个强大的表达式语言,用于获取和设置Java对象的属性。它在许多Java框架中被用作数据绑定和操作对象图的工具,最著名的应用是在Apache Struts2框架中。以下是关于OGNL的一些关键特性:
简单表达式: OGNL允许你以简单的字符串形式编写表达式来访问对象属性,如person.name就可以获取person对象的name属性。
链式导航: 支持链式调用来深入对象图,例如customer.address.street会依次导航到customer的address属性,再从address获取street。
集合操作: OGNL可以直接在表达式中处理集合和数组,包括遍历、筛选、投影等操作,如customers.{name}可以获取所有customers集合中每个元素的name属性。
上下文敏感: OGNL表达式解析时会考虑一个上下文环境,这个环境包含了变量、对象和其他表达式可能需要的信息。
方法调用与构造器: 除了属性访问,OGNL还支持调用对象的方法和构造新对象,如@myUtil.trim(name)调用工具类方法,或new java.util.Date()创建新对象。
条件与逻辑运算: 支持if、else逻辑,以及&&、||等逻辑运算符,使得表达式可以处理更复杂的逻辑判断。
变量赋值: OGNL不仅能够读取数据,还能设置对象属性的值,如person.name = "Alice"。
安全问题: 和SpEL一样,使用OGNL时也需注意表达式注入的安全风险,确保用户输入不会被直接用于构造表达式,以防止恶意操作。
OGNL以其简洁的语法和强大的功能,在处理对象关系和数据绑定方面非常实用,尤其是在需要动态操作对象和集合的场景下。

ognl工具类

public class OgnlExpressionUtil {


    private OgnlExpressionUtil(){}

    /**
     * Evaluates the given Ognl EL expression against the provided root object.
     * 
     * @param rootObject The object to use as the root of the expression evaluation.
     * @param expressionString The OGNL EL expression to evaluate.
     * @param returnType The expected return type.
     * @return The result of the expression evaluation.
     */
    public static <T> T evaluateExpression(Map<String, Object> rootObject, String expressionString, Class<T> returnType) {
        Object value = OgnlCache.getValue(expressionString, rootObject);
        if(value != null && value.getClass().isAssignableFrom(returnType)){
            return (T)value;
        }

        return null;
    }

    public static void main(String[] args) {
        Map<String,Object> map = new HashMap<>();
        map.put("name","lybgeek");
        map.put("hello","world");
        System.out.println(OgnlExpressionUtil.evaluateExpression(map,"#root.name",String.class));

        System.out.println(SpringExpressionUtil.evaluateExpression(map,"#root.get('hello')",String.class));
    }
}

3、Aviator

官方文档
http://fnil.net/aviator/

官方示例
https://github.com/killme2008/aviatorscript

Aviator是一个轻量级的Java表达式执行引擎,它设计用于高性能的动态计算场景,特别是那些需要在运行时解析和执行复杂表达式的应用场景。以下是Aviator的一些核心特点和功能:
高性能: Aviator优化了表达式的编译和执行过程,特别适合于对性能有严格要求的系统,如金融风控、实时计算等领域。
易于集成: 提供简单的API接口,使得在Java项目中嵌入Aviator变得非常容易,只需引入依赖,即可开始编写和执行表达式。
丰富的表达式支持: 支持数学运算、逻辑运算、比较运算、位运算、字符串操作、三元运算、变量定义与引用、函数调用等,几乎覆盖了所有常见的运算需求。
安全沙箱模式: Aviator提供了沙箱机制,可以限制表达式的执行权限,比如禁止访问某些方法或字段,从而提高应用的安全性。
动态脚本执行: 允许在运行时动态加载和执行脚本,非常适合用于规则引擎、配置驱动的系统逻辑等场景。
JIT编译: Aviator采用即时编译技术,将表达式编译成Java字节码执行,进一步提升执行效率。
数据绑定: 可以方便地将Java对象、Map、List等数据结构绑定到表达式上下文中,实现表达式与Java数据的无缝对接。
扩展性: 支持自定义函数,用户可以根据需要扩展Aviator的功能,增加特定业务逻辑的处理能力。
Aviator因其高性能和灵活性,在需要动态脚本处理的场景中,特别是在那些对性能敏感且需要频繁执行复杂计算逻辑的应用中,是一个非常有吸引力的选择。

Aviator工具类

public final class AviatorExpressionUtil {


    private AviatorExpressionUtil() {
    }

    /**
     * 执行Aviator表达式并返回结果
     *
     * @param expression Aviator表达式字符串
     * @param env        上下文环境,可以包含变量和函数
     * @return 表达式计算后的结果
     */
    public static <T> T evaluateExpression(Map<String, Object> env,String expression, Class<T> returnType) {
        Object value = AviatorEvaluator.execute(expression, env);
        if(value != null && value.getClass().isAssignableFrom(returnType)){
            return (T)value;
        }

        return null;

    }

    public static void main(String[] args) {
        Map<String,Object> map = new HashMap<>();
        map.put("name","lybgeek");
        map.put("hello","world");

        Map<String,Object> env = new HashMap<>();
        env.put("root",map);
        System.out.println(evaluateExpression(env,"#root.name",String.class));
    }

}

4、Mvel2

官方文档
mvel.documentnode.com/

官方示例
https://github.com/mvel/mvel

MVEL2(MVFLEX Expression Language 2)是一种强大且灵活的Java库,用于解析和执行表达式语言。它是MVEL项目的第二代版本,旨在提供高效、简洁的方式来操作对象和执行逻辑。下面是关于MVEL2的一些关键特性和使用指南:
动态类型与静态类型混合: MVEL支持动态类型,同时也允许静态类型检查,这意味着你可以选择是否在编译时检查类型错误,增加了灵活性和安全性。
简洁的语法: MVEL语法基于Java但更加简洁,便于编写和阅读,适用于快速构建表达式和小型脚本。
属性访问与方法调用: 类似于其他表达式语言,MVEL允许直接访问对象属性和调用其方法,如person.name或list.size()。
控制流语句: 支持if、else、switch、循环(for、while)等控制流结构,使得在表达式中实现复杂逻辑成为可能。
模板引擎: MVEL2提供了一个强大的模板引擎,可以用来生成文本输出,类似于Velocity或Freemarker,但与MVEL表达式无缝集成。
变量赋值与函数定义: 直接在表达式中定义变量和函数,支持局部变量和闭包(匿名函数)。
数据绑定与转换: 自动或手动进行类型转换,简化了不同数据类型间的操作。
集成与扩展: MVEL设计为易于集成到现有Java项目中,同时提供了扩展点,允许用户定义自定义函数和操作符。
性能优化: MVEL关注执行效率,通过优化的编译器和执行引擎来减少运行时开销。

5、Hutool表达式引擎门面

官方文档
https://doc.hutool.cn/pages/ExpressionUtil/#介绍

hutool工具包在5.5.0版本之后,提供了表达式计算引擎封装为门面模式,提供统一的API,去除差异。目前支持如下表达式引擎

  • Aviator
  • Apache Jexl3
  • MVEL
  • JfireEL
  • Rhino
  • Spring Expression Language
    (SpEL)

如上所述的表达式引擎不能满足要求,hutool还支持通过SPI进行自定义扩展

基于hutool封装的工具类

public class HutoolExpressionUtil {


    private HutoolExpressionUtil(){}


    /**
     * 执行表达式并返回结果。
     *
     * @param expression 表达式字符串
     * @param variables  变量映射,键为变量名,值为变量值
     * @return 表达式计算后的结果
     */
    public static <T> T evaluateExpression(Map<String, Object> variables,String expression, Class<T> returnType) {
        try {
            Object value = ExpressionUtil.eval(expression, variables);
            if(value != null && value.getClass().isAssignableFrom(returnType)){
                return (T)value;
            }
        } catch (Exception e) {
            throw new RuntimeException("Error executing  expression: " + expression, e);
        }

        return null;
    }


    public static void main(String[] args) {
        Map<String,Object> map = new HashMap<>();
        map.put("name","lybgeek");
        map.put("hello","world");

        Map<String,Object> variables = new HashMap<>();
        variables.put("root",map);
        System.out.println(evaluateExpression(variables,"root.name",String.class));
    }
}

总结

本文介绍了市面比较常用的表达式引擎组件,而这些引擎基本上都可以用hutool提供的表达式门面实现,hutool确实在工具类这方面做得很好,基本上我们日常会用到的工具,它大部分都涵盖到。最后文末demo链接,也提供了跟spring整合的表达引擎聚合实现,大家感兴趣也可以看看。

demo链接

https://github.com/lyb-geek/springboot-learning/tree/master/springboot-el

标签:map,return,年用,String,聊聊,组件,expression,表达式,name
From: https://www.cnblogs.com/linyb-geek/p/18242275

相关文章

  • LangChain 记忆组件深度解析:Chain 组件与 Runnable 深入学习
    在构建复杂的AI应用时,有效管理对话历史和上下文信息至关重要。LangChain框架提供了多种记忆组件,使得开发者能够轻松实现具有记忆功能的聊天机器人。本文将深入探讨LangChain中的记忆组件、Chain组件以及Runnable接口,帮助开发者更好地理解和使用这些强大的工具。LangChain......
  • LangChain Runnable 组件深度解析:灵活配置、错误处理与生命周期管理
    在LangChain框架中,Runnable组件是构建灵活、可配置的AI应用的核心。本文将深入探讨Runnable组件的高级特性,包括动态参数配置、组件替换、错误处理机制以及生命周期管理。通过掌握这些特性,开发者可以构建更加健壮和可维护的AI应用。1.Runnable组件动态添加默认调用参数......
  • LangChain记忆组件深度解析:运行流程与源码剖析
    在构建大型语言模型(LLM)应用时,记忆功能扮演着至关重要的角色。它使得AI能够保持上下文连贯性,提供更加智能和个性化的响应。本文将深入探讨LangChain框架中的记忆组件,详细分析其运行流程和源码实现,为开发者提供全面的技术洞察。1.LangChain-ChatMessageHistory组件解析1.1BaseCha......
  • vue.js组件开发实战:带图标的输入框组件
    1.效果展示2.组件页:icotxtbox.vue使用props:['img','wth']接收父页的参数,img为图标url地址,wth为输入框宽度。this.$emit('iptchange',e.target.value)回调父页方法并将输入值传回父页。<template> <viewclass="icotxtbx"style="display:flex;justify-......
  • 鸿蒙自定义组件生命周期
    在开始之前,我们先明确自定义组件和页面的关系:自定义组件:@Component装饰的UI单元,可以组合多个系统组件实现UI的复用,可以调用组件的生命周期。页面:即应用的UI页面。可以由一个或者多个自定义组件组成,@Entry装饰的自定义组件为页面的入口组件,即页面的根节点,一个页面有且仅能有一个......
  • 【神经网络组件】Transformer Encoder
    【神经网络组件】TransformerEncoder目录【神经网络组件】TransformerEncoder1.seq2seq模型2.为什么只需要TransformerEncoder3.TransformerEncoder的结构1.seq2seq模型什么是sequence:sequence指由多个向量组成的序列。例如,有三个向量:\(\mathbf{a}=[1,0,0]^T,\math......
  • 使用react+copy-to-clipboard封装双击复制组件
    前言:最近在公司研发后台系统,用户反馈在双击某些信息时希望可以进行复制的操作,多处使用进而封装为组件首先:安装copy-to-clipboardnpmi--savecopy-to-clipboard其次:封装组件importReact,{memo,useCallback}from'react';import{notification}from"antd";......
  • 小北的字节跳动青训营与LangChain实战课:深入探索Chain的奥秘(上)写一篇完美鲜花推文?用Se
     前言    最近,字节跳动的青训营再次扬帆起航,作为第二次参与其中的小北,深感荣幸能借此机会为那些尚未了解青训营的友友们带来一些详细介绍。青训营不仅是一个技术学习与成长的摇篮,更是一个连接未来与梦想的桥梁~小北的青训营XMarsCode技术训练营——AI加码,字节跳......
  • Vue2中使用Element-ui封装表单(el-form)组件动态渲染数据
    1.创建一个searchForm组件,将需要封装的searchForm组件全局注册,为了方便下次直接使用在main.js文件中全局注册importSearchFormfrom'./components/SearchForm'Vue.component('SearchForm',SearchForm)2.在searchForm组件中创建基本结构<template><divclass="ces-......
  • 基于surging 的木舟平台如何通过Tcp或者UDP网络组件接入设备
    一、概述     上篇文章介绍了木舟通过HTTP网络组件接入设备,那么此篇文章将介绍如何利用Tcp或者UDP网络组件接入设备.     木舟(Kayak)是什么?      木舟(Kayak)是基于.NET6.0软件环境下的surging微服务引擎进行开发的,平台包含了微服务和物联网平台。支......