Monday, June 09, 2014

How to Autowire a Bean That Requires Constructor Argument In Spring

Spring allows you to inject a managed object (bean) as a dependency into another object via the @Autowired annotation.

For example, if I have a UserService that has a dependency on UserRepository, I can have the UserRepository injected using @Autowired annotation like this:

UserRepository Class...
class UserRepository {
UserRepository () {}
}


UserService Class...
class UserService {

@Autowired
private UserRepository userRepository;

UserService () {}

}

This is done using Field Injection. The same thing can be accomplied using Setter Injection:

class UserService {

private UserRepository userRepository;

UserService () {}

@Autowired // Using setter injection
public void setUserRepository(
UserRepository userRepository) {
this.userRepository = userRepository
}

}


or via Constructor Injection:

class UserService {

private UserRepository userRepository;

@Autowired // Using constructor Injection
UserService (UserRepository userRepository) {
this.userRepository = userRepository
}

}


There are different opinions on which method is "the right way" but since this post is not about that debate. I would point you to this post instead: Why I changed My Mind About Field Injection

Which ever method you choose to use, you are essentially doing the same thing: instructing Spring to supply an object's dependency by using the @Autowired annotation.

But what happens when the Object you want injected in has a constructor that requires an argument?



In our example, what if the implementation of the UserRepository requires an entityManager to be supplied as a constructor argument. How do you go about this? Is there a propetry of the @Autowired annotation that allows you to specify the argument constructors?

Something like this:

class UserService {

@Autowired(constructor="specify constructor for UserRepository before injecting it into UserService")
private UserRepository userRepository;

UserService () {}
}

}

Unfortunately no, there is nothing like this, and if you think about it, rightly so. Because at the point of autowiring a bean, the bean should be in existence already, created by the Spring Application context. What the @Autowired annotation is doing is simplying taking what is already there (not creating it) and make it part of the dependency of another bean.

If you then want to autowire a bean with constructor argument in Spring, then you have to look at doing so, at the creation process for that bean.

So in our example above, if UserRepository has a dependency that is supplied via its constructor, you make sure this constructor argument is made known and supplied, before it can then be @Autowired into our UserService.

And this can be done either by using the @Autowired annotation or the @Value annotation. You use the @Autowired notation when the constructor argument is another Object, while the @Value annotation comes in handy when the contructor argument can easily be evaluated using Spring expression.


Using @Autowired

class UserRepository {

@Autowired
UserRepository (EntityManager entityManager) {
// initalize the UserRepository object
// using supplied EntityManager object
}

}

Using @Value
This is useful when the value of the contructor arguments can be evaluated from property files or other sources for that matter. Let us imagine the arguments needed for UserRepository are JDBC configurations. So when you want to autowire it into the UserService class, you want it to already have the needed JDBC properties passed on. As we stated earlier, to do this, we look into UserRepository itself and its creation point, not UserService.

Let us imagine the JDBC property file looks thus:

jdbc.url=jdbc:mysql://localhost/test
jdbc.username=user
jdbc.password=userpass

Then you can have these value passed in using @Value:

class UserRepository {

@Autowired
UserRepository (@Value("${jdbc.url}") String url,
                @Value("${jdbc.usersame}") String username,
                @Value("${jdbc.password}") String password) {
// initalize the UserRepository object 
// using supplied JDBC properties
}
}

With the above, you can happily now autowire the UserRepository into UserService and the constructor argument contract would be gracefully fulfilled.

Note that for the above property placeholder evaluation to be done, you have to have the PropertyPlaceholderConfigurer configured in your Spring application context. You can find more about this here: Hiding of Bean Definition via Namespaces

4 comments:

chris marx said...

so obvious now that I see it, but this was really helpful, thank you-

jaison said...

nothing to say... but only one word ...super

srinivas bharathwaj said...

Neat explanation.

Anonymous said...

Very clearly an detailed explanation. Hats-off to you.