3

The below code outputs "Inside Method" even though I'm passing null to the function. It should throw an exception instead. What's wrong with the below code? Why isn't it throwing an exception when a null parameter is being passed.

    import org.springframework.validation.annotation.Validated;
    import javax.validation.constraints.NotNull;
    @Validated
    class MyClass {
        public static void main(String[] args) {
            MyClass m = new MyClass();
            m.wontTakeNull(null);
        }
        
        public void wontTakeNull(@NotNull String param) {
            System.out.println("Inside Method");
        }
        
    }
Shashi Tunga
  • 496
  • 1
  • 6
  • 24

1 Answers1

2

If you want MyClass to be validated, it needs to be a Spring-managed bean. At the moment you're creating it yourself and then calling a method on it:

MyClass m = new MyClass();
m.wontTakeNull(null);

This means that Spring doesn't know anything about m and that it isn't a Spring-managed bean. As a result, Spring can't perform validation when you call wontTakeNull with null.

To make m a Spring-managed bean, you need to annotate it with @Component and use component-scanning or return an instance of it from a @Bean method in a configuration class. You then need to have Spring inject an instance of MyClass rather than creating it yourself. Here's a complete example that shows the @Component-based approach:

package com.example.demo;

import javax.validation.constraints.NotNull;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;

@SpringBootApplication
public class ValidationExampleApplication {
    
    public ValidationExampleApplication(MyClass myClass) {
        myClass.wontTakeNull(null);
    }

    public static void main(String[] args) {
        SpringApplication.run(ValidationExampleApplication.class, args);
    }
    
    @Component
    @Validated
    static class MyClass {
        
        public void wontTakeNull(@NotNull String param) {
            System.out.println("Inside Method");
        }
        
    }

}

This application will fail to start with the following root cause:

Caused by: javax.validation.ConstraintViolationException: wontTakeNull.param: must not be null
    at org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:120) ~[spring-context-5.3.9.jar:5.3.9]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.9.jar:5.3.9]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750) ~[spring-aop-5.3.9.jar:5.3.9]
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:692) ~[spring-aop-5.3.9.jar:5.3.9]
    at com.example.demo.ValidationExampleApplication$MyClass$$EnhancerBySpringCGLIB$$82020c75.wontTakeNull(<generated>) ~[main/:na]
    at com.example.demo.ValidationExampleApplication.<init>(ValidationExampleApplication.java:14) ~[main/:na]
    at com.example.demo.ValidationExampleApplication$$EnhancerBySpringCGLIB$$4c0df4b8.<init>(<generated>) ~[main/:na]
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:na]
    at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490) ~[na:na]
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:208) ~[spring-beans-5.3.9.jar:5.3.9]
    ... 20 common frames omitted
Andy Wilkinson
  • 108,729
  • 24
  • 257
  • 242
  • What if my use case involves copying variables from one object to another? Let's say There are two objects A { String a, String b} and B {String a, String b }. And I'm copying contents of A to B using B's setter method. I want null values to be flagged and should error out. And importantly A and B are not suitable to be bean. – Shashi Tunga Jul 23 '21 at 05:54
  • 1
    If beans aren’t involved then Bean Validation may not be the right tool for the job. Stack Overflow works best if you ask one question per question. I’d ask that separately as it’s a different problem with a different answer – Andy Wilkinson Jul 23 '21 at 06:12