It's kinda hard to explain... Hope the question isn't vague...
you can look at the code to get the idea...
ClassA.java
public class ClassA {
@Autowired
InterA abcd;
public void dododo() {
abcd.doit();
}
}
ClassB.java
@Component
public class ClassB implements InterA {
@Override
public void doit() {
System.out.println("hoo hoo");
}
}
ClassC.java
@Component("classc")
public class ClassC {
public void doFromAbove() {
ClassA cls = new ClassA();
cls.dododo();
}
}
Interface InterA.java
public interface InterA {
public void doit();
}
Configuration ClassConfig.java (on the same package of other java class files)
@Configuration
@ComponentScan
public class ClassConfig {
}
Main Method
public static void main(String[] args) {
try(AbstractApplicationContext appctx = new AnnotationConfigApplicationContext(ClassConfig.class)) {
ClassC obj = (ClassC) appctx.getBean("classc");
obj.doFromAbove();
}
}
When I execute the main method, the Autowired field "abcd" in ClassA
didn't get injected and results in a NullPointerException
It works only when I declare ClassA
as a @Component
and get it's bean... indirect autowiring is not happening
Should I decouple ClassA
from ClassC
and make everything loosely coupled?
Is there any simple annotation that I can use to tell Spring auto inject the @Autowired field even when the object is created in a tight coupled fashion?
Note
please don't tell me to use ApplicationContext in ClassC
to create the bean of ClassA
.
Any Spring Geek who could find an answer?
After intense googling, Spring Documentation Skimming, I'm convinced there are more possible solutions to this dilemma...
Possible Solutions:
Provider<T>
with @Autowired
FactoryBean<T>
with initialization code in getObject()
(but the bean returned by the factory is not spring managed and thus any autowired field in the prototype class will return NullPointerException)ApplicationContextAware
interface and get the context
object (not recommended)ApplicationContext
and use getBean()
(not recommended)Most Subtle approach among the above is JSR330 Provider
ClassA
@Component("classa")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class ClassA implements InterB {
private static int counter=0;
private int objectid = 0;
@Autowired
InterA abcd;
public ClassA() {
super();
this.objectid = ++counter;
}
@Override
public void dododo() {
System.out.println("instance number "+objectid++);
abcd.doit();
}
}
ClassB
@Component
public class ClassB implements InterA {
@Override
public void doit() {
System.out.println("hoo hoo");
}
}
ClassC
@Component("classc")
public class ClassC {
@Autowired
Provider<InterB> classAPrototypeobj;
public void doFromAbove() {
//you can do a for loop here and get a set of objects for use
InterB cls = (InterB) classAPrototypeobj.get();
InterB cls1 = (InterB) classAPrototypeobj.get();
cls.dododo();
cls1.dododo();
System.out.println(cls);
System.out.println(cls1);
}
}
Now it works flawlessly and the initialized object is spring managed too...
Note: JSR330 dependency has to be set in maven pom.xml
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>