1

I'm trying to write a simple SprintBoot REST Controller to run on Websphere Liberty, and having a problem with @Autowire. Here are the relevant (I think) pieces of code:

@CrossOrigin
@RestController
@RequestMapping(path = "userSetting")
public class RESTInterface {

    @Autowired
    private UserSettingDAO userSettingDAO;

    @RequestMapping(path = "/getUserSetting", method = { RequestMethod.GET }, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String getUserSetting(
            @RequestParam(value="user_id", required=true ) String user_id, 
            @RequestParam(value="app_id", required=true )  String app_id,
            @RequestParam(value="dashboard_name", required=true ) String dashboard_name 
            ) {
        if ( userSettingDAO == null ) {
            System.out.println( "userSettingDAO is null" );
    ...

////////////////////////////////////////////////////////////////////////////////

@Component
 public class UserSettingDAO {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Autowired
    private DataSource dataSource;

     public UserSettingDAO() {
        System.out.println( "dataSource is " + dataSource );
        System.out.println( "jdbcTemplate is " + jdbcTemplate );

When I deploy the application to Liberty, it appears to start up OK. When I hit the appropriate endpoint, I see the following in console.log:

UserSettingDAO.jdbcTemplate = null
dataSource is null
jdbcTemplate is null
2018-04-24 16:54:19.887  INFO 8807 --- [cutor-thread-11] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/userSetting/getUserSetting],methods=[GET],produces=[application/json;charset=UTF-8]}" onto public java.lang.String com.ui.usersetting.restinterface.RESTInterface.getUserSetting(java.lang.String,java.lang.String,java.lang.String)
2018-04-24 16:54:19.890  INFO 8807 --- [cutor-thread-11] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/userSetting/upsertUserSetting],methods=[POST],produces=[application/json;charset=UTF-8]}" onto public void com.ui.usersetting.restinterface.RESTInterface.upsertUserSetting(java.lang.String,java.lang.String,java.lang.String,java.lang.String)
2018-04-24 16:54:19.891  INFO 8807 --- [cutor-thread-11] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/userSetting/deleteUserSetting],methods=[DELETE],produces=[application/json;charset=UTF-8]}" onto public void com.ui.usersetting.restinterface.RESTInterface.deleteUserSetting(java.lang.String,java.lang.String,java.lang.String)
2018-04-24 16:54:19.893  INFO 8807 --- [cutor-thread-11] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/userSetting/hello],methods=[GET],produces=[application/json;charset=UTF-8]}" onto public java.lang.String com.ui.usersetting.restinterface.RESTInterface.hello()
2018-04-24 16:54:19.924  INFO 8807 --- [cutor-thread-11] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2018-04-24 16:54:19.925  INFO 8807 --- [cutor-thread-11] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2018-04-24 16:54:20.166  INFO 8807 --- [cutor-thread-11] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@6a5997f7: startup date [Tue Apr 24 16:54:15 EDT 2018]; root of context hierarchy
2018-04-24 16:54:21.386  INFO 8807 --- [cutor-thread-11] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2018-04-24 16:54:21.389  INFO 8807 --- [cutor-thread-11] o.s.j.e.a.AnnotationMBeanExporter        : Bean with name 'dataSource' has been autodetected for JMX exposure
2018-04-24 16:54:21.396  INFO 8807 --- [cutor-thread-11] o.s.j.e.a.AnnotationMBeanExporter        : Located MBean 'dataSource': registering with JMX server as MBean [com.zaxxer.hikari:name=dataSource,type=HikariDataSource]

My question is: Why are the two println statements saying that their respective variables are null? Does it have something to do with the fact that the UserSettingDAO constructor appears in the log to be executed before the lines about dataSource appear in the log?

What should I do to get those variables properly initialized?

Andy Guibert
  • 41,446
  • 8
  • 38
  • 61
Mark Lavin
  • 1,002
  • 1
  • 15
  • 30
  • do you have a `spring.datasource.url` configured in your application.properties? Check out https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-sql.html for reference – Andy Guibert Apr 24 '18 at 22:24
  • Yes, along with `spring.datasource.driver-class-name`, `spring.datasource.username` and `spring.datasource.password`. – Mark Lavin Apr 25 '18 at 00:32
  • I'd really suggest to use Java EE APIs, like JAX-RS, CDI, JPA on Liberty instead of Spring Boot. It is better integrated and supported, also by tooling. – Gas Apr 25 '18 at 15:16

1 Answers1

0

At the time a Spring component's constructor is called, any @Autowired objects will still be null. This is because the spring container needs to initialize an instance of the class before it can inject values into the @Autowired fields.

I don't know how Spring implements injection, but most Java injection works something like this:

  1. Construct new instance of the bean class, normally by invoking default ctor (or initializing a dynamically generated sub-class proxy)
  2. Obtain objects that need to be injected into the bean
  3. Using reflection/proxy, set the fields on the bean

To validate this, I created a method on UserSettingsDAO that utilizes the @AutoWired fields:

@Component
public class UserSettingDAO {

   @Autowired
   private JdbcTemplate jdbcTemplate;

   @Autowired
   private DataSource ds;

   public UserSettingDAO() {
       System.out.println("ctor/jdbcTemplate is " + jdbcTemplate );
       System.out.println("ctor/datasource is: " + ds);
   }

   public void doStuff() {
       System.out.println("doStuff/jdbcTemplate is " + jdbcTemplate );
       System.out.println("doStuff/datasource is: " + ds);
   }
}

If we inject this DAO into another class and use it, we will see that the fields are initialized after the UserSettingDAO is constructed:

@CrossOrigin
@RestController
public class RESTInterface {

    @Autowired
    private UserSettingDAO dao;

    @RequestMapping(path = "/jdbc", method = { RequestMethod.GET }, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String jdbc() {
        return dao.doStuff();
    }
}

Checking logs, this will produce the following output:

ctor/jdbcTemplate is null
ctor/datasource is: null
doStuff/jdbcTemplate is org.springframework.jdbc.core.JdbcTemplate@4eb46bed
doStuff/datasource is: com.ibm.ws.rsadapter.jdbc.WSJdbcDataSource@727d23c5
Andy Guibert
  • 41,446
  • 8
  • 38
  • 61