0

I have the below code in my SpringBoot project which is throwing NPE at a specific line mentioned.

Exception in thread "main" java.lang.NullPointerException
  at com.temp.controller.Controller.triggerJob(Controller.java:15)
  at com.temp.Application.main(Application.java:19)

Application.java

@Configuration
@SpringBootApplication
@ImportResource({"classpath:applicationContext.xml"})
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
        
        Controller controller = new Controller();
        controller.triggerJob();
    }
}

Controller.java

@Controller
public class Controller {
    @Autowired
    private Service Service;
    
    public void triggerJob() {
        Service.selectRecords();
    }
}

Service.selectRecords(); is where the NPE is being thrown

Service.java

public interface Service {
    List<VO> selectRecords();
}

ServiceImpl.java

@Service
public class ServiceImpl implements Service {
    @Autowired
    private Dao dao;

    @Override
    public List<VO> selectRecords() {
        return dao.selectRecords();
    }
}

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:util="http://www.springframework.org/schema/util" xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
                            http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
                            http://www.springframework.org/schema/jdbc
                            http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd
                            http://www.springframework.org/schema/util 
                            http://www.springframework.org/schema/util/spring-util-3.2.xsd
                            http://www.springframework.org/schema/context
                            http://www.springframework.org/schema/context/spring-context-3.2.xsd">

    <util:properties id="configProperties"
        location="file:${app.config.home}/config/config-${spring.profiles.active}.properties" />

    <context:property-placeholder
        location="file:${app.config.home}/config/config-${spring.profiles.active}.properties" />

    <bean id="crypt" class="com.temp.util.MyUtil">
        <property name="userId" value="${username}"></property>
        <property name="password" value="${password}"></property>
        <property name="key" value="123456789012345678901234"></property>
    </bean>

    <bean id="datasource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${driver}" />
        <property name="url" value="${connection}" />
        <property name="username" value="#{crypt.userId}" />
        <property name="password" value="#{crypt.password}" />
    </bean>

    <bean id="namedJdbcTemplate"
        class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
        <constructor-arg ref="datasource" />
    </bean>
</beans>

I have a similar project like this and have compared the configuration of both and they are the same except for 2 things.

  1. The spring boot starter version is 2.4.2 for this project and 1.5.3 for the other project.
  2. Java version is 11 for this project and 1.8 for the other project.

Not sure where I'm going wrong.

Am I missing something? Kindly help.

Mike
  • 721
  • 1
  • 17
  • 44

1 Answers1

0

You are creating new instance Controller controller = new Controller(); manually and this instance is not in Spring context. Therefore, the injected (autowired) instance of Service is null. @Autowired only works when the instance exists in Spring context.

The best approach is to keep Controller testable is injection via constructor:

@Configuration
@SpringBootApplication
@ImportResource({"classpath:applicationContext.xml"})
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
        
        Controller controller = new Controller(new ServiceImpl(new DaoImpl()));
        controller.triggerJob();
    }
}

Inject the instance via constructor:

    @Controller
    public class Controller {
        
        private final Service Service;
    
        public Controller(final Service Service) {
           this.service = service;
        }
        
        public void triggerJob() {
            Service.selectRecords();
        }
    }

And also inject the Dao dependency via constructor:

@Service
public class ServiceImpl implements Service {
    
    private final Dao dao;

    public ServiceImpl(final Dao dao) {
       this.dao = dao;
    }

    @Override
    public List<VO> selectRecords() {
        return dao.selectRecords();
    }
}

For Spring version above 4.3, @Autowired could be omitted from top of the constructor, Spring scans the injection automatically and injects the dependency via constructor. For Spring version below 4.3, add @Autowired on top of constructors i.e.:

@Controller
public class Controller {

    private final Service Service;
    
    @Autowired
    public Controller(final Service Service) {
       this.service = service;
    }

    public void triggerJob() {
        Service.selectRecords();
    }
}
Ali K. Nouri
  • 495
  • 5
  • 18
  • Thanks Ali. Is there a way to use Autowired and call the Controller by any chance? – Mike Feb 15 '21 at 18:15
  • No because main class is static, and it is loaded before Spring bean creation. There is no way to inject a Spring Bean in a static method. And if you instantiate a class manually, the instance will be out of Spring context, so Spring does not have any control over its injection or life-cycle. – Ali K. Nouri Feb 15 '21 at 18:27
  • Okay Ali. If you can see, there is a NamedParameterJdbcTemplate as well. When I inject the same via constructor in my Application.java, I am receiving error. Not able to inject it. – Mike Feb 16 '21 at 13:41