I'm trying to get a better understanding of how to implement constructor injection in my application. I have some background processes that are executed by SimpleMessageListenerContainer workers that pull messages off of an AMQP server.
My application contains a service layer and a repo layer, a worker uses the services for data reads/writes. My controller, services, and repos are all setup using constructor injection, however since new versions of workers need to be instantiated I am stuck on how to initialize the worker.
Worker
public class RandomWorker extends Worker {
private UserService userService;
@Autowired
public RandomWorker(UserService userService) {
this.userService = userService;
}
@Override
public byte[] handleMessage(byte[] message) {
... do work ...
}
}
Service Layer
@Service
public class UserService {
private SecurityAreaRepo securityAreaRepo;
private SecurityRoleRepo securityRoleRepo;
private UserRepo userRepo;
@Autowired
public UserService(SecurityAreaRepo securityAreaRepo,
SecurityRoleRepo securityRoleRepo,
UserRepo userComponent) {
this.securityAreaRepo = securityAreaRepo;
this.securityRoleRepo = securityRoleRepo;
this.userRepo = userRepo;
}
}
WorkerConfig
@Configuration
public class WorkerConfig {
@Bean
public RandomWorker randomWorker() {
return new RandomWorker();
}
@Bean(name="randomWorkerContainer")
public SimpleMessageListenerContainer randomWorkerContainer() {
SimpleMessageListenerContainer smlc = new SimpleMessageListenerContainer();
smlc.setConnectionFactory(connectionFactory());
smlc.setMessageListener(new MessageListenerAdapter(randomWorker(), "handleMessage"));
smlc.setQueueNames("random.worker.queue");
smlc.setConcurrentConsumers(5);
smlc.start();
return smlc;
}
}
Since my worker requires the UserService, I must provide an instance of this in the WorkerConfig when I initialize a new RandomWorker. So am I going to have to create a @Bean for EVERY service that all of workers use? My WorkerConfig would look something like this:
@Configuration
public class WorkerConfig {
@Bean
public UserService userService() {
return new UserService(new SecurityAreaRepo(), new SecurityRoleRepo(), new UserRepo());
}
@Bean
public RandomWorker randomWorker() {
return new RandomWorker(userService());
}
@Bean(name="randomWorkerContainer")
public SimpleMessageListenerContainer randomWorkerContainer() {
SimpleMessageListenerContainer smlc = new SimpleMessageListenerContainer();
smlc.setConnectionFactory(connectionFactory());
smlc.setMessageListener(new MessageListenerAdapter(randomWorker(), "handleMessage"));
smlc.setQueueNames("random.worker.queue");
smlc.setConcurrentConsumers(5);
smlc.start();
return smlc;
}
}
If this is the case, I just do not see the point of constructor injection, when field injection makes everything so much simpler. Can somebody shed some light on this?
Spring automatically injects dependencies if you specify them as bean method's arguments. In your case, you just need to modify your worker bean method to:
@Bean
public RandomWorker randomWorker(UserService userService) {
return new RandomWorker(userService);
}
If UserService
service is available in context, Spring will automatically inject it as userService
parameter. You don't need to use @Bean
methods for every service - any method of registering beans in context will work (e.g. @ComponentScan, @SpringBootApplication or even manually adding the bean to context). It doesn't matter if you use constructor or setter injection.
As a side note - constructor injection is better because you can be sure your object is always instantiated in valid state. It's generally a good design to keep all of your objects in valid state all the time.