Question

I have converted a Spring 4.0-based project from xml to javaconfig.

On initialization, one of my beans needs to access Hibernate to fetch some config-data from the DB, through a Spring @Service (buildingService). The bean initialization looks like this:

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

The problem is that in bean.init(), the service is accessed, which fails with a NullPointerException.

I added buildingService to @DependsOn but it did not help.

Probably the @Service-annotated classes are processed after the @Bean !?

Can I initialize the @Service-annotated class myself upfront?

Edit 1

Thanks so far for all the feedback !

I need to add some details:

buildingService is not a @Bean, it's, well, a @Service and looks like this:

@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 is a Spring managed Bean, and initialized in an @Configuration-annotated class which is doing the initialization of the root-context.

Smarty has a direct dependency on buildingService, like so:

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

I tried annotating Smarty.init() with @PostConstruct but this did not change anything.

Note that the first thing Smarty.init() does is calling buildingService.getAll();

Was it helpful?

Solution

You're confused about the lifecycle of a bean. Spring has to first create the bean before it can inject anything. In your @Bean method, you've created your bean

Smarty bean = new Smarty(); 

then immediately called one of its methods

bean.init();

that seems to depend on a field being injected.

There's nothing between those two calls. How do you expect Spring to do anything?

Instead, you could annotate your init() method with @PostConstruct. Once Spring is done initializing your bean, ie. when your @Bean method returns and Spring injects all the object's injection targets, it will invoke the method automatically.

@DependsOn is not necessary here.

OTHER TIPS

@Sevice annotated beans are autodiscovered and initialized via component scanning, to enable this use @ComponentScan on Spring Configuration.

@ComponentScan

Configures component scanning directives for use with @Configuration classes.

@Bean are used for manual creating beans, without using special annotation like @Service or component scanning.

@Bean

Indicates that a method produces a bean to be managed by the Spring container. (...) Typically, @Bean methods are declared within @Configuration classes. In this case, bean methods may reference other @Bean methods in the same class by calling them directly.


Context configuration

@Autowired
EntityManager entityManager; //needs to access Hibernate

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

And your Smarty bean

public Smarty {

   final EntityManager entityManager;

   public Smarty(EntityManager entityManager){
      this.entityManager = entityManager;
   }
}

You don't need the @DependsOn annotation as you Smarty bean has (or should have) a direct dependency on BuildingService. See the @DependsOn javadoc for more info on when to use it.

The following example demonstrates how you can solve your problem:

@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();
    }
}
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top