Skip to content

Instantly share code, notes, and snippets.

@thomasdarimont
Last active July 10, 2023 12:19
Show Gist options
  • Save thomasdarimont/7df3f04d3be7acea5dfa to your computer and use it in GitHub Desktop.
Save thomasdarimont/7df3f04d3be7acea5dfa to your computer and use it in GitHub Desktop.
How to use custom SpEL functions in @value with Spring Boot
package demo;
import static java.lang.reflect.Modifier.*;
import java.util.Arrays;
import java.util.Set;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanExpressionResolver;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.expression.StandardBeanExpressionResolver;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
@SpringBootApplication
public class App {
public static void main(String[] args) {
System.setProperty("someProperty", "a,b,c,a");
BusinessComponent bc = SpringApplication.run(App.class, args).getBean(BusinessComponent.class);
System.out.println(bc);
}
static class BusinessComponent {
// Here we use our custom spel function
@Value("#{#csvToSet(systemProperties['someProperty'])}")//
Set<String> values;
@Override
public String toString() {
return "BusinessComponent [values=" + values + "]";
}
}
@Bean
CustomSpelFunctionProvider customSpelFunctionProvider() {
return new CustomSpelFunctionProvider(CustomSpelFunctions.class);
}
@Bean
BusinessComponent businessComponent() {
return new BusinessComponent();
}
public interface CustomSpelFunctions {
// our custom SpEL function
static Set<String> csvToSet(String line) {
return StringUtils.commaDelimitedListToSet(line);
}
}
/**
* A custom {@link BeanFactoryPostProcessor} that allows to register custom SpEL functions into the evaluation
* context.
* <p>
* We customize the {@link BeanExpressionResolver} used by the {@link ConfigurableListableBeanFactory} for the custom
* SpEL function registration.
*/
static class CustomSpelFunctionProvider implements BeanFactoryPostProcessor {
private final Class<?>[] functionHolders;
public CustomSpelFunctionProvider(Class<?>... functionHolders) {
this.functionHolders = functionHolders;
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver() {
@Override
protected void customizeEvaluationContext(StandardEvaluationContext evalContext) {
Arrays.stream(functionHolders).forEach(cls -> ReflectionUtils.doWithMethods(cls, m -> {
ReflectionUtils.makeAccessible(m);
evalContext.registerFunction(m.getName(), m);
}, m -> isPublic(m.getModifiers()) && isStatic(m.getModifiers())));
}
});
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment