Каков порядок оценки @Autowiring в проекте SpringBoot

Я пытаюсь написать простой контроллер SprintBoot REST для работы в Websphere Liberty, и у меня возникла проблема с @Autowire. Вот соответствующие (я думаю) фрагменты кода:

@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 );

Когда я развертываю приложение в Liberty, оно запускается нормально. Когда я попадаю в соответствующую конечную точку, я вижу в 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]

Мой вопрос: почему два оператора println говорят, что их соответствующие переменные равны нулю? Это как-то связано с тем, что в журнале появляется конструктор UserSettingDAO, который должен быть выполнен до появления в журнале строк о dataSource?

Что следует сделать, чтобы правильно инициализировать эти переменные?


person Mark Lavin    schedule 24.04.2018    source источник
comment
у вас есть spring.datasource.url, настроенный в вашем application.properties? Ознакомьтесь с docs.spring.io/ spring-boot/docs/current/reference/html/ для справки   -  person Andy Guibert    schedule 25.04.2018
comment
Да, вместе с spring.datasource.driver-class-name, spring.datasource.username и spring.datasource.password.   -  person Mark Lavin    schedule 25.04.2018
comment
Я бы действительно предложил использовать API-интерфейсы Java EE, такие как JAX-RS, CDI, JPA на Liberty вместо Spring Boot. Он лучше интегрирован и поддерживается, в том числе с помощью инструментов.   -  person Gas    schedule 25.04.2018


Ответы (1)


Во время вызова конструктора компонента Spring любые объекты @Autowired по-прежнему будут нулевыми. Это связано с тем, что контейнеру Spring необходимо инициализировать экземпляр класса, прежде чем он сможет вводить значения в поля @Autowired.

Я не знаю, как Spring реализует инъекцию, но большинство инъекций Java работает примерно так:

  1. Создайте новый экземпляр класса компонента, обычно вызывая ctor по умолчанию (или инициализируя динамически сгенерированный прокси-подкласс)
  2. Получить объекты, которые необходимо внедрить в bean-компонент
  3. Используя отражение/прокси, установите поля в bean-компоненте

Чтобы проверить это, я создал метод для UserSettingsDAO, который использует поля @AutoWired:

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

Если мы внедрим этот DAO в другой класс и будем его использовать, мы увидим, что поля инициализируются после создания UserSettingDAO:

@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();
    }
}

Проверка журналов приведет к следующему выводу:

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
person Andy Guibert    schedule 25.04.2018