Spring bean decoration without ambiguity errors - java

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.

Related

Why do we use a qualifier when we can have a name for the bean?

Why do we use qualifiers with #Bean when we can have different names for different beans of the same type (class)?
#Bean
#Qualifier("fooConfig")
public Baz method1() {
}
Isn't the following code more clean?
#Bean("fooConfig")
public Baz method1() {
}
If I create two beans of the same type with different names (using #Bean annotation), then can we inject them specifically using the #Qualifier annotation(can be added on field/constructor parameter/setter) in another bean?
#Bean("fooConfig")
public Baz method1(){
}
#Bean("barConfig")
public Baz method2(){
}
// constructor parameter of a different bean
final #Qualifier("fooConfig") Baz myConfig
If the above is true, then where do we use #Qualifier (with #Bean or #Component) instead of giving the bean a name as shown below?
#Bean
#Qualifier("fooConfig")
public Baz method1(){
}
#Bean
#Qualifier("barConfig")
public Baz method2(){
}
// constructor parameter of a different bean
final #Qualifier("fooConfig") Baz myConfig
Beans have names. They don't have qualifiers. #Qualifier is annotation, with which you tell Spring the name of Bean to be injected.
No.
Default Qualifier is the only implementation of the interface(example is below, 4th question) or the only method with a particular return type. You don't need to specify the #Qualifier in that case. Spring is smart enough to find itself.
For example:
#Configuration
public class MyConfiguration {
#Bean
public MyCustomComponent myComponent() {
return new MyCustomComponent();
}
}
If you will try to inject myComponent somewhere, Spring is smart enough to find the bean above. Becaude there is only one Bean with return type MyCustomComponent. But if there was a couple of methods, that would return MyCustomComponent, then you would have to tell Spring which one to inject with #Qualifier annotation.
SIDENOTE: #Bean annotation by default Spring uses the method name as a bean name. You can also assign other name like #Bean("otherComponent").
You have one Interface, and a couple of Classes implementing it. You inject bean of your interface. How can Spring know which Class should be used?
This is you interface:
public interface TestRepository{}
This is your implementation 1:
#Repository
public class Test1Repository implements TestRepository{}
Your implementation 2:
#Repository
public class Test2Repository implements TestRepository{}
Now you are injecting it like:
private final TestRepository testRepository;
public TestServiceImpl(TestRepository testRepository) {
this.testRepository= testRepository;
}
QUESTION! How is Spring supposed to know which class to inject? Test1 or Test2? That's why you tell it with #Qualifier which class.
private final TestRepository testRepository;
public TestServiceImpl(#Qualifier("test1Repository") TestRepository testRepository) {
this.testRepository= testRepository;
}
I Prefer different method to not using #Qualifier
Create common Interface
public interface CommonFooBar{
public String commonFoo();
public String commonBar();
}
Extends to each service
public interface FooService extends CommonFooBar {
}
public interface BarService extends CommonFooBar {
}
Then using it to your class
#Autowired
FooService fooService;
or
#Autowired
BarService barService;
so, we can defined the single responsibility to each interface and This kind of segregation is more readable to every junior.
I quite like a different way of working. Surely if you provide a unique name for your bean, then that is all you need?
Given the example below, its easy to see that Spring will name the beans based on the method name used to create the beans. In other words, if you give your beans sensible names, then the code should become self-explanatory. This also works when injecting beans into other classes.
The end result of this is:
Spring will name your beans based on the method used to create them.
If you import a bean, Spring will try to match on the bean name.
If you try to import a bean that does not match the name, Spring will attempt to match the class.
If your injected field name does not match the bean name and there are more than one instance of your bean, Spring will throw an exception on startup as it won't know which one to inject.
Lets not over-complicate Spring.
#Bean
mqConnectionFactory() {
ConnectionFactory connectionFactory = new MQXAConnectionFactory();
return connectionFactory;
}
#Bean
public ConnectionFactory pooledConnectionFactory(ConnectionFactory mqconnectionFactory) {
JmsPoolConnectionFactory connectionFactory = new JmsPoolConnectionFactory();
connectionFactory.setConnectionFactory(mqConnectionFactory);
return connectionFactory;
}
#Bean
public ConnectionFactory cachingConnectionFactory(ConnectionFactory mqConnectionFactory) {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setTargetConnectionFactory(mqConnectionFactory);
return connectionFactory;
}
#Bean
public JmsTemplate jmsTemplate(ConnectionFactory cachingConnectionFactory) {
JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setConnectionFactory(cachingConnectionFactory);
return jmsTemplate;
}
#Bean
public DefaultMessageListenerContainer messageListenerContainer(ConnectionFactory pooledConnectionFactory) {
DefaultMessageListenerContainer container = new DefaultMessageListenerContainer();
container.setConnectionFactory(pooledConnectionFactory);
...
return container;
}

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 prevent instantiation and use intended implementation

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;
}
}

How to ask for Prototype bean in Spring service class without applicationContext

I have a component defined with prototype scope. I want to use that component in my service class. I want spring to provide me a new instance of that Bean everytime I call for it.
Component Class:
#Getter
#Setter
#Component
#Scope("prototype")
public class ProtoTypeBean {
//.. Field variables
}
Service Class:
#AllArgsConstructor
#Service
public class ServiceClass {
ProtoTypeBean prototypeBean;
ArrayList<ProtoTypeBean> prototypeBeans;
public void demoMethod(ArrayList<String> someArrayList) {
for(var singleString: someArrayList) {
prototypeBean.setFieldValue(singleString);
prototypeBeans.add(prototypeBean);
}
System.out.println(prototypeBeans.toString());
}
}
By using this configuration, I am getting the same instance of ProtoTypeBean in my prototypeBeans ArrayList. The question is, how would I make Spring understand to give me a new instance of prototypeBean every time I am calling it into the foreach loop?
I found I can use ApplicationContext.getBean() to get a new instance of the Bean in foreach loop but I also heard that it's a bad practice. So kindly help me with the best practice.
Use an ObjectProvider to lazily get the result you want. However the first prototype scoped bean will not be represented in the list of beans as, well they are prototype scoped.
#AllArgsConstructor
#Service
public class ServiceClass {
private final ObjectProvider<ProtoTypeBean> provider;
public void demoMethod(ArrayList<String> someArrayList) {
PrototypeBean pb = provider.getIfUnique();
for(var singleString: someArrayList) {
pb.setFieldValue(singleString);
pb.add(prototypeBean);
}
System.out.println(prototypeBean.toString());
}
}
Also if you don't need all the dependency injection, proxy creation etc. for your object then why bother. There is nothing wrong with just the new keyword in a Spring application. Not everything has to be managed by Spring.
Set up your prototype bean similar to this:
#Getter
#Setter
#Component
#Scope("prototype")
public class ProtoTypeBean {
final private String param;
public ProtoTypeBean(final String p) {
this.param = p;
}
}
Now, in your service class use a BeanFactory to create the beans for you:
#Service
#AllArgsConstructor
public class ServiceClass {
private final BeanFactory factory;
private List<ProtoTypeBean> prototypeBeans;
#Autowired
public ServiceClass(final BeanFactory f) {
this.factory = f;
}
public void demoMethod(List<String> someArrayList) {
this.prototypeBeans = someArrayList
.stream()
.map(param -> factory.getBean(ProtoTypeBean.class, param))
.collect(Collectors.toList());
}
}
I came across this issue recently. I am sure there must be a better way than mine, but this is how I did it:
public class ServiceClass {
ArrayList<ProtoTypeBean> prototypeBeans = new ArrayList<>();
#Autowired
ApplicationContext ctx;
public void demoMethod(ArrayList<String> someArrayList) {
for(var singleString: someArrayList) {
//magic is in below line.. getting a bean from ApplicatioContext.
ProtoTypeBean prototypeBean= ctx.getBean("protoTypeBean"); //Or ctx.getBean(ProtoTypeBean.class);
prototypeBean.setFieldValue(qBean.getFieldValue());
prototypeBeans.add(prototypeBean);
}
System.out.println(prototypeBeans.toString());
}
This way, Spring container always give you a new instance. And it is totally managed by Spring container.
The way you tried it, I tried that as well, but it would always inject one instance at the time of autowiring, hence defeating the purpose of prototyping.
You could have gone the route of using new Keyword. But then that is just regular Java instantiation and I think that new instance is not managed by Spring because it is annotated with #Component instead of #Configuration. I could be wrong here though.

Changing a class annotated #Component to #Bean annotated method

I have a class that is annotated #Component that was then #Autowired into another class. However, I need to remove this #Component annotation and instead, create it with an #Bean annotated method in the class where its was previously autowired.
Where previously the classes looked like:
#Component
public class MyClass implements IMyClass
{
// Stuff
}
#Configuration
public class MyUsingClass
{
#Autowired
private IMyClass myClass;
private void methodUsingMyClass()
{
myClass.doStuff();
}
}
So now I have removed the #Component annotation and written a #Bean annotated method like this:
public class MyClass implements IMyClass
{
// Stuff
}
#Configuration
public class MyUsingClass
{
#Bean
public IMyClass getMyClass()
{
return new MyClass();
}
....
}
My question is around replacing the previous call of myClass.doStuff() to use the new bean. Do I now pass in a parameter of type MyClass to the private method:
private void methodUsingMyClass(final MyClass myClass)
{
myClass.doStuff();
}
... or do I call this method directly (doesn't seem the correct way to me):
private void methodUsingMyClass()
{
getMyClass().doStuff();
}
... or are neither of these correct?
I think you misunderstand the #Bean annotation. It can be used to create a Bean. So basically spring will scan all classes, will find your #Bean and create a Bean, not more. You can now use this bean, like if you would use one created with <bean></bean>. To actually use the bean you need to either get it from ApplicationContext or #Autowire it. Of course you can still use that function like any other function in your code, to create a new instance of that object, but that would contradict to what you want to achieve with beans
Using Annotations that solutions
public class MyClass implements IMyClass{
private OtherClassInjection otherClassInjection;
private OtherClassInjection2 otherClassInjection2;
MyClass(OtherClassInjection otherClassInjection, OtherClassInjection2 otherClassInjection2){
this.otherClassInjection=otherClassInjection;
this.otherClassInjection2=otherClassInjection2;
}
public void useObject(){
otherClassInjection.user();
}
}
#Bean(name = "myClass")
#Autowired
#Scope("prototype") //Define scope as needed
public MyClass getMyClass(#Qualifier("otherClassInjection") OtherClassInjection otherClassInjection,
OtherClassInjection2 otherClassInjection2) throws Exception
{
return new MyClass(otherClassInjection, otherClassInjection2);
}
that logical, it's work injection #Autowired when create a Bean if context are know that bean, that you will to want inject.
I'm use that way.

Categories

Resources