From 78427b0ef2a5dcbe1bccfc224b1ec75868f4955e Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Fri, 21 Feb 2025 15:01:40 +0100 Subject: [PATCH] GH-3085 Add Number and Boolean Spel converter Refactor SpelConverter into an abstract class Resolves #3085 --- .../SpelExpressionConverterConfiguration.java | 46 +++++++++++++------ ...ExpressionConverterConfigurationTests.java | 31 ++++++++++++- 2 files changed, 60 insertions(+), 17 deletions(-) diff --git a/core/spring-cloud-stream/src/main/java/org/springframework/cloud/stream/config/SpelExpressionConverterConfiguration.java b/core/spring-cloud-stream/src/main/java/org/springframework/cloud/stream/config/SpelExpressionConverterConfiguration.java index 9be6aa748..9b8f3b8da 100644 --- a/core/spring-cloud-stream/src/main/java/org/springframework/cloud/stream/config/SpelExpressionConverterConfiguration.java +++ b/core/spring-cloud-stream/src/main/java/org/springframework/cloud/stream/config/SpelExpressionConverterConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2018 the original author or authors. + * Copyright 2015-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,8 +16,6 @@ package org.springframework.cloud.stream.config; -import java.beans.Introspector; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.config.BeanDefinition; @@ -44,6 +42,7 @@ import org.springframework.integration.json.JsonPropertyAccessor; * * @author Eric Bottard * @author Artem Bilan + * @author Oleg Zhurakousky */ @Configuration(proxyBeanMethods = false) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) @@ -61,19 +60,19 @@ public class SpelExpressionConverterConfiguration { @Bean public static SpelPropertyAccessorRegistrar spelPropertyAccessorRegistrar() { return new SpelPropertyAccessorRegistrar() - .add(Introspector - .decapitalize(JsonPropertyAccessor.class.getSimpleName()), - new JsonPropertyAccessor()); + .add(new JsonPropertyAccessor()); } @Bean @ConfigurationPropertiesBinding @IntegrationConverter - public Converter spelConverter(ConfigurableApplicationContext context) { + public Converter spelConverter(ConfigurableApplicationContext context) { SpelConverter converter = new SpelConverter(); ConfigurableConversionService cs = (ConfigurableConversionService) context.getBeanFactory().getConversionService(); if (cs != null) { cs.addConverter(converter); + cs.addConverter(new NumberToStringSpelConverter()); + cs.addConverter(new BooleanToStringSpelConverter()); } return converter; } @@ -83,7 +82,30 @@ public class SpelExpressionConverterConfiguration { * * @author Eric Bottard */ - public static class SpelConverter implements Converter { + public static class SpelConverter extends AbstractSpelConverter { + @Override + public Expression convert(String source) { + return this.doConvert(source); + } + } + + public static class NumberToStringSpelConverter extends AbstractSpelConverter { + @Override + public Expression convert(Number source) { + String value = source.toString(); + return this.doConvert(value); + } + } + + public static class BooleanToStringSpelConverter extends AbstractSpelConverter { + @Override + public Expression convert(Boolean source) { + String value = source.toString(); + return this.doConvert(value); + } + } + + public static abstract class AbstractSpelConverter implements Converter { private SpelExpressionParser parser = new SpelExpressionParser(); @@ -92,12 +114,8 @@ public class SpelExpressionConverterConfiguration { @Lazy private EvaluationContext evaluationContext; - @Override - public Expression convert(Object source) { + public Expression doConvert(String source) { try { - if (!(source instanceof String)) { - source = String.valueOf(source); // see https://github.com/spring-cloud/spring-cloud-stream/issues/2989 - } Expression expression = this.parser.parseExpression((String) source); if (expression instanceof SpelExpression) { ((SpelExpression) expression) @@ -110,7 +128,5 @@ public class SpelExpressionConverterConfiguration { "Could not convert '%s' into a SpEL expression", source), e); } } - } - } diff --git a/core/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/config/SpelExpressionConverterConfigurationTests.java b/core/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/config/SpelExpressionConverterConfigurationTests.java index 235092e2d..b7d63b375 100644 --- a/core/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/config/SpelExpressionConverterConfigurationTests.java +++ b/core/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/config/SpelExpressionConverterConfigurationTests.java @@ -31,6 +31,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.PropertyAccessor; @@ -47,8 +48,7 @@ import static org.assertj.core.api.Assertions.assertThat; * @author Artem Bilan * @author Soby Chacko */ -@SpringBootTest(classes = SpelExpressionConverterConfigurationTests.Config.class, properties = { - "expression: a.b" }) +@SpringBootTest(classes = SpelExpressionConverterConfigurationTests.Config.class) class SpelExpressionConverterConfigurationTests { @Autowired @@ -77,6 +77,12 @@ class SpelExpressionConverterConfigurationTests { assertThat(propertyAccessors) .hasAtLeastOneElementOfType(JsonPropertyAccessor.class); + + Expression numberExpression = this.pojo.getNumberExpression(); + assertThat(numberExpression.getValue()).isEqualTo(5); + + Expression booleanExpression = this.pojo.getBooleanExpression(); + assertThat(booleanExpression.getValue()).isEqualTo(true); } @ConfigurationProperties @@ -84,6 +90,10 @@ class SpelExpressionConverterConfigurationTests { private Expression expression; + private Expression numberExpression; + + private Expression booleanExpression; + public Expression getExpression() { return this.expression; } @@ -92,11 +102,28 @@ class SpelExpressionConverterConfigurationTests { this.expression = expression; } + public Expression getNumberExpression() { + return numberExpression; + } + + public void setNumberExpression(Expression numberExpression) { + this.numberExpression = numberExpression; + } + + public Expression getBooleanExpression() { + return booleanExpression; + } + + public void setBooleanExpression(Expression booleanExpression) { + this.booleanExpression = booleanExpression; + } + } @Configuration @EnableAutoConfiguration @EnableConfigurationProperties(Pojo.class) + @PropertySource("classpath:/application.yml") public static class Config implements BeanFactoryAware { private BeanFactory beanFactory;