I have an interface like this
public interface InterfaceA {
public String methodA();
}
and I have implemented it like this
public class ClassA implements InterfaceA {
#Override
public String methodA(){
return "HELLO";
}
}
I'm trying to reference a bean of this class in another class
public class ClassB {
#Autowired
private InterfaceA mybean;
String str = mybean.methodA();
}
I have the following bean configuration
<bean id="mybean" class="ClassA"></bean>
Most interesting point is if I remove all the declaration and implementation of the methodA in InterfaceA and ClassA and then try to just this
public class ClassB {
#Autowired
private InterfaceA mybean;
}
no error is shown.
In the other case the following error is shown when I try to run this application: "No qualifying bean of type [ClassA] found for dependency"
It is because of the livecyle of a bean and a java class!
in your ClassB you have two variables. mybean will been populated by Spring after the object instance ins created (by spring). But String str = mybean.methodA(); will be assinged as soon as the object instance is created. And at this point the variable mybean is still null, and therfor the instance creation will fail!
Solution: use #PostConstruct, spring init-method, or implement InitializingBean -- see this answer for an overview
public class ClassB {
#Autowired
private InterfaceA mybean;
private String str;
void afterPropertiesSet() {
String str = mybean.methodA();
}
}
Related
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();
}
}
Is it possible to Autowire fields in a dynamic class?
I am getting a class name from the database and I want to autowire this class
Short Answer
That's not possible. Spring needs to know what Beans there are for injecting them.
Long Answer
You could #Autowire every possible bean into a class and then cache them in a Map, where the Class represents the key, and the Object the value. See below simplified example:
public class MyClass{
private final Map<Class<?>, Object> cache = new HashMap<>();
#Autowired
public MyClass(Service1 s1, Service2 s2){
// registering the beans
cache.put(Service1.class, s1);
cache.put(Service2.class, s2);
}
public <T> T getService(String className) throws ClassNotFoundException{
// getting the bean
Class<?> clazz = Class.forName(className);
return (T) cache.get(clazz);
}
}
Not sure it's a good idea, but you can inject a class like mentionned here :
Injecting beans into a class outside the Spring managed context
You can try this:
import javax.annotation.PostConstruct;
#Component
public class ApplicationContextAccessor {
private static ApplicationContextAccessor instance;
#Autowired
private ApplicationContext applicationContext;
public static T getBean(Class clazz) {
return instance.applicationContext.getBean(clazz);
}
#PostConstruct
private void registerInstance() {
instance = this;
}
}
Read this post : https://www.helicaltech.com/uses-of-springs-applicationcontext-while-using-reflection/
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;
}
I seem to be having an issue with the way I have implemented autowiring in my Spring Batch application.For example if I use:
public class A{
#Autowired
BeanList beanList;
}
this works fine for Class A.In the sense that,beanList returns the values that it should.But if from a method from class A I am calling a method from a different class and then have the same
#Autowired
BeanList beanList
,beanList return a null.But autowiring seems to work fine across steps.I have
I'm not sure if I understood your question correctly. I assume, you have something like this:
public class A{
B aB;
#Autowired
BeanList beanList;
public void callToB() { aB.aMethod(); }
}
public class B {
#Autowired
BeanList beanList;
public void aMethod() {Assert.notNull(beanList);}
}
If this is correct, then the problem is likely that you didn't instantiate class B as a "spring bean".
The most simply way to do this would be to mark class B with #Component and to autowire it into class A.
public class A {
#Autowired
B aB;
or to instantiate it with an #Bean directly in class A
#Component
public class A {
#Autowired
BeanList beanList;
#Bean
B myBean() {return new B();}
public void callToB() { myBean().aMethod(); }
Does this describe and solve your problem?