Spring bean prevent instantiation and use intended implementation - java

Given that I have two implementation of a Processor interface:
One Synchronous:
#Service("Synchronous")
#Primary
public class SyncProcessor implements Processor { ... }
Other Async:
#Service("Asynchronous")
public class AsynchronousProcessor implements Processor { ... }
The class that uses these services:
public class TestController{
private final Processor processor;
public TestController(final Processorprocessor) {
this.processo r= processor;
}
}
These services and the controller class sit in a common library framework maven project (say project A) and they get injected as dependencies in other projects (e.g. project B & C).
I want project B & C to use SyncProcessor by default which it does since it is annotated with #Primary.
However, the async service gets also instantiated and I would like to prevent that.
It should only be instantiated when in the projects B and C I do the following:
#Bean("customAsync")
public Processor processor(MyRepo repo,
JobRunner jobRunner) {
return new AsynchronousProcessor (repo, jobRunner);
}
The problem I am currently having is AsyncProcessor instance is getting created multiple times. How can I prevent this from happening?
The other issue I am having is that even when I create the bean in project B, the synchronous bean is being called. How can I use async?

You can use #Profile so you can decide on startup what processor you use.
#Component
#Profile("sync")
public class SyncProcessor implements Processor {...}
#Component
#Profile("async")
public class SyncProcessor implements Processor {...}
This way, your project can decide which will get created by setting the spring.profiles.active property accordingly (sync or async). It's not really intended for libraries, so your use case isn't as simple as you intend,.

By default, the following will inject your #Primary Processor
#RestController
public class TestController{
private final Processor processor;
public TestController(final Processorprocessor) {
this.processor = processor;
}
}
If you want a processor other that #Primary injected, you have to specify it
#RestController
public class TestController{
#Autowired
#Qualifier("customAsync")
private final Processor processor;
public TestController(final Processorprocessor) {
this.processor = processor;
}
}

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");

How to inject a private field in a Component class while keep initiating with #Autowired in parent class in Spring?

I am learning Spring while I like the idea of using #Component and #Autowired to let Spring manage the dependent bean. For example, I have a Service class and Builder Class I can do with
// SomeService.java
#Service
public class SomeService {
// SomeBuilder is a #Component class
#Autowired
SomeBuilder someBuilder;
}
// SomeController.java
#Component
public class SomeController {
#Autowired
SomeService someSerivce;
}
Spring would take care of the creation of from SomeController to SomeService to SomeBuilder with the usage of #Autowired. However, now my SomeService class needs a private field which is NOT a Component class, just a plain context object, for example
// SomeService.java
#Service
public class SomeService {
#Autowired
SomeBuilder someBuilder;
private SomeContext someContext;
// Plan A: Using constructor to initiate the private field. However, I cannot use #Autowired to initiate SomeService in SomeController anymore as it requires a parameterless constructor
// Plan B: using #Autowired on constructor level, I cannot use this because SomeContext is not a #Component class
//public SomeService(SomeContext someContext) {
//this.someContext = someContext;
//}
// Plan C: This seems work but I kinda feel it's not the right way, as usually private field are initiated through constructor
//public void init(SomeContext someContext) {
// this.someContext = someContext;
//}
// demo usage of someContext
public someAnswer realMethod() {
System.out.println(someContext.getName());
}
}
Now I have no idea how to inject the someContext now, I used
plan A: Assign the private field using class constructor
plan B: Using #Autowired on constructor level
plan C: Using a wired method to assign the private field.
but I am not satisfied and don't have a clear way of doing the right approach.
First lets take a look at your plans and bust some myths/misunderstandings.
Plan A: Using constructor to initiate the private field. However, I cannot use #Autowired to initiate SomeService in SomeController anymore as it requires a parameterless constructor
Great plan, and the way to go. #Autowired doesn't depend on having a default constructor. It only indicates that you want the field to be injected with an object of that type. How that object comes to live (default constructor, constructor with arguments) doesn't matter for #Autowired. So that part of your understanding is just wrong.
using #Autowired on constructor level, I cannot use this because SomeContext is not a #Component class
If there is just a single constructor in a bean Spring will automatically use that to satisfy the dependencies. So in this case you don't need #Autowired. A bean doesn't have to be an #Component, a bean is just an instance of a class available to the application context. One way of achieving that is by marking it as an #Component but there are other ways as well. Like defining an #Bean method in in an #Configuration class to construct the bean.
#Configuration
#ComponentScan("your.base.package")
public class YourConfiguration {
#Bean
public SomeContext someContext() {
return new SomeContext();
}
}
Something along those lines. It will detect the #Component annotated classes through the #ComponentScan and will create a bean of type SomeContext for use as a bean.
Plan C: This seems work but I kinda feel it's not the right way, as usually private field are initiated through constructor
All your fields should be private not just the ones initialized in a constructor, so also the #Autowired ones. You don't want those fields to be, easily, accessible from the outside so they can be modified. So make them private.
That all being said, go with constructor injection over field injection or setters/methods for injection. It is clearer and less hidden than field injection and the way to go for mandatory dependencies (for optional dependencies you can use a setter/method).
So using the above config and below classes, it should "just work (tm)".
// SomeService.java
#Service
public class SomeService {
// SomeBuilder is a #Component class
private final SomeBuilder someBuilder;
private final SomeContext someContext;
public SomeService(SomeBuilder someBuilder, SomeContext someContext) {
this.someBuilder=someBuilder;
this.someContext=someContext;
}
}
// SomeController.java
#Component
public class SomeController {
private final SomeService someSerivce;
public SomeController(SomeService someService) {
this.someService=someService;
}
}

Spring bean decoration without ambiguity errors

I have the following scenario: A factory interface with 2 implementations, while the second one used as decorator to the first one.
public final class BaseMailFactory implements MailFactory {
#Autowired
private final ClassA classA;
#Autowired
private final ClassB classB;
public Mail createMail(){
.
.
.
}
}
public final class MetricAwareMailFactory implements MailFactory {
private final MailFactory mailFactory;
public Mail createMail(){
var mail = mailFactory.createMail();
return new MetricsAwareMail(mail);
}
}
#Configuration
public class MailFactoryConfiguration {
#Bean
public MailFactory metricsAwareMailFactory(){
return new MetricAwareMailFactory(???);
}
}
The wrapped object previously instantiated through spring container (context), hence all auto wired fields populated successfully. After creation of the second implementation I am struggle to find an elegant way to initialize the first instance without adding multiple implementations to MailFactory interface which leads to application startup errors due to ambiguity.
I know that I can use qualifies for that but they pollute my code.
I am looking for a way to instantiate a class through spring but without actually register it as a bean, in older spring versions I get to use anonymous beans for such purposes.
I found the #Primary annotation useful here:
#Configuration
public class MailFactoryConfiguration {
#Bean
#Lazy
MailFactory baseMailFactory(){
return new BaseMailFactory();
}
#Bean
#Primary
public MailFactory metricsAwareMailFactory(){
return new MetricAwareMailFactory(baseMailFactory());
}
}
I such way, both beans will be created but the primary one will be selected in case of multiple implementations.

Spring boot inject bean at runtime based on request endpoint/request param

I have an interface that has two implementations, and I'd like to conditionally inject either of the two implementations in a spring boot service.
The point is that the eligible implementation should be picked up based on the request message (JSON mapped to a POJO).
My searches leaded me to implement a FactoryBean to control selecting between those two implementations, and to keep the factory telling spring that the beans are not singleton (by returning false for the isSingleton method).
But if this is the right way, I am still not sure how to get the request message to check it and return the right bean.
Can you please tell me if I am on the right track for what I am trying to attain?
=============
UPDATE
I do not want to pollute my code and deal with managing the relation between my service and the dependencies' implementation in the service.
Considering that I will need to deal with more implementations in the future, I need my service to care only about its responsibility.
I need my service to have only one reference of the generic interface and deal with it in an abstracted way.
I need to find a spring-based way to choose the right implementation for each request based on a condition that is derived from the request itself, and inject it in the service.
One option is to inject both beans and conditionally pick the required bean. You can autowire classes implementing same interface into a Map.
Following example uses a factory class to hide the conditional check.
#Component("type1")
public class Type1 implements SomeInterface{}
#Component("type2")
public class Type2 implements SomeInterface{}
#Component
public class MyTypeFactory {
#Autowired
private Map<String, SomeInterface> typesMap;
public SomeInterface getInstance(String condition){
return typesMap.get(condition);
}
}
#Component
public class MyService {
#Autowired
private MyTypeFactory factory;
public void method(String input){
factory.getInstance(input).callRequiredMethod();
}
}
You could #Autowire both beans in the controller and decided based on the request which one to return.
Consider the below interface:
public interface MyInterface { ... }
Sample config:
#Configuration
public class MyConfig {
#Bean("first")
public MyInterface firstBean() { ... }
#Bean("second")
public MyInterface secondBean() { ... }
}
Sample controller:
#RestController
public class MyController {
#Autowire
#Qualifier("first")
public MyInterface first;
#Autowire
#Qualifier("second")
public MyInterface second;
#GetMapping
public MyInterface doStuff(#RequestBody body) {
if(shouldReturnFirst(body)){
return first;
} else {
return second;
}
}
}
Note that you should most likely not do it this way though, but have a single service, say MyService that should implement this logic for you.
#Component
public class MyService {
public MyInterface doStuff(body) {
if(shouldReturnFirst(body)){
// build your response here
} else {
// build your response here
}
}
}
And just delegate to the service from the controller
#GetMapping
public MyInterface doStuff(#RequestBody body) {
return myService.doStuff(body);
}
Spring has a concept of Conditional Bean...
Have a look here https://www.intertech.com/Blog/spring-4-conditional-bean-configuration/

Spring Boot - Autowire component using different profiles

I have a component EmbeddedRedis that depends on a configuration object RedisConfig parsed from the application's property file. There are different property files, corresponding to the possible application profiles that can be run. Thus, when run in profile master, the component EmbeddedRedis will be provisioned according to the master profile.
In a test class, that is supposed to set-up a local Redis cluster, I also require Redis objects provisioned according to all other profiles. I sketched my idea below using the #Qualifier annotation, which does not bring the desired result.
#Autowired #Qualifier("dev-cluster-master")
private Redis embeddedRedisMaster;
#Autowired #Qualifier("dev-cluster-slave-001")
private Redis embeddedRedisSlave1;
#Autowired #Qualifier("dev-cluster-slave-002")
private Redis embeddedRedisSlave2;
How can I archive the desired result in Spring Boot? If that doesn't work directly, would it also suffice to obtain the before-mentioned configuration objects parsed from the different property files.
#Component
#ConfigurationProperties(prefix = "spring.redis")
public class RedisConfig {
....
}
Thanks in advance!
You can do something like this:
Consider you have a class definition (Redis in your example)
public class CustomService {
private String name;
public CustomService(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
And a configuration class like:
#Configuration
public class Config {
#Bean
#Profile("master")
CustomService serverConfig1(){
CustomService service1 = new CustomService("master");
return service1;
}
#Bean
#Profile("slave")
CustomService serverConfig2(){
CustomService service1 = new CustomService("slave");
return service1;
}
}
which initiate 2 different objects based on current active profile. If current active profile is "master", then serverConfig1() will get executed, otherwise serverConfig2().
And finally autowired your service/object like this:
#Autowired
CustomService service;
This will depends on above executed bean definition in configuration file.
And property file should look like this:
spring.profiles.active=slave
So in this example, after executing above code, the value of 'name' in CustomService service; will be "slave" instead of "master", because current active profile is "slave" and thus "serverConfig2()" will get executed
You can do something like this: Consider you have an interface definition:
public interface SomeService {
String someMethod;
}
And two implemented class:
#Profile("production")
#Service
public class SomeServiceProd implements SomeService {
#Override String someMethod() {return "production";}
}
#Profile("development")
#Service
public class SomeServiceProd implements SomeService {
#Override String someMethod() {return "development";}
}
And use this service in test and main code:
#Autowired SomeService service;
If you need your component only with certain profile and you don't want to or can't create a common interface you could inject like this:
#Autowired
private Optional< Redis > redis;
You would have to check if present on each different object on every profile.
If you share a common interface use the other answer solution to create different beans implementing the interface per each profile.

Categories

Resources