I am refactoring existing code and need to get an applicationContext reference in an abstract class.
Simplifying, here are two classes illustrating the issue:
public abstract class BaseCheck {
private ApplicationContext applicationContext;
protected AbstractConfig configClass;
@Autowired
// not called before setConfigClass called so null
public final void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public AbstractConfig getConfigClass() {
return configClass;
}
public void setConfigClass(String configClass) {
// fails due to null applicationContext
this.configClass = (AbstractConfig) applicationContext.getBean(configClass);
}
}
@Component
@DependsOn({"myConfig"})
public class FirstCheck extends BaseCheck {
public FirstCheck() {
setConfigClass("myConfig");
}
}
I can't get applicationContext injected. This code annotates the setter as described in https://www.baeldung.com/spring-autowired-abstract-class
I also tried implementing ApplicationContextAware, but that does not work for an abstract class either.
Does anyone know how I can get it to work?
The problem is that you are trying to access the ApplicationContext
in the constructor. At this point the setApplicationContext
method hasn't been called as Spring is only able to call it after the object has been constructed. You are accessing it during construction and thus a NullPointerException
.
What you can do is either inject the ApplicationContext
into the constructor so you can access it. Instead of using the ApplicationContext
to lookup the bean use an @Qualifier
and inject the correct config into the constructor instead of using setConfigClass
public abstract class BaseCheck {
protected AbstractConfig configClass;
protected BaseCheck(AbstractConfig configClass) {
this.configClass=configClass;
}
public AbstractConfig getConfigClass() {
return configClass;
}
}
@Component
public class FirstCheck extends BaseCheck {
public FirstCheck(@Qualifier("myConfig") AbstractConfig configClass) {
super(configClass);
}
}
This way you don't need to access the ApplicationContext
. You already had the name in the constructor anyway as well on the @DependsOn
(which can now be removed), so moving it to an @Qualifier
to get the correct instance should do it.