В Spring javaconfig, как инициализировать @Bean, который зависит от @Service

Я преобразовал проект на основе Spring 4.0 из xml в javaconfig.

При инициализации одному из моих bean-компонентов необходимо получить доступ к Hibernate, чтобы получить некоторые данные конфигурации из БД через Spring @Service (buildingService). Инициализация бина выглядит так:

@Bean
@DependsOn({ "transactionManager", "webSocketHandler", "buildingService" })
Smarty smarty() {
    Smarty bean = new Smarty();
    bean.init(); // I also tried @Bean(initMethod = "init") with no difference
    return bean;
}

Проблема в том, что в bean.init() осуществляется доступ к службе, которая завершается с ошибкой NullPointerException.

Я добавил buildingService к @DependsOn, но это не помогло.

Вероятно, классы с аннотациями @Service обрабатываются после @Bean !?

Могу ли я заранее инициализировать класс с аннотациями @Service?

Изменить 1

Спасибо за все отзывы!

Мне нужно добавить некоторые детали:

BuildingService — это не @Bean, это, ну, @Service и выглядит так:

@Service("buildingService")
@Transactional
public class BuildingService {

...

    public List<Building> getAll() {
        final Session session = sessionFactory.getCurrentSession();
        final Query query = session.createQuery("from Building order by name");
        return query.list();
    }

...

}

Smarty — это управляемый компонент Spring, инициализированный в классе с аннотацией @Configuration, который выполняет инициализацию корневого контекста.

Smarty имеет прямую зависимость от buildingService, например:

@Resource(name = "buildingService")
private BuildingService     buildingService;

Я попытался аннотировать Smarty.init() с помощью @PostConstruct, но это ничего не изменило.

Обратите внимание, что первое, что делает Smarty.init(), — это звонит buildingService.getAll();.


person yglodt    schedule 26.01.2014    source источник


Ответы (3)


Вы запутались в жизненном цикле бина. Spring должен сначала создать bean-компонент, прежде чем он сможет что-либо внедрить. В вашем методе @Bean вы создали свой bean-компонент

Smarty bean = new Smarty(); 

затем сразу вызвал один из его методов

bean.init();

это, кажется, зависит от вводимого поля.

Между этими двумя звонками ничего нет. Как вы ожидаете, что Spring что-то сделает?

Вместо этого вы можете аннотировать свой метод init() с помощью @PostConstruct. Как только Spring завершит инициализацию вашего компонента, т.е. когда ваш метод @Bean возвращается и Spring внедряет все цели внедрения объекта, он автоматически вызывает метод.

@DependsOn здесь не нужен.

person Sotirios Delimanolis    schedule 26.01.2014

@Sevice аннотированные bean-компоненты автоматически обнаруживаются и инициализируются посредством сканирования компонентов, чтобы включить это, используйте @ComponentScan в конфигурации Spring.

@ComponentScan

Настраивает директивы сканирования компонентов для использования с @Configuration классами.

@Bean используются для ручного создания bean-компонентов без использования специальных аннотаций вроде @Service или сканирования компонентов.

@Bean

Указывает, что метод создает bean-компонент для управления контейнером Spring. (...) Как правило, методы @Bean объявляются в классах @Configuration. В этом случае методы компонента могут ссылаться на другие методы @Bean в том же классе, вызывая их напрямую.


Конфигурация контекста

@Autowired
EntityManager entityManager; //needs to access Hibernate

@Bean
Smarty smarty() {
   return = new Smarty(entityManager);
}

И твой Smarty боб

public Smarty {

   final EntityManager entityManager;

   public Smarty(EntityManager entityManager){
      this.entityManager = entityManager;
   }
}
person MariuszS    schedule 26.01.2014

Вам не нужна аннотация @DependsOn, так как ваш Smarty bean имеет (или должен иметь) прямую зависимость от BuildingService. См. @DependsOn javadoc для получения дополнительной информации о том, когда его использовать.

В следующем примере показано, как можно решить вашу проблему:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SmartyTest.TestConfig.class)
public class SmartyTest {

@Autowired
Smarty1 smarty1;

@Autowired
Smarty2 smarty2;

@Test
public void testSmarty() throws Exception {
}

@Configuration
static class TestConfig {

    @Bean
    public BuildingService buildingService() {
        return new BuildingService();
    }

    @Bean
    public Smarty1 smarty1(BuildingService buildingService) {
        Smarty1 smarty = new Smarty1(buildingService);
        smarty.init();
        return smarty; // manually inject dependencies & handle initialisation
    }

    @Bean
    public Smarty2 smarty2() {
        // injecting the building service & initialising the component is handled by spring
        // by using @Autowired & @PostConstruct(-> alternative to @Bean(initMethod="init"))
        return new Smarty2();
    }
}


static class BuildingService {
    public void buildSomething() {
        System.out.println("BuildingService: I am building something!");
    }
}


static class Smarty1 {
    BuildingService buildingService;

    Smarty1(BuildingService buildingService) {
        this.buildingService = buildingService;
    }

    public void init() {
        System.out.println("Smarty 1: initialising ...");
        buildingService.buildSomething();
    }
}

static class Smarty2 {
    @Autowired
    BuildingService buildingService;

    @PostConstruct
    public void init() {
        System.out.println("Smarty 2: initialising ...");
        buildingService.buildSomething();
    }
}
}
person Pieter    schedule 26.01.2014