同服务层方法调用时@NotNull未触发ConstraintViolationException问题
同服务层内调用带@NotNull注解的方法为何不触发校验?
服务类代码
@Service @Validated public class ValidateService { public void testValidation(String notValidated) { testValidationNotNull(notValidated); } public void testValidationNotNull(@NotNull String validated) { validated.isBlank(); } }
问题描述
当执行testValidation方法并传入null时,预期会触发ConstraintViolationException,但实际却出现了NullPointerException(NPE),因为validated.isBlank()代码行被执行了。请问这是javax.validation.constraints.NotNull在同服务层内方法调用时的预期行为吗?
测试类代码
@SpringBootTest public class ValidateServiceTest { @Autowired private ValidateService sut; // 触发NPE而非ConstraintViolationException的测试 @Test public void testValidation() { sut.testValidation(null); } // 正常触发ConstraintViolationException的测试 @Test public void testValidationNotNull_shouldThrowConstraintsViolationException() { final ConstraintViolationException violationException = assertThrows(ConstraintViolationException.class, () -> sut.testValidationNotNull(null)); assertEquals("testValidationNotNull.validated: must not be null", violationException.getMessage()); } }
解答
这是预期行为,核心原因是Spring的方法校验依赖AOP代理机制:
- 当外部(如测试类)直接调用被
@Validated标记的Bean方法时,请求会经过Spring生成的代理类,代理类会先执行参数校验逻辑,校验不通过则抛出ConstraintViolationException。 - 但在同一个Bean内部调用方法时,是直接调用原始对象的方法,不会经过代理类拦截,因此
@NotNull的校验规则不会被触发,null值直接传入testValidationNotNull方法,执行validated.isBlank()时自然抛出NPE。
如果需要在内部调用时也触发校验,可以用以下两种方式解决:
- 注入自身代理对象调用
@Service @Validated public class ValidateService { @Autowired private ValidateService selfProxy; public void testValidation(String notValidated) { selfProxy.testValidationNotNull(notValidated); } public void testValidationNotNull(@NotNull String validated) { validated.isBlank(); } }
- 手动执行校验逻辑
@Service @Validated public class ValidateService { private final Validator validator; public ValidateService(ValidatorFactory validatorFactory) { this.validator = validatorFactory.getValidator(); } public void testValidation(String notValidated) { // 手动校验testValidationNotNull方法的参数 Set<ConstraintViolation<ValidateService>> violations = validator.validateValue( ValidateService.class, "testValidationNotNull", notValidated ); if (!violations.isEmpty()) { throw new ConstraintViolationException(violations); } testValidationNotNull(notValidated); } public void testValidationNotNull(@NotNull String validated) { validated.isBlank(); } }
内容的提问来源于stack exchange,提问作者Ivo Pogace
相关产品推荐
相关产品推荐

