You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Expression Language能否在对象图中搜索匹配节点并返回单个/全部结果?

在SpEL中实现对象图全局搜索的方案

针对你的问题:SpEL本身没有原生的递归遍历对象图的语法,但可以通过自定义SpEL函数的方式,实现全局搜索第一个匹配节点或所有匹配节点的需求,以下是具体实现方案(基于Spring Boot 3.0.11对应Spring 6.0.x版本):


1. 实现全局搜索并返回第一个匹配节点

通过编写递归查找属性的工具方法,将其注册为SpEL自定义函数,即可在表达式中调用实现全局搜索。

步骤1:编写属性查找工具类

import java.lang.reflect.Field;
import java.util.Iterable;

public class SpelPropertySearcher {
    // 递归查找第一个匹配的属性值
    public static Object findFirst(Object root, String propertyName) throws IllegalAccessException {
        if (root == null) return null;
        
        // 遍历当前对象的所有字段
        Field[] fields = root.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            // 匹配目标属性,直接返回值
            if (field.getName().equals(propertyName)) {
                return field.get(root);
            }
            
            Object fieldValue = field.get(root);
            // 跳过基本类型、字符串,递归处理引用类型
            if (fieldValue != null && !field.getType().isPrimitive() && !field.getType().equals(String.class)) {
                // 处理集合类,遍历每个元素递归查找
                if (fieldValue instanceof Iterable<?>) {
                    for (Object element : (Iterable<?>) fieldValue) {
                        Object result = findFirst(element, propertyName);
                        if (result != null) return result;
                    }
                } else {
                    // 处理单个引用对象,递归查找
                    Object result = findFirst(fieldValue, propertyName);
                    if (result != null) return result;
                }
            }
        }
        return null;
    }
}

步骤2:在SpEL中注册并使用函数

BookingEvent event = getEvent();
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();

// 注册自定义查找函数
context.registerFunction(
    "findFirst", 
    SpelPropertySearcher.class.getMethod("findFirst", Object.class, String.class)
);

// 搜索整个event对象中的第一个employeeId
Expression exp = parser.parseExpression("#findFirst(this, 'employeeId')");
String firstEmployeeId = (String) exp.getValue(event, context);
Assertions.assertEquals("EM1", firstEmployeeId);

// 仅在metadata下搜索第一个employeeId
exp = parser.parseExpression("#findFirst(metadata, 'employeeId')");
firstEmployeeId = (String) exp.getValue(event, context);

2. 返回所有匹配的属性值

同样通过自定义函数,修改工具方法为收集所有匹配结果:

步骤1:扩展工具类的全量查找方法

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Iterable;
import java.util.List;

public class SpelPropertySearcher {
    // 递归查找所有匹配的属性值
    public static List<Object> findAll(Object root, String propertyName) throws IllegalAccessException {
        List<Object> results = new ArrayList<>();
        if (root == null) return results;
        
        Field[] fields = root.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            if (field.getName().equals(propertyName)) {
                results.add(field.get(root));
            }
            
            Object fieldValue = field.get(root);
            if (fieldValue != null && !field.getType().isPrimitive() && !field.getType().equals(String.class)) {
                if (fieldValue instanceof Iterable<?>) {
                    for (Object element : (Iterable<?>) fieldValue) {
                        results.addAll(findAll(element, propertyName));
                    }
                } else {
                    results.addAll(findAll(fieldValue, propertyName));
                }
            }
        }
        return results;
    }
}

步骤2:注册并调用全量查找函数

// 注册全量查找函数
context.registerFunction(
    "findAll", 
    SpelPropertySearcher.class.getMethod("findAll", Object.class, String.class)
);

// 搜索整个event对象中的所有employeeId
Expression exp = parser.parseExpression("#findAll(this, 'employeeId')");
List<String> allEmployeeIds = (List<String>) exp.getValue(event, context);
Assertions.assertTrue(allEmployeeIds.contains("EM1"));

3. 注意事项

  • 上述工具类基于字段反射实现,如果需要遵循JavaBean规范(通过getter方法访问属性),可以修改为遍历类的Method,匹配对应getter后调用。
  • 可扩展逻辑支持数组、Map等类型的遍历,覆盖更多场景。
  • 如果需要在Spring Data JPA的@Query中全局使用这些函数,可以通过EvaluationContextExtension注册为全局扩展,无需每次手动注册。

内容的提问来源于stack exchange,提问作者tuk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 20:49:58