QLExpress Documentation

repository·main·Indexed 26 days ago

https://github.com/alibaba/qlexpress

An embedded Java dynamic scripting tool for evolving business rules. It provides a high-performance, secure, and flexible DSL supporting functional programming, JSON syntax, and expression tracing. Key features include the SerializableParseCache, which allows scripts to be compiled on one machine and executed on another via JSON-friendly DTOs using Express4Runner.

Tokens
18.9K
Snippets
34
Records
100
Agent score
89%

What's inside QLExpress

  1. Set up Development Environment for Contributing

    main

    To contribute to QLExpress, follow these steps to prepare your local environment:

    1. Generate Antlr4 runtime code: Run mvn compile in the project root.
    2. Configure automatic code formatting: Create a .git/hooks/pre-commit file to ensure spotless is applied before every commit:
    #!/bin/sh
    mvn spotless:apply
    git add -u
    exit 0
    1. Run tests: Use mvn test to verify your changes locally.
  2. Set up QLExpress development environment

    main

    To contribute to QLExpress, follow these steps to prepare your local environment:

    1. Generate Antlr4 code: Run mvn compile in the project root to generate the necessary runtime code.
    2. Configure Code Formatting: Use the spotless plugin to ensure code adheres to project standards. You can automate this by creating a .git/hooks/pre-commit file.
    3. Run Tests: Always execute mvn test locally to ensure code quality before submitting changes.
    # Create a pre-commit hook to automate formatting
    #!/bin/sh
    mvn spotless:apply
    git add -u
    exit 0
  3. Use Java 8 features in QLExpress

    main

    QLExpress supports common Java 8 syntax, including for-each loops, Stream API, and functional interfaces.

    To use the Stream API on Java collections, you must ensure your Security Policy is configured to allow access to Java methods.

    Lambda Expressions can be assigned to Java 8 functional interfaces (like Function, Consumer, or Predicate) or passed as arguments to methods accepting these interfaces.

  4. Export and Import Workflow for Parse Cache

    main

    The serialization process follows these specific pipelines:

    Export Workflow: script $\rightarrow$ Express4Runner.parseDefinition(script) $\rightarrow$ QCompileCache $\rightarrow$ SerializableParseCacheExporter $\rightarrow$ SerializableParseCache DTO.

    Import Workflow: SerializableParseCache DTO $\rightarrow$ SerializableParseCacheImporter $\rightarrow$ QLambdaDefinitionInner + QLInstruction[] $\rightarrow$ QCompileCache $\rightarrow$ LoadedParseCache.

    Key Importer Responsibilities:

    • Validate top-level required fields and modelVersion support.
    • Validate main, instructions, params, and all operand schemas.
    • Parse class names and bind operators via OperatorManager.
    • Reconstruct DefaultErrReporter, nested lambdas, catch tables, and trace trees.
    • Crucial: The importer must NOT re-parse the original script to ensure the goal of 'direct deserialization for execution' is met.
  5. Configure Expression Caching

    main

    To improve performance, you can enable caching so that identical expressions are not re-compiled.

    Standard Cache: Enable the cache option in the runner. Note that this cache has no size limit and is suitable for a finite number of expressions. Use clearCompileCache() to prevent memory issues.

    Serializable Pre-compiled Cache: For distributed environments, you can pre-compile scripts on a production machine, serialize the SerializableParseCache (using Jackson, Fastjson2, etc.), and distribute it to consumer machines. Consumers should load it as a LoadedParseCache for high-frequency execution.

  6. Execute expressions using a Java Object as context

    main

    You can execute a QLExpress script by passing a plain Java object as the context using the execute(String, Object, QLOptions) method. In this mode, variable names in the script correspond to the fields or accessible getter methods of the provided object.

    Typical Scenario: Passing an existing DTO or POJO that carries context data so the script can read its fields directly.

  7. Use Dynamic Strings and Templates

    main

    QLExpress4 supports string interpolation using the $^{expression} syntax. This allows you to embed calculation results directly into strings.

    Example: "Hello, $^{user.name}"

    Escaping: To keep the literal characters, use \$: "\$^{expression}".

    Template Engine: You can use executeTemplate to render template strings without manually adding quotes.

  8. Install QLExpress4 via Maven

    main

    To use QLExpress4 in your Java project, add the following dependency to your pom.xml. It is recommended to use the latest stable version 4.1.2.

    Requirements:

    • JDK 8 or higher
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>qlexpress4</artifactId>
        <version>4.1.2</version>
    </dependency>
  9. Use @QLAlias for Java Class, Field, and Method Aliases

    main

    To allow non-technical users to write natural language rules, use the @QLAlias annotation on your Java classes, fields, or methods. This maps technical names to user-friendly aliases used within QLExpress scripts.

    Example mapping:

    • Class @QLAlias("用户") maps to 用户 in scripts.
    • Field @QLAlias("是vip") maps to 是vip in scripts.
    @QLAlias("用户")
    public class User {
        @QLAlias("是vip")
        private boolean vip;
        // ...
    }
    
    // Usage in script:
    // "用户.是vip? 订单.金额 * 0.8 : 订单.金额"
  10. Define and use Macros

    main

    Macros allow for code reuse by inserting predefined script fragments into the execution flow. Unlike functions, macros share the caller's scope and have no parameter passing overhead.

    In QLExpress4, macros are inserted at the call site, meaning return, continue, and break statements within a macro will affect the control flow of the caller. Macros can be defined in two ways:

    1. Using the macro keyword directly in the script.
    2. Using the Java API via addMacro or addOrReplaceMacro.
            // Define via Java API
            Express4Runner express4Runner = new Express4Runner(InitOptions.DEFAULT_OPTIONS);
            express4Runner.addMacro("rename", "name='haha-'+name");
            Map<String, Object> context = Collections.singletonMap("name", "wuli");
            Object result = express4Runner.execute("rename", context, QLOptions.DEFAULT_OPTIONS).getResult();
            assertEquals("haha-wuli", result);