Spring Expression Language(SpEL) Jakarta validator.
Apply SpEL to Hibernate @ScriptAssert by creating a new validation annotation @SpELScriptAssert.
Using Spring Expression Language(SpEL) to validate a Java bean.
Bean
@SpELScriptAssert( //
script = "@mathComponent.multiply(#add(a, b), c) == output", //
performIf = "a != null && b != null && c != null", //
helpers = {MathHelper.class}, //
reportOn = "output", //
message = "{validation.MixBean.output}")
public class MixBean {
public Integer output = 10;
public Integer a = 1;
public Integer b = 2;
public Integer c = 3;
}
Validation message:
output != (a + b) * c
Message properties:
validation.MixBean.output={reportOn} != (a + b) * c
Spring Component:
@Component
public class MathComponent {
public int multiply(int a, int b) { return a * b; }
}
Helper class:
public class MathHelper {
public static int add(int a, int b) { return a + b; }
}
target
evaluated value can be reused in script and message attributes by calling #target
#{...}
blocks in message attribute are all SpEL expressions which will be evaluated eventually
Bean
@SpELScriptAssert( //
target = "a * b * c", //
script = "#target >= 10", //
message = "#{#target} {validation.AdvancedBean.msg} #{T(java.lang.Math).abs(#target - 10)}")
public class AdvancedBean {
public Integer a = 1;
public Integer b = 2;
public Integer c = 3;
}
Validation message:
6 is NOT greater than or equal to Ten before adding 4
Message properties:
validation.AdvancedBean.msg=is NOT greater than or equal to Ten before adding
<dependency>
<groupId>com.github.wnameless.spring</groupId>
<artifactId>spelscriptassert</artifactId>
<version>${newestVersion}</version>
<!-- Newest version shows in the maven-central badge above -->
</dependency>
Spring Environment - Inject MessageSource into the Jakarta Validator.
@Configuration
public class SpELScriptAssertConfig {
@Bean
LocalValidatorFactoryBean validatorFactoryBean(MessageSource messageSource) {
var bean = new LocalValidatorFactoryBean();
bean.setValidationMessageSource(messageSource);
return bean;
}
}
Standalone mode
// Jakarta Validator looks up `ValidationMessages.properties` file under classpath by default
Name | Description | Since |
---|---|---|
SpEL code blocks in message template | Them message attribute of @SpELScriptAssert now accepts #{spel_expr} code blocks. | v1.1.0 |
target | An optional attribute can hold an evaluation result for further use. | v1.1.0 |
Optional Spring Environment | @SpELScriptAssert can be used standalone or with Spring Environment. The only difference is that @springComponent syntax of SpEL expression won't work in standalone mode. |
v1.0.0 |
Java Module supported | The module-info.java included. | v1.0.0 |
performIf | A condition expression for determining whether a validation is performed or NOT. | v1.0.0 |
helpers | Register static methods from given Helper Classes. Methods can be called by #helperMethod syntax in SpEL expression. |
v1.0.0 |
reportOn | Same as reportOn in Hibernate validation @ScriptAssert annotation. |
v1.0.0 |
Smart Evaluation Strategies | Smart boolean convertion for evaluation values. | v1.0.0 |
🔝 SpEL code blocks(#{...}) in message template
@SpELScriptAssert( //
script = "str.empty", //
message = "str is NOT empty, str is '#{str}' with length of #{str.length}")
public class CodeBlockBean {
public String str = "SpELScriptAssert";
}
Validation message:
str is NOT empty, str is 'SpELScriptAssert' with length of 16
🔝 Utilize target
attribute
The target
attribute can hold an evaluation value for further uses in script
and message
contents. It can not only impove the readability but also save computation time if the evaluation of the expression in target
attribute is highly time comsuming.
@SpELScriptAssert( //
target = "bi1.multiply(bi2)", //
script = "#target.toString.length < 10", //
reportOn = "bi1 * bi2", //
message = "{reportOn} = #{#target} and the digits are more than 10")
public class TargetBean {
public BigInteger bi1 = new BigInteger("123456789");
public BigInteger bi2 = new BigInteger("987654321");
}
Validation message:
bi1 * bi2 = 121932631112635269 and the digits are more than 10
🔝 Smart Evaluation Strategies
SpELScriptAssert expects all evaluation scripts (both script
and performIf
) which always return Boolean values, however it also accepts other returning value types and treats them as Boolean values.
@SpELScriptAssert(script = "a", reportOn = "a", message = "{reportOn} is false")
@SpELScriptAssert(script = "b", reportOn = "b", message = "{reportOn} is false")
public class NumBean {
public int a = 0;
public long b = 1L;
}
Validation message:
a is false
@SpELScriptAssert(script = "a", reportOn = "a", message = "{reportOn} is false")
@SpELScriptAssert(script = "b", reportOn = "b", message = "{reportOn} is false")
@SpELScriptAssert(script = "c", performIf = "d", reportOn = "c", message = "{reportOn} is false")
public class ArrayCollectionBean {
public int[] a = {};
public String[] b = {"I"};
public List<?> c = List.of();
public Map<?, ?> d = Map.of("II", "III");
}
Validation message:
a is false
c is false
@SpELScriptAssert(script = "a", reportOn = "a", message = "{reportOn} is false")
@SpELScriptAssert(script = "b", reportOn = "b", message = "{reportOn} is false")
public class CharSequenceBean {
public String a = " ";
public CharSequence b = "I";
}
Validation message:
a is false
4. Non-empty Optional is True, otherwise False. However Optional<Boolean> can only be dertermined by its own returning value.
@SpELScriptAssert(script = "a", reportOn = "a", message = "{reportOn} is false")
@SpELScriptAssert(script = "b", reportOn = "b", message = "{reportOn} is false")
@SpELScriptAssert(script = "c", reportOn = "c", message = "{reportOn} is false")
public class OptionalBean {
public Optional<String> a = Optional.empty();
public Optional<String> b = Optional.of("I");
public Optional<Boolean> c = Optional.of(false);
}
Validation message:
a is false
c is false
@SpELScriptAssert(script = "a", reportOn = "a", message = "{reportOn} is false")
@SpELScriptAssert(script = "b", reportOn = "b", message = "{reportOn} is false")
public class NullBean {
public BigInteger a = null;
public BigDecimal b = BigDecimal.valueOf(123L);
}
Validation message:
a is false