javaspringspring-ioc

Spring application having AnnotationConfigApplicationContext: getting run time exception


This is a continuation to one of my SO questions.

I extended the same example and I expected it to work, however I am getting NullPointerException.

Here is the complete source code:

FirstBean.java

package com.example;

import org.springframework.stereotype.Component;

@Component
public class FirstBean {

    public FirstBean() {

    }

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "FirstBean [name=" + name + "]";
    }
}

SomeBean.java

package com.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

@Component
@Configuration
@ComponentScan(basePackages = { "com.example" })
public class SomeBean {

    @Autowired
    private FirstBean fb;

    @Bean
    FirstBean instantiateFirstBean() {
        return new FirstBean();
    }

    public SomeBean() {
        // this.fb.setName("Some Name"); -> this was causing problem as 
        // bean isn't still created fully
    }

    public void print() {
        this.fb.toString(); 
    }
    
    @PostConstruct
    void post() {
        fb.setName("Some name");
    }
}

MainDriver.java

package com.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;

@Configuration
@ComponentScan(basePackages = { "com.example" })
@PropertySource(ignoreResourceNotFound = true, value = "classpath:/application.props")
public class MainDriver {

    @Autowired
    private Environment env;

    @Autowired
    private ConfigurableEnvironment cenv;

    @Autowired
    ApplicationContext ctx;

    @Autowired
    private SomeBean sb;

    @Bean
    public SomeBean instantiateSomeBean() {
        return new SomeBean();
    }

    public static void main(String[] args) {

        ApplicationContext ctx = new AnnotationConfigApplicationContext(MainDriver.class);

        MainDriver l = ctx.getBean(MainDriver.class);
        System.out.println("In main() the ctx is " + ctx);
        l.test();
    }

    public void test() {
        System.out.println("hello");
        sb.print();
    }
}

I was expecting SomeBean to get Autowired (ctx, env, cenv are getting Autowired perfectly), but it is not getting and I am getting run time exception.

Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'mainDriver': Unsatisfied dependency expressed through field 'sb'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'someBean' defined in file [/Users/vkoul/understand-code/platform/stream-base/trunk/target/classes/com/example/SomeBean.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.example.SomeBean$$EnhancerBySpringCGLIB$$fad3571b]: Constructor threw exception; nested exception is java.lang.NullPointerException
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'mainDriver': Unsatisfied dependency expressed through field 'sb'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'someBean' defined in file [/Users/vkoul/understand-code/platform/stream-base/trunk/target/classes/com/example/SomeBean.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.example.SomeBean$$EnhancerBySpringCGLIB$$fad3571b]: Constructor threw exception; nested exception is java.lang.NullPointerException
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:584)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:370)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1336)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:572)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:495)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:759)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:548)
    at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:88)
    at com.example.MainDriver.main(MainDriver.java:37)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'someBean' defined in file [/Users/vkoul/understand-code/platform/stream-base/trunk/target/classes/com/example/SomeBean.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.example.SomeBean$$EnhancerBySpringCGLIB$$fad3571b]: Constructor threw exception; nested exception is java.lang.NullPointerException
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1228)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1127)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:535)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:495)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:251)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1135)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1062)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:581)
    ... 14 more
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.example.SomeBean$$EnhancerBySpringCGLIB$$fad3571b]: Constructor threw exception; nested exception is java.lang.NullPointerException
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:182)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:87)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1220)
    ... 25 more
Caused by: java.lang.NullPointerException
    at com.example.SomeBean.<init>(SomeBean.java:23)
    at com.example.SomeBean$$EnhancerBySpringCGLIB$$fad3571b.<init>(<generated>)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:170)
    ... 27 more

I am mentioning @Bean as well which I hoped it will tell how to create those beans, but still getting the exception.

What am I not understanding, and how can I fix it?


Solution

  • I think this is because Spring will first have to instantiate your SomeBean class and then it will try to autowire FirstBean through reflection because you put @Autowired annotation on the field.

    You are trying to access FirstBean dependency in SomeBean constructor but when constructor of SomeBean is invoked, when context is being created, the FirstBean dependency is null because Spring has not autowired the dependency yet - it will try to autowire it through reflection after object creation. That is why you are getting NullPointerException there. The same situation happens with your SomeBean class - decide on one approach.

    Additionaly it is strange that you create classes that are both annotated with @Configuration and @Component. Please refere to this SO question to see potential errors.

    Moreover there are some issues in your configuration. You annotate FirstBean with @Component annotation and you still create a @Bean annotated method that returns instance of this class. You should decide on one apprach - you instantiate it for application context using @Bean annotation or you annotate this class with @Component and you let component scan automatically create it for you.

    EDIT :

    You should decide if you want to use @ComponentScan or you want to create your beans for context through @Bean annotation - right now you are mixing different types and you will get errors for duplicate bean definitions when trying to autowire by type, because in your current implementation there will be 2 bean for SomeBean and 2 beans for FirstBean in your context - they will only have different ids. Note that error that I pointed out with NPE expection has nothing to do with it. I will propose a solution using automated package scanning :

    FirstBean class

    @Component
    public class FirstBean {
    
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "FirstBean [name=" + name + "]";
        }
    }
    

    SomeBean class

    @Component
    public class SomeBean {
    
        private FirstBean fb;
    
        @Autowired
        public SomeBean(FirstBean firstBean) {
            this.fb = firstBean;
            this.fb.setName("Some Name");
        }
    
        public void print() {
            this.fb.toString();
        }
    }
    

    MyConfig class

    @Configuration
    @ComponentScan(basePackages = { "com.example" })
    public class MyConfig {
    
    }
    

    MainDriver class

    @Component
    public class MainDriver {
    
        @Autowired
        private Environment env;
    
        @Autowired
        protected ConfigurableEnvironment cenv;
    
        @Autowired
        ApplicationContext ctx;
    
        @Autowired
        private SomeBean sb;
    
    
        public static void main(String[] args) {
    
            ApplicationContext ctx = new AnnotationConfigApplicationContext(MyConfig.class);
    
            MainDriver l = ctx.getBean(MainDriver.class);
            System.out.println("In main() the ctx is " + ctx);
            l.test();
        }
    
        public void test() {
            System.out.println("hello");
            sb.print();
        }
    }