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();
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.
@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();
}
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.