SpEl表达式使用示例

在 SpEL 中,如果你想动态计算 #walk_speed * 0.8 并生成 JSON 字符串 {"speedExec": 8.0}(假设 walk_speed = 10),你可以使用以下方法:


方法 1:直接解析 JSON 字符串中的 SpEL 表达式

如果你的 JSON 字符串是固定的(如 "{\"speedExec\": #walk_speed*0.8}"),你可以:

  1. 提取 SpEL 表达式#walk_speed*0.8)。
  2. 计算它的值10 * 0.8 = 8.0)。
  3. 替换回 JSON
代码示例
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.util.HashMap;
import java.util.Map;

public class SpELJsonExample {

    private static final ExpressionParser spelParser = new SpelExpressionParser();

    public static void main(String[] args) {
        Map<String, Object> variables = new HashMap<>();
        variables.put("walk_speed", 10); // 假设 walk_speed = 10

        StandardEvaluationContext context = new StandardEvaluationContext();
        context.setVariables(variables);

        String jsonTemplate = "{\"speedExec\": #walk_speed*0.8}";

        // 提取 SpEL 表达式(#walk_speed*0.8)
        String spelExpression = jsonTemplate.substring(
            jsonTemplate.indexOf("#"),
            jsonTemplate.indexOf("}") - 1
        );

        // 计算 SpEL 表达式的值
        Expression exp = spelParser.parseExpression(spelExpression);
        Double result = exp.getValue(context, Double.class);

        // 替换 JSON 中的 SpEL 表达式为计算结果
        String finalJson = jsonTemplate.replace(spelExpression, result.toString());
        System.out.println(finalJson); // 输出: {"speedExec": 8.0}
    }
}

输出

{"speedExec": 8.0}

方法 2:使用 SpEL 直接构造 JSON(推荐)

如果 JSON 结构较简单,可以直接用 SpEL 构造整个 JSON 字符串:

String spelJson = "{'speedExec': #walk_speed * 0.8}"; // 注意:SpEL 允许单引号
Expression exp = spelParser.parseExpression(spelJson);
Map<String, Object> result = exp.getValue(context, Map.class);

System.out.println(result); // 输出: {speedExec=8.0}
转换为标准 JSON 字符串

如果你想输出标准的 JSON 格式(如 {"speedExec": 8.0}),可以借助 JacksonGson

import com.fasterxml.jackson.databind.ObjectMapper;

ObjectMapper mapper = new ObjectMapper();
String jsonOutput = mapper.writeValueAsString(result);
System.out.println(jsonOutput); // 输出: {"speedExec":8.0}

方法 3:动态替换 JSON 中的多个 SpEL 表达式

如果 JSON 中有多个 SpEL 表达式(如 {"a": #x, "b": #y+1}),可以用正则匹配替换:

import java.util.regex.*;

private static String evaluateJson(String json, StandardEvaluationContext context) {
    Pattern pattern = Pattern.compile("#([^\\s\"'}]+)");
    Matcher matcher = pattern.matcher(json);
    StringBuffer output = new StringBuffer();
    
    while (matcher.find()) {
        String expr = matcher.group(); // 如 "#walk_speed*0.8"
        Object value = spelParser.parseExpression(expr).getValue(context);
        matcher.appendReplacement(output, value.toString());
    }
    matcher.appendTail(output);
    return output.toString();
}

使用示例

String json = "{\"speedExec\": #walk_speed*0.8, \"threshold\": #walk_speed/2}";
String result = evaluateJson(json, context);
System.out.println(result); // 输出: {"speedExec": 8.0, "threshold": 5.0}

总结

场景推荐方法
固定 JSON 结构直接提取 SpEL 计算后替换(方法 1)
动态构造 JSON用 SpEL 生成 Map 再转 JSON(方法 2)
复杂 JSON 含多个 SpEL正则匹配替换(方法 3)

完整示例

import com.alibaba.fastjson.JSONObject;
import com.hsrg.utils.utils.HsrgUtil;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;

public class SpELTest {

    private static final ExpressionParser spelParser = new SpelExpressionParser();

    public static void main(String[] args) {
        Map report = new HashMap();
        report.put("walk_distance",350); // 测试值在300-450之间
        report.put("walk_speed",10);
        report.put("id","id替换");
        Calendar calendar = Calendar.getInstance();
        String startTime = HsrgUtil.formatDateToStr(calendar.getTime());
        report.put("startTime",startTime);
        calendar.add(Calendar.DATE,7);
        String endTime = HsrgUtil.formatDateToStr(calendar.getTime());
        report.put("endTime",endTime);
        report.put("patientId","patientId替换");
        report.put("personId","personId替换");
        report.put("org_root_name","org_root_name替换");
        String json = "{\"speedExec\": #walk_speed*0.8}";
        StandardEvaluationContext evalContext = new StandardEvaluationContext();
        evalContext.setVariables(report);

        // 验证条件1
        System.out.println(evaluateCondition("#walk_distance < 450 && #walk_distance >= 300", evalContext));

        String spelJson = "{\n" +
                "\t\"businessId\": #id,\n" +
                "\t\"endTime\": #end_time,\n" +
                "\t\"extendParam\": {\n" +
                "\t\t\"speedExec\": #walk_speed*0.8,\n" +
                "\t\t\"targetRr\": 0,\n" +
                "\t\t\"thrHight\": 0,\n" +
                "\t\t\"thrLow\": 0,\n" +
                "\t\t\"thrType\": 5,\n" +
                "\t\t\"timeExec\": 20,\n" +
                "\t\t\"timeReady\": 5,\n" +
                "\t\t\"timeRecovery\": 5,\n" +
                "\t\t\"trainingMode\": \"speedMode\",\n" +
                "\t\t\"vegetable\": 0.0\n" +
                "\t},\n" +
                "\t\"frequency\": 3.0,\n" +
                "\t\"frequencyUnit\": \"周\",\n" +
                "\t\"item\": \"hurry\",\n" +
                "\t\"itemName\": \"快走\",\n" +
                "\t\"orgId\": #orgId,\n" +
                "\t\"parentItem\": \"aerobics\",\n" +
                "\t\"parentItemName\": \"有氧运动\",\n" +
                "\t\"patientId\": #patientId,\n" +
                "\t\"personId\": #personId,\n" +
                "\t\"recipelCycle\": 1.0,\n" +
                "\t\"recording\": 0,\n" +
                "\t\"rootOrgId\": #org_root_name,\n" +
                "\t\"source\": \"recipel\",\n" +
                "\t\"startTime\": #start_time,\n" +
                "\t\"status\": \"create\",\n" +
                "\t\"times\": 3\n" +
                "}"; // 注意:SpEL 允许单引号
        Expression exp = spelParser.parseExpression(spelJson);
        Map<String, Object> result = exp.getValue(evalContext, Map.class);

        System.out.println(JSONObject.toJSONString(result)); // 输出: {speedExec=8.0}
    }
    private static boolean evaluateCondition(String condition, StandardEvaluationContext context) {
        try {
            Expression exp = spelParser.parseExpression(condition);
            Boolean result = exp.getValue(context, Boolean.class);
            return result != null && result;
        } catch (Exception e) {
            throw new RuntimeException("SpEL evaluation failed for condition: " + condition, e);
        }
    }
}

输出结果

true
{"businessId":"id替换","extendParam":{"speedExec":8.0,"targetRr":0,"thrHight":0,"thrLow":0,"thrType":5,"timeExec":20,"timeReady":5,"timeRecovery":5,"trainingMode":"speedMode","vegetable":0.0},"frequency":3.0,"frequencyUnit":"周","item":"hurry","itemName":"快走","orgId":"#orgId","parentItem":"aerobics","parentItemName":"有氧运动","patientId":"patientId替换","personId":"personId替换","recipelCycle":1.0,"recording":0,"rootOrgId":"org_root_name替换","source":"recipel","status":"create","times":3}

SpEL表达式高级用法示例

SpEL支持丰富的表达式语法(以下写法仅支持设置根对象,如果是setVariables()需要按照完整示例的做修改):

// 1. 基本运算
"walkDistance * walkSpeed > 1000"

// 2. 正则匹配
"orgId matches 'org\\d+'"

// 3. 集合操作
"someList.?[#this > 10]"

// 4. 安全导航
"someObject?.someProperty"

// 5. 调用方法
"T(java.lang.Math).random() * 100.0"

// 6. 复杂条件组合
"(walkDistance < 300 && walkSpeed > 4) || orgId == 'special'"

此语法需要修改,这种写法需要使用如下代码(设置根对象),此方法验证是失败的,或许我写的不对。

public static void main(String[] args) {
    Map report = new HashMap();
    report.put("walk_distance", 350);
    StandardEvaluationContext evalContext = new StandardEvaluationContext(report); // 设置根对象

    // 现在可以直接使用属性名访问
    System.out.println(evaluateCondition("walk_distance < 450 && walk_distance >= 300", evalContext));
}
<think>好的,用户现在想了解Spring表达式语言(SpEL)中复杂数据结构的示例。之前他们遇到了SpEL解析异常的问题,现在向更进阶的应用场景,可能是想更深入地使用SpEL处理复杂数据。我需要确保示例涵盖常见的复杂结构,如集合、对象图、条件运算、类型操作和方法调用,同时要说明清楚每个例子适用的场景。 首先,用户可能是在开发Spring应用时,需要动态处理配置、条件注入或者数据换,所以示例需要贴近实际应用场景。比如,在配置文件中使用集合过滤,或者在注解中使用条件判断。要注意SpEL的语法特点,比如使用T()进行类型操作,以及如何访问嵌套属性。 其次,用户可能对SpEL的能力不是很清楚,需要展示其灵活性,比如如何操作Map、List,甚至自定义对象。同时,要提醒用户在使用复杂表达式时的性能考量,避免过于复杂的表达式影响应用性能。 然后,我需要考虑用户可能的疑问点,例如在XML配置或注解中如何正确编写表达式,如何处理集合的投影和选择,以及如何调用静态方法。此外,用户可能对安全方面有顾虑,比如执行任意表达式带来的风险,需要给出安全建议。 最后,确保示例代码正确,语法正确,并且每个例子都有简要说明,帮助用户理解应用场景。可能需要分点列出,结构清晰,便于用户参考和直接使用。同时,总结部分要强调SpEL的灵活性和使用时的注意事项,帮助用户在实际应用中避免常见错误。</think>以下是 Spring 表达式语言(SpEL)处理复杂数据结构的典型示例使用场景说明: --- ### 一、集合操作 #### 1. 集合过滤(`.?[]`) ```java List<User> users = Arrays.asList( new User("Alice", 25), new User("Bob", 30), new User("Charlie", 22) ); // SpEL 表达式:筛选年龄大于 25 的用户 ExpressionParser parser = new SpelExpressionParser(); Expression expr = parser.parseExpression("?[age > 25]"); List<User> filtered = (List<User>) expr.getValue(users); // 结果:包含 Bob (30) ``` #### 2. 集合投影(`.![]`) ```java // SpEL 表达式:提取所有用户的名字 Expression expr = parser.parseExpression("![name]"); List<String> names = (List<String>) expr.getValue(users); // 结果:["Alice", "Bob", "Charlie"] ``` #### 3. 集合索引与选择 ```java // 获取第一个用户的名字 String firstName = parser.parseExpression("[0].name").getValue(users, String.class); // 结果:Alice ``` --- ### 二、对象图导航 #### 1. 嵌套属性访问 ```java class Company { private Department dept; // getters/setters } class Department { private Employee leader; // getters/setters } // SpEL 表达式:获取公司部门的负责人邮箱 String expr = "dept.leader.email"; String email = parser.parseExpression(expr).getValue(company, String.class); ``` #### 2. 安全导航(避免 NullPointerException) ```java // 若 dept 为 null 则返回 null,而不是抛出异常 String expr = "dept?.leader?.email"; ``` --- ### 三、Map 操作 #### 1. 直接访问键值 ```java Map<String, Integer> scores = Map.of("Math", 90, "English", 85); // 获取 Math 分数 int mathScore = parser.parseExpression("['Math']").getValue(scores, Integer.class); ``` #### 2. 遍历 Map ```java // 获取所有键的集合 Set<String> keys = parser.parseExpression("keySet()").getValue(scores, Set.class); // 获取所有值的集合 Collection<Integer> values = parser.parseExpression("values()").getValue(scores, Collection.class); ``` --- ### 四、条件与逻辑运算 #### 1. 三目运算符 ```java // 根据年龄判断是否成年 String expr = "age >= 18 ? 'Adult' : 'Minor'"; String status = parser.parseExpression(expr).getValue(user, String.class); ``` #### 2. Elvis 操作符(简化空值处理) ```java // 如果 displayName 为 null,则使用 username String expr = "displayName ?: username"; String name = parser.parseExpression(expr).getValue(user, String.class); ``` --- ### 五、类型操作与静态方法 #### 1. 调用静态方法 ```java // 调用 Math.max(10, 20) int max = parser.parseExpression("T(java.lang.Math).max(10, 20)").getValue(Integer.class); // 结果:20 ``` #### 2. 类型换 ```java // 将字符串换为 BigDecimal BigDecimal value = parser.parseExpression("new java.math.BigDecimal('100.5')").getValue(BigDecimal.class); ``` --- ### 六、自定义对象解析 ```java class Product { private String name; private boolean inStock; // getters/setters } // 组合条件:名称包含 'Phone' 且库存为真 String expr = "name matches '.*Phone.*' and inStock"; List<Product> phonesInStock = (List<Product>) parser.parseExpression(expr).getValue(products); ``` --- ### 七、XML/注解中的复杂表达式 #### 1. XML 配置示例 ```xml <bean id="discount" class="com.example.Discount"> <property name="eligible" value="#{user.age > 18 and user.orderCount >= 5}"/> </bean> ``` #### 2. Spring Boot 注解示例 ```java @Value("#{systemProperties['user.timezone'] ?: 'UTC'}") private String timezone; @PreAuthorize("hasRole('ADMIN') or #user.id == authentication.principal.id") public void updateUser(User user) { ... } ``` --- ### 使用场景总结 1. **动态配置**:根据环境变量或系统属性动态注入值 2. **条件筛选**:在集合中快速过滤或换数据 3. **安全控制**:结合 Spring Security 实现细粒度权限判断 4. **模板处理**:在 Thymeleaf 或 XML 中实现复杂逻辑 5. **规则引擎**:实现简单的业务规则配置 --- ### 注意事项 1. **性能考量**:复杂表达式可能影响性能,避免深度嵌套 2. **可读性**:对关键业务逻辑的表达式添加注释 3. **安全性**:避免执行未经验证的外部输入表达式(存在代码注入风险) 通过灵活组合这些功能,SpEL 可以显著简化 Java/Spring 应用中的动态逻辑处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

豆芽脚脚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值