I have two spring bean classes which are implemeting the same interface.
public interface Abc()
{
String getNumber();
}
The two classes are
#Service
public class SomeClass implements abc
{
#Override
public class getNumber()
{
}
}
#Service
public class SomeClass1 implements abc
{
#Override
public class getNumber()
{
}
}
In my Service class.
#Service
public class Demo
{
#Autowired
private Abc abc;
}
}
I got an error "required a single bean, but 2 were found"
For that error i can have the chance to put #Primary in the top of one of the bean.
But i have only way to say "one bean configuration" based on the value which i will get in runtime(From the database).
Can you please suggest me a way.
You can autowire a list of interfaces and then choose the right one. You can write:
#Autowired
List<Abc> abcs;
this will result in a list of implementations of the interface. In your method body you can then choose the right one.
Couple of ways you can autowire the correct implementation.
Change your autowired field name to the same name of the implementation class (in camelcase)
#Autowired
private Abc someClass;
This will attempt to find an implementation of interface 'Abc' with the classname 'SomeClass'.
Another way is to add a bean name to your service annotation
#Service("someClass")
public class SomeClass implements abc
This can then be autowired like the following
#Autowired
#Qualifier("someClass")
private Abc SomeClass;
I think the problem he is asking about how to configure two implentation and also using the right bean dynamically(based on data in DB) . It seems this is the an example for factory pattern
Psuedo Code
Class SomeFactory{
#Autowired
private Abc someClass;
#Autowired
private Abc someClass1;// keeping bean Name same as class name would solve bean finding issue
public Abc getBeanFor(String type) {
if("someClass".equals(type)
return someClass;
return someClass1;
}
}
Class TestClass{
#Autowired
private SomeFactory factory ;
private void someProcess() {
// Read type from DB for data
factory.getBeanFor(typeReadFromData)
.process();
}
}
Related
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;
}
}
I'm trying to create the following structure:
public interface A {string calculateA(){....}}
#Service("policy1")
public class APolicy1 implements A {
#Override
public String calculateA(){...}
}
#Service("policy2")
public class APolicy2 implements A {
#Override
public String calculateA(){...}
}
and another interface
public interface B{string calculateB(){....}}
#Service("policy1")
public class BPolicy1 implements B{
#Override
public String calculateB(){...}
}
#Service("policy2")
public class BPolicy2 implements B{
#Override
public String calculateB(){...}
}
and the classes that uses these beans:
#service
#Primary
public class Arouter implements A{
#Autowired
Map<String, A> AServices;
#Autowired
PolicyResolver policyResolver;
#Override
public String calculateA(){
String policy = policyResolver.getPolicy();
AServices.get(policy).CalculateA();
}
#service
#Primary
public class Brouter Implements B{
#Autowired
Map<String, B> AServices;
#Autowired
PolicyResolver policyResolver;
#Override
public String calculateB(){
String policy = policyResolver.getPolicy();
AServices.get(policy).CalculateB();
}
note: the idea here is to delegate to the correct business logic by some kind of policy
for example, if i'm running in policy a context and need to trigger Service B, then the router will call BPolicy2 method.
but I'm getting:
org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'policy1' for bean class [...apackage.APolicy1] conflicts with existing, non-compatible bean definition of same name and class [...bpackage.BPolicy1]
I would expect that since these are different bean types I would be able to give them the same name
edit: I have a solution: add a prefix to the beans the name and the routers will add the prefix to the policyResolver return value, but I'm it's less elegant
#qualifier annotation can be used to differentiate between different beans.
Here is a link with small example of #qualifier annotation usage in Spring :
https://memorynotfound.com/handling-multiple-autowire-dependencies-with-spring-qualifier/
Consider there are 2 classes:
When creating bean A using factory.getbean(), the bean gets created but the property coldata is null inspite of initializing to new hashmap.
#Component
#Scope("prototype")
public class A{
private Map<String, Map<String,String>> coldata = new HashMap<String, Map<String,String>>();
}
#Service
public class B{
#Autowired
private BeanFactory factory;
public void test(){
A a= (A)factory.getBean("A");
System.out.println(a.coldata)
}
}
There's a wrong approach at first to correct.
First of all, as #Sun says: correct the code and make that map as public or give that field a getter at least.
Secondly
If you use autowiring don't use beanFactory:
Class A is annotated as Autowired and as a Component. If you want an instance of that class from the container just use an autowired instance in class B:
#Service
public class B{
#Autowired
private A a;
public void test(){
System.out.println(a.coldata)
}
}
avoid using the getBean method of the BeanFactory/ApplicationContext classes, especially if you want to use autowiring.
Here's a good explanation on why you should avoid using that method:
Why is Spring's ApplicationContext.getBean considered bad?
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.
I have to create a instance of a class, that have autowired elements, for test.
public class MyClass extends SomeOtherClass {
#Autowired
public MyClass(OtherClass1 one, OtherClass2 two){
super(one, two)
}
}
How can i in code create instance of this class, with the arguments wired in though spring?
Your test can be made Spring-aware if you use the SpringJUnit4ClassRunner to read in your Spring Context to be used in the test. For instance:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"the-config.xml"})
public final class MyClassTests {
#Autowired
private MyClass testee;
#Test
public void testSomething() {
assertThat(testee).doesSomethingExpected();
}
}
Note that you should reuse as much of your production config as possible and not create a parallel Spring Context config that mirrors it.
Instead of passing the other elements in as constructor arguments, you Autowire them as properties. Spring will then inject the objects.
public class MyClass extends SomeOtherClass {
#Autowired
private OtherClass1 one;
#Autowired
private OtherClass2 two
public MyClass(){
super(one, two)
}
}
Edit: Based on http://www.mkyong.com/spring/spring-auto-wiring-beans-with-autowired-annotation/, adding #Autowired to the constructor is also valid.
If you want to Autowire MyClass, you must annotate it with #Component or a similar annotation such as #Service.
#Component
public class MyClass extends SomeOtherClass
Then, you can use it in other classes
public class ClassThatUsesMyClass {
#Autowire
private MyClass myClass;
}