Constructor Injection in SimpleMessageListenerContainer - java

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.

Related

How do I create multiple Spring beans of the same type without defining each one

I've seen a lot of workaround-looking things regarding what I'm trying to do using BeanDefinitionRegistryPostProcessor, but I wondered if there was a way to tap directly into Spring's bean creation API to override some behavior.
What I would like to see is something like this (note the 's' in #Components):
#Components(prefix="myBean-", numberOfInstances="${myapp.mybean.numberOfInstances}")
public class MyBean {
private final MyService myService;
public MyBean(final MyService myService) {
this.myService = myService;
}
#Scheduled(fixedDelayString = "${myapp.mybean.fixedDelay}")
public myJob() {
System.out.println("I'm working!");
}
}
I am basically looking for the same functionality of #Component where I can specify how many instances to make and just have the name generated.
As I mentioned before, the only way I have found to do this sort of thing (specifically for scheduled tasks now) is to use the BeanDefinitionRegistryPostProcessor to create the instances or create a custom SchedulingConfigurer to configure the tasks manually without using Spring beans, which means all the Runnable's dependencies have to be wired into the SchedulingConfigurer, and that just feels dirty.
Is this even possible--to add a new annotation to scan for and invoke some other way to create the beans?
Update
Thanks to #vince for helping me realize I don't need a separete bean for each job; I just have to configure the singleton multiple times into the FixedDelayTask.
#Component
public class MyBean {
private final MyService myService;
public MyBean(final MyService myService) {
this.myService = myService;
}
// Remove #Scheduled here since we have to configure multiple
// instances manually. This is where it would be nice to specify
// how many jobs of the same type you want.
// #Scheduled(fixedDelayString = "${myapp.mybean.fixedDelay}")
public myJob() {
System.out.println("I'm working!");
}
}
#Configuration
#EnableScheduling
public class MyBeanTaskConfiguration implements SchedulingConfigurer {
private final MyBean myBean;
public MyBeanTaskConfiguration(MyBean myBean) {
this.myBean = myBean;
}
#Override
public void configureTasks(final ScheduledTaskRegistrar taskRegistrar) {
for (int i = 0; i < numberOfWorkers; i++) {
taskRegistrar.scheduleFixedDelayTask(
new FixedDelayTask(
myBean,
repeatIntervalMs,
repeatIntervalMs / numberOfWorkers * i + startDelayMs
)
);
}
}
}
Actually I'm wondering why u wanna do this. According to the IOC philosophy, beans should be delegated to container and clients don't need to care about beans' lifecycles. That's why Spring provides #Scope to support different bean scopes like singleton/request/session. So I don't think it a good way to control the specific number of a certain bean, besides, beans should theoretically be non-stateful, thus a single instance is fairly enough.
Prototype scoped beans will be provided as a new instance for each request to the container.
#Component
#Scope("prototype")
public class MyBean {
private final MyService myService;
public MyBean(final MyService myService) {
this.myService = myService;
}
// ...
}
// Get two separate instances
MyBean bean1 = (MyBean)applicationContext.getBean("myBean");
MyBean bean2 = (MyBean)applicationContext.getBean("myBean");

Spring: Mixing of wiring in JavaConfig with wiring using #Autowired

I am totally confused about mixing of "wiring in JavaConfig" and "wiring using #Autowired". I will tell you my problems in 4 scenarios:
(I am ok with mixing of #Autowired and stereotype annotations and I don't have any question about that. my problem is Javaconfig and #autowired)
Scenario 1:
My CDPlayer Class:
public class CDPlayer implements MediaPlayer {
private CompactDisc cd;
public CDPlayer() {
cd = new CompactDisc() {
#Override
public void play() {
System.out.println("123456");
}
};
}
#Autowired
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}
public void play() {
cd.play();
}
}
My JavaConfig File:
#Configuration
public class CDPlayerConfig {
#Bean
public CompactDisc sgtPeppers() {
return new SgtPeppers();
}
#Bean
public CDPlayer cdPlayer(CompactDisc compactDisc) {
return new CDPlayer();
}
}
For Example in this scenario, I see that #Autowired is effectless and cannot make Spring to invoke and use the parameterized constructor and no-arg constructor will be executed (because it is invoked in the #Bean method) and the output is the text "123456".
=================================================================
SCENARIO 2:
My JavaConfig File:
#Configuration
public class CDPlayerConfig {
#Bean
public CompactDisc sgtPeppers() {
return new SgtPeppers();
}
#Bean
public CDPlayer cdPlayer(CompactDisc compactDisc) {
return new CDPlayer(compactDisc);
}
}
My CDPlayer Class:
public class CDPlayer implements MediaPlayer {
private CompactDisc cd;
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}
public void play() {
cd.play();
}
}
we wired those two beans in the config file. and I know that we do not need #Autowired at all.
=================================================================
SCENARIO 3:
My JavaConfig File:
#Configuration
public class CDPlayerConfig {
#Bean()
public CompactDisc sgtPeppers() {
return new SgtPeppers();
}
#Bean
public CDPlayer cdPlayer() {
return new CDPlayer();
}
}
My CDPlayer Class:
public class CDPlayer implements MediaPlayer {
private CompactDisc cd;
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}
public void play() {
cd.play();
}
#Autowired
public void setCd(CompactDisc cd) {
this.cd = cd;
}
}
I know that if #Autowired is above of parameterized constructor, that constructor will not be executed but now that is above of setCd(), this method will be executed.
=================================================================
SCENARIO 4:
My JavaConfig File:
#Configuration
public class CDPlayerConfig {
#Bean
public CompactDisc sgtPeppers() {
return new SgtPeppers();
}
#Bean
public CDPlayer cdPlayer(CompactDisc compactDisc) {
return new CDPlayer(compactDisc);
}
}
My CDPlayer Class:
public class CDPlayer implements MediaPlayer {
private CompactDisc cd;
public CDPlayer() {
}
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}
public void play() {
cd.play();
}
#Autowired
public void doSomething(CompactDisc cd) {
this.cd = new CompactDisc() {
#Override
public void play() {
System.out.println("AAAAA");
}
};
}
}
and in this scenario, Although that we wired those two beans together, but #Autowired makes spring to execute the doSomething()method.
What is happening?! I can't see the Big Picture. I can't understand the pattern that is going on.
sometimes #Autowired works and sometimes doesn't work. what is the general pattern? do we need #Autowired at all when we wire beans together in JavaConfig file?
An autowired constructor is invoked if spring invokes the constructor by reflection, typically because you declare the bean using component scanning or XML config. If you manually invoke a constructor in a #Bean method, that constructor executes, and #Autowired has no effect.
An autowired method is invoked after the bean has been created, irrespective of how the bean was created.
The reason is that, in Java, each constructor call creates a new object, making it impossible to call two constructors for the same object. That's why Spring can't call a second constructor if you have already called a different one. In contrast, it is possible to call many methods on the same object, so Spring does support method autowiring just fine.
To conclude, you can use autowiring with JavaConfig, but you should autowire fields or methods rather than constructors. Or you can do without autowiring, and pass everything explicitly in your #Bean method. Or any mixture of the two.
The Spring Context contains all the beans you need in your program, and Spring do the rest of the job for you. But something to understand is that your beans comes from many parts of your application :
Internal beans (POJO from your domain).
External beans (POJO from other libraries or third partie classes).
Reading this from the spring documentation, you can find all the differents sources of beans :
#SpringBootApplication is a convenience annotation that adds all of
the following:
#Configuration: Tags the class as a source of bean definitions for the
application context.
#EnableAutoConfiguration: Tells Spring Boot to start adding beans
based on classpath settings, other beans, and various property
settings. For example, if spring-webmvc is on the classpath, this
annotation flags the application as a web application and activates
key behaviors, such as setting up a DispatcherServlet.
#ComponentScan: Tells Spring to look for other components,
configurations, and services in the com/example package, letting it
find the controllers.
Follow these rules :
In your domain classes (Controller, Service) : use #Autowired in your constructor. It is the recommanded way to inject your dependencies.
You want to use external classes : implements a Java Configuration with #Configuration annotation, to instanciate your external classes as beans.
You want to create custom utilities classes : decorate it with #Component.
When you have more than on implementation, use #Qualifier and define your beans in a #Configuration class.

Respect #Lazy annotation on non-#Primary #Bean

I'm having problems getting Spring to respect the #Lazy annotation on #Bean methods when it is configured to use a different #Bean method that returns an implementation of the same interface that is flagged as #Primary.
Specifically, I have a #Configuration-annotated class with several #Bean methods that all return the same interface. Many of these #Bean methods are #Lazy, as they contact external services for which the application may not currently be using. The #Primary bean is not #Lazy, as it looks at runtime configuration to determine which implementation to return.
Here is a contrived example of that configuration class, revolving around a fictitious ThingService interface:
#Configuration
#ComponentScan(basePackages = { "com.things" })
public class ThingConfiguration {
#Bean
public ThingOptions thingOptions() {
ThingOptions options = new ThingOptions();
options.sharing = true;
return options;
}
#Primary
#Bean
public ThingService primaryThing(ThingOptions options, ApplicationContext context) {
System.out.println("PrimaryThing -- Initialized");
if (options.sharing) {
return context.getBean("OurThing", ThingService.class);
} else {
return context.getBean("YourThing", ThingService.class);
}
}
#Lazy
#Bean(name = "YourThing")
public ThingService yourThing() {
System.out.println("YourThingService -- Initialized");
return new YourThingService();
}
#Lazy
#Bean(name = "OurThing")
public ThingService ourThing() {
System.out.println("OurThingService -- Initialized");
return new OurThingService();
}
}
I then have a #Component that depends on this interface which that the #Primary annotation will ensure that the correct implementation will be injected into the object. Here is an example of that downstream #Component:
#Component
public class ThingComponent {
private final ThingService thingService;
#Inject
public ThingComponent(ThingService thingService) {
this.thingService = thingService;
}
}
I then built a small test to ensure that #Lazy and #Primary are all being respected.
public class ThingTest {
#Test
public void TestLazyAndPrimary() {
// Arrange
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(ThingConfiguration.class);
context.refresh();
// Act
ThingComponent component = context.getBean(ThingComponent.class);
// Assert
Assert.assertNotNull(component);
}
}
However, when I run this test, I found that #Lazy was being ignored. The following text is emitted to the console:
PrimaryThing -- Initialized
OurThingService -- Initialized
YourThingService -- Initialized
The "YourThing" #Bean should not have been initialized, as it was #Lazy and not loaded at runtime via the ApplicationContext.getBean() method. Yet when the ThingComponent is resolved, it causes the #Bean methods with that return an implementation of ThingService to be hydrated before the #Primary mean is chosen.
How do I get the #Primary annotated implementation of an interface to be respected without causing all of the non-#Primary implementations annotated with #Lazy to be hydrated?
I have been unable to stop the #Primary annotation from forcing eager hydration of all #Bean methods that return that interface, even though this information seems available without forcing hydration from the annotations in exclusivity. I got around this by using a naming convention on #Bean methods instead.
Specifically, I changed my #Primary annotated #Bean method to include a name like so:
#Configuration
#ComponentScan(basePackages = { "com.things" })
public class ThingConfiguration {
// #Primary -- I don't want someone to accidentally use this without a #Qualifier!
#Bean(name = "PrimaryThingService")
public ThingService primaryThing(ThingOptions options, ApplicationContext context) {
System.out.println("PrimaryThing -- Initialized");
if (options.sharing) {
return context.getBean("OurThing", ThingService.class);
} else {
return context.getBean("YourThing", ThingService.class);
}
}
// ... the rest of the methods removed for clarity ...
}
Then I placed a #Qualifier on the ThingService being injected into the #Component like so:
#Component
public class ThingComponent {
private final ThingService thingService;
#Inject
public ThingComponent(#Qualifier("PrimaryThingService") ThingService thingService) {
this.thingService = thingService;
}
}
Now when I rerun the test, I get the following output:
PrimaryThing -- Initialized
OurThingService -- Initialized
So this removes the #Primary annotation in place of using a named #Bean following a convention of "Primary{Interface}", stepping around the Spring's overeager hydration of non-#Primary annotated #Bean methods.

#Bean ResourceProcessor with #Autowired

In my Spring Boot 1.5.10 application with Spring Data REST and HATEOAS, I have a ResourceProcessor bean with an #Autowired service, like:
#Bean
public ResourceProcessor<Resource<Order>> orderResourceProcessor() {
return new ResourceProcessor<Resource<Order>>() {
#Autowired
private OrderHandler orderHandler;
#Override
public Resource<Order> process(Resource<Order> resource) {
Order order = resource.getContent();
Payment payment = orderHandler.payment(order);
resource.add(makeLink(payment));
return resource;
}
private Link makelink(Payment payment) {
return new Link(/*...*/);
}
};
}
When the #Autowired service is added, the resource processor bean is no longer triggered, unfortunately; i.e., when OrderHandler is commented out, the resource processor runs as it should.
Can a ResourceProcessor use #Autowired services; and, if so, what's the right way to construct it?
This part of the #Bean annotation javadoc should interest you :
#Bean Methods in #Configuration Classes
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. This ensures that references
between beans are strongly typed and navigable. Such so-called
'inter-bean references' are guaranteed to respect scoping and AOP
semantics, just like getBean() lookups would.
Example :
#Bean
public FooService fooService() {
return new FooService(fooRepository());
}
#Bean
public FooRepository fooRepository() {
return new JdbcFooRepository(dataSource());
}
It means that you have not to use #Autowired to set the dependency inside the #Bean declaration but reference another method annotated with #Bean.
But do you really need to set the dependency to create your bean ?
No at all. The OrderHandler is used only during the process() invocation.
So you can simply inject OrderHandler at the same level that the method annotated with #Bean and using it in the anonymous class :
#Autowired
private OrderHandler orderHandler; // only change
#Bean
public ResourceProcessor<Resource<Order>> orderResourceProcessor() {
return new ResourceProcessor<Resource<Order>>() {
#Override
public Resource<Order> process(Resource<Order> resource) {
Order order = resource.getContent();
Payment payment = orderHandler.payment(order);
resource.add(makeLink(payment));
return resource;
}
private Link makelink(Payment payment) {
return new Link(/*...*/);
}
};
}
I guess you can Autowire orderHandler to outer class. In your way it will not work as you create the instance of ResourceProcessor yourself.
#Autowired
private OrderHandler orderHandler;
#Bean
public ResourceProcessor<Resource<Order>> orderResourceProcessor() {
return new ResourceProcessor<Resource<Order>>() {
#Override
public Resource<Order> process(Resource<Order> resource) {
Order order = resource.getContent();
Payment payment = orderHandler.payment(order);
resource.add(makeLink(payment));
return resource;
}
private Link makelink(Payment payment) {
return new Link(/*...*/);
}
};
}

Loading Beans based on hostname

I am writing services in Spring boot that get their configurations from Spring cloud. These services are multi-tenant and the tenant is based on the host name.
what I have now is
public class MyController {
#Autowired
public MyController(MyServiceFactory factory) {
...
}
#RequestMapping("some/path/{id}")
ResponseEntity<SomeEntity> getSomeEntity(#RequestHeader header, #PathVariable id) {
return factory.getMyService(header).handle(id);
}
}
where MyServiceFactory looks something like...
public class MyServiceFactory {
private final HashMap<String, MyService> serviceRegistry = new HashMap<>();
public MyService getMyService(String key) {
return serviceRegistry.get(key);
}
MyServiceFactory withService(String key, MyService service) {
this.serviceRegistry.put(key, service);
return this;
}
}
then in a configuration file
#Configuration
public ServiceFactoryConfiguration {
#Bean
public MyServiceFactory getMyServiceFactory() {
return new MyServiceFactory()
.withService("client1", new MyService1())
.withService("client2", new MyService2());
}
}
While what I have now works, I don't like that I need to create a factory for every dependency my controller may have. I'd like to have my code look something like this...
public class MyController {
#Autowired
public MyController(MyService service) {
...
}
#RequestMapping("some/path/{id}")
ResponseEntity<SomeEntity> getSomeEntity(#PathVariable id) {
return service.handle(id);
}
}
with a configuration file like
#Configuration
public class MyServiceConfiguration() {
#Bean
#Qualifier("Client1")
public MyService getMyService1() {
return new MyService1();
}
#Bean
#Qualifier("Client2")
public MyService getMyService2() {
return new MyService2();
}
}
I can get the code that I want to write if I use a profile at application start up. But I want to have lots of different DNS records pointing to the same (pool of) instance(s) and have an instance be able to handle requests for different clients. I want to be able to swap out profiles on a per request basis.
Is this possible to do?
Spring profiles would not help here, you would need one application context per client, and that seems not what you want.
Instead you could use scoped beans.
Create your client dependent beans with scope 'client' :
#Bean
#Scope(value="client",proxyMode = ScopedProxyMode.INTERFACES)
#Primary
MyService myService(){
//does not really matter, which instance you create here
//the scope will create the real instance
//may be you can even return null, did not try that.
return new MyServiceDummy();
}
There will be at least 3 beans of type MyService : the scoped one, and one for each client. The annotation #Primary tells spring to always use the scoped bean for injection.
Create a scope :
public class ClientScope implements Scope {
#Autowired
BeanFactory beanFactory;
Object get(String name, ObjectFactory<?> objectFactory){
//we do not use the objectFactory here, instead the beanFactory
//you somehow have to know which client is the current
//from the config, current request, session, or ThreadLocal..
String client=findCurrentClient(..);
//client now is something like 'Client1'
//check if your cache (HashMap) contains an instance with
//BeanName = name for the client, if true, return that
..
//if not, create a new instance of the bean with the given name
//for the current client. Easiest way using a naming convention
String clientBeanName=client+'.'+name;
Object clientBean=BeanFactory.getBean(clientBeanName);
//put in cache ...
return clientBean;
};
}
And your client specific beans are configured like this :
#Bean('Client1.myService')
public MyService getMyService1() {
return new MyService1();
}
#Bean('Client2.myService')
public MyService getMyService2() {
return new MyService2();
}
Did not test it but used it in my projects. Should work.
tutorial spring custom scope

Categories

Resources