Backend Development 17 min read

Dynamic Parameter Handling with Spring SpEL and Strategy Pattern

The article demonstrates replacing fragile if‑else channel logic with a Strategy pattern and Spring Expression Language, storing parameter mappings in a database so that new payment channels or Excel formats can be added simply by configuring SpEL expressions, achieving a flexible, maintainable, data‑driven solution.

DeWu Technology
DeWu Technology
DeWu Technology
Dynamic Parameter Handling with Spring SpEL and Strategy Pattern

This article presents a design for a funds management platform that needs to download and process channel‑specific billing files. Different payment channels require different request parameters, leading to a challenge in designing a flexible and maintainable solution.

Problem Statement : The naive approach uses a series of if‑else statements to assemble channel‑specific request objects, which quickly becomes hard to maintain and violates the Open‑Closed Principle.

Solution 1 – Simple if‑else :

/**
 * 资金系统请求支付系统下载渠道账单
 * @param instCode 渠道名
 * @param instAccountNo 账户
 * @return 同步结果
 */
public String applyFileBill(String instCode, String instAccountNo) {
    // 不同渠道入参组装
    FileBillReqDTO channelReq = new FileBillReqDTO();
    if ("支付宝".equals(instCode)) {
        channelReq.setBusinessCode("ALIPAY_" + instAccountNo + "_BUSINESS");
        channelReq.setPayTool(4);
        channelReq.setTransType(50);
    } else if ("微信".equals(instCode)) {
        channelReq.setBusinessCode("WX_" + instAccountNo);
        channelReq.setPayTool(3);
        channelReq.setTransType(13);
    } else if ("通联".equals(instCode)) {
        channelReq.setBusinessCode("TL_" + instAccountNo);
        channelReq.setPayTool(5);
        channelReq.setTransType(13);
    }
    // ... 可以继续添加其他渠道的处理逻辑
    BaseResult
result = cnRegionDataFetcher.applyFileBill(channelReq, "资金账单下载");
    return "处理中";
}

Drawbacks: each new channel requires code changes, leading to code bloat and high maintenance cost.

Solution 2 – Strategy Pattern :

// Strategy interface
public interface IChannelApplyFileStrategy {
    /** 渠道匹配策略 */
    boolean match(String instCode);
    /** 入参组装 */
    FileBillReqDTO assembleReqData(String instAccountNo);
}

// Alipay implementation
@Component
public class AlipayChannelApplyFileStrategy implements IChannelApplyFileStrategy {
    @Override
    public boolean match(String instCode) { return "支付宝".equals(instCode); }
    @Override
    public FileBillReqDTO assembleReqData(String instAccountNo) {
        FileBillReqDTO channelReq = new FileBillReqDTO();
        channelReq.setBusinessCode("ALIPAY_" + instAccountNo + "_BUSINESS");
        channelReq.setPayTool(4);
        channelReq.setTransType(50);
        return channelReq;
    }
}

// Wechat implementation
@Component
public class WechatChannelApplyFileStrategy implements IChannelApplyFileStrategy {
    @Override
    public boolean match(String instCode) { return "微信".equals(instCode); }
    @Override
    public FileBillReqDTO assembleReqData(String instAccountNo) {
        FileBillReqDTO channelReq = new FileBillReqDTO();
        channelReq.setBusinessCode("WX_" + instAccountNo);
        channelReq.setPayTool(3);
        channelReq.setTransType(13);
        return channelReq;
    }
}

// Client that selects strategy at runtime
@Component
public class ChannelApplyFileClient {
    @Resource
    private List
iChannelApplyFileStrategies;
    @Resource
    private CNRegionDataFetcher cnRegionDataFetcher;
    public String applyFileBill(String instCode, String instAccountNo) {
        IChannelApplyFileStrategy strategy = iChannelApplyFileStrategies.stream()
            .filter(s -> s.match(instCode)).findFirst().orElse(null);
        FileBillReqDTO channelReq = strategy.assembleReqData(instAccountNo);
        BaseResult
result = cnRegionDataFetcher.applyFileBill(channelReq, "资金账单下载");
        return "处理中";
    }
}

This approach adheres to the Open‑Closed Principle: adding a new channel only requires a new strategy class.

Introducing Spring Expression Language (SpEL)

To further decouple parameter configuration, the article proposes storing channel‑parameter mappings in a database and using SpEL to evaluate expressions at runtime. This enables dynamic, configurable handling without code changes.

Key SpEL concepts covered:

Expression parsing and evaluation

Evaluation context with variables and custom functions

Root object and property access

Simple SpEL example:

public String spELSample(int number) {
    // Create parser
    ExpressionParser parser = new SpelExpressionParser();
    String expressionStr = "#number > 10 ? 'true' : 'false'";
    Expression expression = parser.parseExpression(expressionStr);
    // Set variable
    StandardEvaluationContext context = new StandardEvaluationContext();
    context.setVariable("number", number);
    // Evaluate
    return expression.getValue(context, String.class);
}

Dynamic Parameter Service

@Slf4j
@Service
@CacheConfig(cacheNames = CacheNames.EXPRESSION)
public class ExpressionUtil {
    private final ExpressionParser expressionParser = new SpelExpressionParser();
    public StandardEvaluationContext createContext(String instAccountNo){
        StandardEvaluationContext context = new StandardEvaluationContext();
        context.setVariable("instAccountNo", instAccountNo);
        this.registryFunction(context);
        return context;
    }
    private void registryFunction(StandardEvaluationContext context) {
        try {
            context.addPropertyAccessor(new MapAccessor());
            context.registerFunction("yuanToCent", ExpressionHelper.class.getDeclaredMethod("yuanToCent", String.class));
            context.registerFunction("substringBefore", StringUtils.class.getDeclaredMethod("substringBefore", String.class, String.class));
        } catch (Exception e) { log.info("SpEL函数注册失败:", e); }
    }
    @Cacheable(key = "'getExpressionWithCache:'+#cacheKey", unless = "#result == null")
    public Expression getExpressionWithCache(String cacheKey, String expressionString) {
        try { return expressionParser.parseExpression(expressionString); }
        catch (Exception e) { log.error("SpEL表达式解析异常,表达式:[{}]", expressionString, e); throw new BizException(ReturnCode.EXCEPTION.getCode(), String.format("SpEL表达式解析异常:[%s]", expressionString), e); }
    }
}

@Service
public class ExpressionService {
    @Resource
    private ExpressionUtil expressionUtil;
    public FileBillReqDTO transform(ChannelEntity channel, String instAccountNo) throws Exception {
        StandardEvaluationContext context = expressionUtil.createContext(instAccountNo);
        FileBillReqDTO target = ClassHelper.newInstance(FileBillReqDTO.class);
        for (ChannelApiEntity api : channel.getApis()) {
            Field field = ReflectionUtils.findField(FileBillReqDTO.class, api.getFieldCode());
            String expressionString = api.getFieldExpression();
            Expression expression = expressionUtil.getExpressionWithCache(api.fieldExpressionKey(), expressionString);
            Object value = expression.getValue(context, FileBillReqDTO.class);
            field.setAccessible(true);
            field.set(target, value);
        }
        return target;
    }
}

@Component
public class ChannelApplyFileClient {
    @Resource
    private CNRegionDataFetcher cnRegionDataFetcher;
    @Resource
    private ExpressionService expressionService;
    @Resource
    private ChannelRepository channelRepository;
    public String applyFileBill(String instCode, String instAccountNo) {
        ChannelEntity channel = channelRepository.findByInstCode(instCode);
        FileBillReqDTO channelReq = expressionService.transform(channel, instAccountNo);
        BaseResult
result = cnRegionDataFetcher.applyFileBill(channelReq, "资金账单下载");
        return "处理中";
    }
}

Advantages: the parameter handling logic becomes data‑driven; adding a new channel only requires inserting a row in the configuration table with the appropriate SpEL expression.

Extension – Excel Parsing

The same SpEL‑driven approach can be applied to parse heterogeneous Excel files from different channels. By mapping Excel columns to SpEL expressions stored in a table, the system can dynamically extract and transform data without writing channel‑specific parsers.

Conclusion

Using SpEL together with the Strategy pattern provides a powerful, flexible, and maintainable way to handle dynamic channel parameters in a backend Java system. It reduces code coupling, improves extensibility, and aligns with domain‑driven design principles.

backendJavaStrategy PatternSpringdynamic configurationSPEL
DeWu Technology
Written by

DeWu Technology

A platform for sharing and discussing tech knowledge, guiding you toward the cloud of technology.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.