Search code examples
javaspring-bootnotnull

@NotNull annotation not working - Doesn't throw an exception when null is passed


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");
        }
        
    }

Solution

  • 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