spring el 增强

26 阅读5分钟

一、常用函数实例实现

java

复制

下载

// 数学函数类
public class MathFunctions {
    public static double round(double value, int precision) {
        double scale = Math.pow(10, precision);
        return Math.round(value * scale) / scale;
    }
    
    public static double percentage(double value) {
        return value * 100;
    }
    
    public static double toRadians(double degrees) {
        return Math.toRadians(degrees);
    }
}

// 日期函数类
public class DateFunctions {
    public static String formatDate(Date date, String pattern) {
        return new SimpleDateFormat(pattern).format(date);
    }
    
    public static Date addDays(Date date, int days) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.add(Calendar.DATE, days);
        return cal.getTime();
    }
    
    public static long daysBetween(Date start, Date end) {
        return TimeUnit.DAYS.convert(
            end.getTime() - start.getTime(), 
            TimeUnit.MILLISECONDS
        );
    }
    
    public static boolean isWeekend(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int day = cal.get(Calendar.DAY_OF_WEEK);
        return day == Calendar.SATURDAY || day == Calendar.SUNDAY;
    }
}

二、Spring集成实现

java

复制

下载

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.SpelCompilerMode;
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Configuration
public class ExpressionConfig {

    // 表达式解析器Bean(线程安全)
    @Bean
    public ExpressionParser spelExpressionParser() {
        return new SpelExpressionParser(
            new SpelParserConfiguration(
                SpelCompilerMode.IMMEDIATE, 
                getClass().getClassLoader()
            )
        );
    }

    // 表达式缓存Bean
    @Bean
    public Map<String, Expression> expressionCache() {
        return new ConcurrentHashMap<>();
    }

    // 提取器Bean
    @Bean
    public ExpressionExtractor expressionExtractor(
            @Autowired ExpressionParser parser,
            @Autowired Map<String, Expression> cache) {
        return new ExpressionExtractor(parser, cache);
    }
}

@Service
public class ExpressionExtractor {
    
    private final ExpressionParser parser;
    private final Map<String, Expression> expressionCache;
    
    @Autowired
    public ExpressionExtractor(
            ExpressionParser parser, 
            Map<String, Expression> expressionCache) {
        this.parser = parser;
        this.expressionCache = expressionCache;
    }

    /**
     * 执行取值操作
     * @param data 原始数据
     * @param elExpress SpEL表达式
     * @param resultCode 结果标识码
     * @return 提取结果
     */
    public Map<String, Object> extract(
            Map<String, Object> data, 
            String elExpress, 
            String resultCode) {
        
        try {
            Object value = evaluateExpression(data, elExpress);
            return Collections.singletonMap(resultCode, value);
        } catch (Exception e) {
            return errorResult(resultCode, "表达式解析失败: " + e.getMessage());
        }
    }

    private Object evaluateExpression(Map<String, Object> data, String expression) {
        Expression expr = expressionCache.computeIfAbsent(expression, parser::parseExpression);
        StandardEvaluationContext context = createEvaluationContext(data);
        return expr.getValue(context);
    }

    private StandardEvaluationContext createEvaluationContext(Map<String, Object> data) {
        StandardEvaluationContext context = new StandardEvaluationContext(data);
        context.setRootObject(data);
        registerFunctions(context);
        return context;
    }

    private void registerFunctions(StandardEvaluationContext context) {
        // 注册数学函数
        registerMathFunctions(context);
        // 注册日期函数
        registerDateFunctions(context);
        // 注册工具函数
        registerUtilityFunctions(context);
    }

    private void registerMathFunctions(StandardEvaluationContext context) {
        try {
            context.registerFunction("mathRound", 
                MathFunctions.class.getDeclaredMethod("round", double.class, int.class));
            context.registerFunction("mathPercentage", 
                MathFunctions.class.getDeclaredMethod("percentage", double.class));
            context.registerFunction("mathToRadians", 
                MathFunctions.class.getDeclaredMethod("toRadians", double.class));
        } catch (Exception e) {
            throw new RuntimeException("数学函数注册失败", e);
        }
    }

    private void registerDateFunctions(StandardEvaluationContext context) {
        try {
            context.registerFunction("dateFormat", 
                DateFunctions.class.getDeclaredMethod("formatDate", Date.class, String.class));
            context.registerFunction("dateAddDays", 
                DateFunctions.class.getDeclaredMethod("addDays", Date.class, int.class));
            context.registerFunction("dateDaysBetween", 
                DateFunctions.class.getDeclaredMethod("daysBetween", Date.class, Date.class));
            context.registerFunction("dateIsWeekend", 
                DateFunctions.class.getDeclaredMethod("isWeekend", Date.class));
        } catch (Exception e) {
            throw new RuntimeException("日期函数注册失败", e);
        }
    }

    private void registerUtilityFunctions(StandardEvaluationContext context) {
        try {
            context.registerFunction("size", 
                ExpressionUtils.class.getDeclaredMethod("size", Object.class));
            context.registerFunction("empty", 
                ExpressionUtils.class.getDeclaredMethod("empty", Object.class));
            context.registerFunction("coalesce", 
                ExpressionUtils.class.getDeclaredMethod("coalesce", Object[].class));
        } catch (Exception e) {
            throw new RuntimeException("工具函数注册失败", e);
        }
    }

    private Map<String, Object> errorResult(String resultCode, String message) {
        Map<String, Object> result = new HashMap<>();
        result.put(resultCode, null);
        result.put("error", message);
        return result;
    }
}

// 工具函数类
public class ExpressionUtils {
    public static int size(Object obj) {
        if (obj == null) return 0;
        if (obj instanceof Map) return ((Map<?, ?>) obj).size();
        if (obj instanceof Iterable) return ((Iterable<?>) obj).spliterator().getExactSizeIfKnown();
        if (obj.getClass().isArray()) return java.lang.reflect.Array.getLength(obj);
        return 0;
    }
    
    public static boolean empty(Object obj) {
        return size(obj) == 0;
    }
    
    public static Object coalesce(Object... values) {
        for (Object value : values) {
            if (value != null) return value;
        }
        return null;
    }
}

三、设计模式应用

1. 工厂方法模式 - 创建不同安全级别的上下文

java

复制

下载

public interface EvaluationContextFactory {
    StandardEvaluationContext createContext(Map<String, Object> data);
}

// 完全权限上下文工厂
@Service
@Profile("dev") // 仅开发环境使用
public class FullAccessContextFactory implements EvaluationContextFactory {
    @Override
    public StandardEvaluationContext createContext(Map<String, Object> data) {
        StandardEvaluationContext context = new StandardEvaluationContext(data);
        context.setRootObject(data);
        return context;
    }
}

// 安全限制上下文工厂
@Service
@Profile("!dev") // 非开发环境使用
public class RestrictedContextFactory implements EvaluationContextFactory {
    @Override
    public StandardEvaluationContext createContext(Map<String, Object> data) {
        StandardEvaluationContext context = new StandardEvaluationContext(data);
        // 禁用危险操作
        context.setPropertyAccessors(Collections.singletonList(
            new ReflectivePropertyAccessor() {
                @Override
                public boolean canRead(EvaluationContext context, Object target, String name) {
                    // 禁止访问以"system"开头的属性
                    return !name.startsWith("system") && super.canRead(context, target, name);
                }
            }
        ));
        return context;
    }
}

// 在ExpressionExtractor中使用工厂
@Service
public class ExpressionExtractor {
    
    private final EvaluationContextFactory contextFactory;
    
    @Autowired
    public ExpressionExtractor(
            ExpressionParser parser, 
            Map<String, Expression> cache,
            EvaluationContextFactory contextFactory) {
        // ...
        this.contextFactory = contextFactory;
    }
    
    private StandardEvaluationContext createEvaluationContext(Map<String, Object> data) {
        StandardEvaluationContext context = contextFactory.createContext(data);
        registerFunctions(context);
        return context;
    }
}

2. 策略模式 - 表达式执行策略

java

复制

下载

public interface ExpressionExecutionStrategy {
    Object execute(Expression expression, StandardEvaluationContext context);
}

// 直接执行策略
@Service
public class DirectExecutionStrategy implements ExpressionExecutionStrategy {
    @Override
    public Object execute(Expression expression, StandardEvaluationContext context) {
        return expression.getValue(context);
    }
}

// 安全执行策略(添加审计日志)
@Service
public class AuditedExecutionStrategy implements ExpressionExecutionStrategy {
    private static final Logger logger = LoggerFactory.getLogger(AuditedExecutionStrategy.class);
    
    @Override
    public Object execute(Expression expression, StandardEvaluationContext context) {
        String expressionString = expression.getExpressionString();
        long startTime = System.currentTimeMillis();
        
        try {
            Object result = expression.getValue(context);
            long duration = System.currentTimeMillis() - startTime;
            
            logger.info("表达式执行成功: {} | 耗时: {}ms", expressionString, duration);
            return result;
        } catch (Exception e) {
            logger.error("表达式执行失败: {} | 错误: {}", expressionString, e.getMessage());
            throw e;
        }
    }
}

// 在ExpressionExtractor中使用策略
@Service
public class ExpressionExtractor {
    
    private final ExpressionExecutionStrategy executionStrategy;
    
    @Autowired
    public ExpressionExtractor(
            // ... 其他依赖
            ExpressionExecutionStrategy executionStrategy) {
        // ...
        this.executionStrategy = executionStrategy;
    }
    
    private Object evaluateExpression(Map<String, Object> data, String expression) {
        Expression expr = expressionCache.computeIfAbsent(expression, parser::parseExpression);
        StandardEvaluationContext context = createEvaluationContext(data);
        return executionStrategy.execute(expr, context);
    }
}

3. 装饰器模式 - 添加缓存装饰器

java

复制

下载

public class CachedExpressionDecorator implements Expression {
    private final Expression target;
    private final Map<EvaluationContext, Object> cache = new ConcurrentHashMap<>();

    public CachedExpressionDecorator(Expression target) {
        this.target = target;
    }

    @Override
    public Object getValue(EvaluationContext context) throws EvaluationException {
        return cache.computeIfAbsent(context, ctx -> target.getValue(ctx));
    }

    // 其他方法委托给target...
}

// 在ExpressionExtractor中应用装饰器
@Service
public class ExpressionExtractor {
    private Object evaluateExpression(Map<String, Object> data, String expression) {
        Expression expr = expressionCache.computeIfAbsent(expression, exp -> 
            new CachedExpressionDecorator(parser.parseExpression(exp))
        );
        // ...
    }
}

四、完整使用示例

java

复制

下载

@Service
public class DataProcessingService {
    
    @Autowired
    private ExpressionExtractor extractor;
    
    public void processResponse(Map<String, Object> apiResponse) {
        // 1. 基本取值
        Map<String, Object> cityResult = extractor.extract(
            apiResponse, 
            "#root['user']['address']['city']", 
            "CITY_CODE"
        );
        
        // 2. 数学函数
        Map<String, Object> discountResult = extractor.extract(
            apiResponse,
            "#mathRound(#root['price'] * 0.85, 2)", 
            "DISCOUNT_PRICE"
        );
        
        // 3. 日期函数
        Map<String, Object> deliveryResult = extractor.extract(
            apiResponse,
            "#dateAddDays(T(java.util.Date).from(java.time.Instant.now()), 3)", 
            "DELIVERY_DATE"
        );
        
        // 4. 复杂逻辑
        Map<String, Object> statusResult = extractor.extract(
            apiResponse,
            "#root['orderStatus'] == 'PAID' && #root['inventory'] > 0 ? 'READY_TO_SHIP' : 'PENDING'", 
            "SHIPPING_STATUS"
        );
        
        // 5. 安全函数
        Map<String, Object> safeResult = extractor.extract(
            apiResponse,
            "#safeEscapeHtml(#root['userComment'])", 
            "SAFE_COMMENT"
        );
    }
}

五、设计模式优势总结

  1. 工厂方法模式

    • 根据不同环境创建不同安全级别的上下文
    • 符合开闭原则,易于扩展新的上下文类型
    • 通过Spring Profile实现环境隔离
  2. 策略模式

    • 灵活切换表达式执行策略
    • 支持添加审计、监控等横切关注点
    • 符合单一职责原则
  3. 装饰器模式

    • 为表达式添加缓存功能而不修改原有逻辑
    • 提高重复表达式的执行效率
    • 透明地增强对象功能
  4. 模板方法模式(隐含在流程中):

    • 固定表达式处理流程(解析→上下文准备→执行→结果处理)
    • 允许子步骤自由变化
  5. 依赖注入

    • 通过Spring容器管理组件生命周期
    • 解耦组件依赖关系
    • 支持配置化切换实现

六、安全增强建议

  1. 表达式白名单机制

java

复制

下载

@Service
public class ExpressionValidator {
    private final Set<String> allowedPatterns = Set.of(
        "user\..*", 
        "order\..*", 
        "product\..*"
    );

    public boolean validate(String expression) {
        return allowedPatterns.stream()
            .anyMatch(pattern -> expression.matches(pattern));
    }
}

// 在ExpressionExtractor中使用
public Map<String, Object> extract(...) {
    if (!validator.validate(expression)) {
        return errorResult(resultCode, "表达式未授权");
    }
    // ...
}
  1. 执行时间限制

java

复制

下载

public class TimeLimitedExecutionStrategy implements ExpressionExecutionStrategy {
    @Override
    public Object execute(Expression expression, StandardEvaluationContext context) {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<Object> future = executor.submit(() -> expression.getValue(context));
        
        try {
            return future.get(500, TimeUnit.MILLISECONDS);
        } catch (TimeoutException e) {
            future.cancel(true);
            throw new EvaluationException("表达式执行超时");
        } catch (Exception e) {
            throw new EvaluationException("执行错误: " + e.getMessage());
        } finally {
            executor.shutdownNow();
        }
    }
}

这个完整方案提供了:

  1. 丰富的数学/日期函数实例
  2. Spring容器集成管理
  3. 多种设计模式应用
  4. 生产级安全防护
  5. 灵活的扩展点

系统可根据实际需求选择不同的上下文工厂和执行策略,平衡功能与安全性,满足各种复杂场景下的表达式解析需求。