Spring Application context is null in a ApplicationContextAware class - java

I am getting NullPointerException inside an ApplicationContextAware class and I am not sure why.
Why is the applicationContext null on calling getBean(SomeClass.class) method
Following is my BeanProviderUtil class:
#Component
public class BeanProviderUtil implements ApplicationContextAware {
public static ApplicationContext applicationContext;
public static <T extends Object> T getBean(Class<T> beanClass) {
return applicationContext.getBean(beanClass);
}
public static Object getBean(String beanName) {
return applicationContext.getBean(beanName);
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
BeanProviderUtil.applicationContext = applicationContext;
}
}
On calling function
SomeOtherClass obj = BeanProviderUtil.getBean(SomeOtherClass.class)
I am getting NPE. On debugging, it comes out that applicationContext is not properly initialized by Spring.
Note
Is it because I have enabled lazy bean initialization?
However, I tried disabling lazy initialization by #Lazy(false) as well as from Global application.yml, but it didn't help.

So I figured out what was going wrong here by Myself.
I have set global lazy Initialization and that was the main culprit.
Since I was not Autowiring BeanProviderUtil, the bean was never created and hence applicationContext never got initialized.
Setting #Lazy(false) fixes the issue.
Earlier, on disabling #Lazy using #Lazy(false) it was not working because somehow Spring was not recognizing the BeanProviderUtil a component. Adding #ComponentScan to my test class does the trick.

Related

Application context is always null

I am using JUnit5, with sureFire version:
<maven.surefire.plugin.version>3.0.0-M5</maven.surefire.plugin.version>
and code
#ExtendWith(MockitoExtension.class)
#ContextConfiguration(classes =....ContextConfiguration.class)
#TestPropertySource({"classpath:application-${env:dev}.properties", "classpath:app-${env:dev}.properties"})
class TestRunner{
#Test
void testService(){
BeanUtils.getBean(...);
}
}
#Service
public class BeanUtil implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext; // NOSONAR
}
/**
* Use this method to manually autowire Spring Beans into classes that are not managed by Spring.
*
* #param beanClass - Class type of the required bean.
**/
public static <T> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
}
My ContextConfigurationClass has #ComponentScan directed to BeanUtils class, but the ApplicationContext is always null, not autowired.
I am using #ExtendWith(MockitoExtension.class) cuz i am also mocking in the test.
What JUnit5extension do i need to use in order to init it?
You should use SpringExtension.
You could also check this post to understand the differences between MockitoExtension and SpringExtension

How do I make a Spring ApplicationContext available for another class's unit test?

I have a SpringContext class which implements ApplicationContextAware so that I can access Spring beans from a regular Java class that isn't managed by Spring:
#Component
public class SpringContext implements ApplicationContextAware {
private static ApplicationContext context;
/**
* Returns the Spring managed bean instance of the given class type if it exists.
* Returns null otherwise.
* #param beanClass
* #return
*/
public static <T extends Object> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
#Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
// store ApplicationContext reference to access required beans later on
SpringContext.context = context;
}
}
This works well when running the actual application, but during unit testing the context object is null, so getBean() returns a NPE.
The getBean() method is being called by a spy in the test class, but not directly, i.e. the spy has a mocked dependency which then calls this method.
I've tried making the test class ApplicationContextAware but that doesn't help, and neither does #Injecting the ApplicationContext. How can I make sure that the context object is initialized?

Autowire a Spring bean in a Singleton class

I am trying to autowire a bean inside of a Singleton class, I know that it always a best idea to avoid manual autowiring, but this class is being used in so many places so I do not want to change the caller of this class.
Runner.java
#Component
public class RunnerClass {
#Autowired
public ConfigService configService;
}
ConfigService.java
#Service
public class ConfigService {
private ConfigServiceDAO = ConfigServiceDAO.getInstance();
}
ConfigServiceDAO.java
public class ConfigServiceDAO {
//Bean I want to autowire here....
#Autowired
ConfigServiceDAOBuilder DAOBuilder
public static ConfigServiceDAO getInstance() {
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder {
public static final ConfigServiceDAO INSTANCE = new ConfigServiceDAO();
private SingletonHolder() {}
}
}
DAOBuilder inside ConfigServiceDAO is always null, which makes sense because my understanding is when the class is instantiated manually, spring injection doesn't happen.
What could be the solution here if I want to keep ConfigServiceDAO as non spring component?
====EDIT====
I know it is possible to make ConfigServiceDAO as a spring component and autowire all dependencies.
But a lot of classes from different packages already call
ConfigServiceDAO.getInstance().someMethod()
So I guess the the right question is, what would be the best way to autowire a spring component to the class that is instantiated manually.
I don't know your use case but you cannot use #Autowired annotation outside a Spring bean.
However if you really need to access a Spring bean from a non Spring piece of code you can do it like below. However this is a very non Spring way of designing your dependencies.
import org.springframework.context.ApplicationContext;
public enum ApplicationContextHolder {
INSTANCE;
private ApplicationContext applicationContext;
public ApplicationContext getApplicationContext() {
return applicationContext;
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
}
Then you have a configuration class:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
#Configuration
public class SomeConfig {
#Autowired
private ApplicationContext applicationContext;
#PostConstruct
public void init() {
ApplicationContextHolder.INSTANCE.setApplicationContext(applicationContext);
}
}
Then in your DAO class you get a reference to the builder bean you are interested. Something like this:
public class ConfigServiceDAO {
public static ConfigServiceDAO getInstance() {
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder {
public static final ConfigServiceDAO INSTANCE =
ApplicationContextHolder.INSTANCE.getApplicationContext().getBean(ConfigServiceDAOBuilder.class).buildConfigServiceDAO()
private SingletonHolder() {}
}
}
Again this is a very non Spring way of doing things.
Spring processed #Autowired only in beans that it manages by itself.
So you have two choices:
Get Rid Of singleton - if you're using spring, its already a singleton in the application context. This is by far the best approach in general (assuming other parts of application that call your singleton are also spring driven). I don't think you should fear to change ConfigServiceDAO.getInstance.method() - refactoring tools in IDE will do the job.
If you can't do 1, Don't use autowired annotation in the singleton - its useless anyway, instead, when you have an application context configured (in listener that spring emits when the application started for example), get the access to the ConfigServiceDAOBuilder bean by calling appCtx.getBean(ConfigServiceDAOBuilder.class) and "inject it" manually by reflection, this is what Spring does with spring managed beans anyway:
#EventListener
public void onApplicationReadyEvent(ApplicationReadyEvent event) {
ConfigServiceDAOBuilder builder =
event.getApplicationContext().getBean(ConfigServiceDAOBuilder.class);
ConfigServiceDao dao = ConfigServiceDAO.getInstance();
dao.setDaoBuilder(builder); // or alternatively by reflection
}
As a side note, consider using method setDaoBuilder to be a package private to protect the singleton from some accidentally calling a setter
As far as I understand what you want: Create by Spring ConfigServiceDAOBuilder. After that inject it into non-managed object of class ConfigServiceDAO. You can do it after the Spring application context is instantiated. For example with CommanLineRunner:
#Component
public class CommandLineAppStartupRunner implements CommandLineRunner {
#Autowired
ConfigServiceDAOBuilder DAOBuilder
#Override
public void run(String...args) throws Exception {
ConfigServiceDAO.getInstance().init(DAOBuilder);
}
}
In ConfigServiceDAO has to be method init that helps to register all needed beans.
I'm confused after reading your comments, hence let me put in this way. What you are referring to manual autowiring is the Spring dependency injection way.
Whenever you are using any of the Spring Stereotype annotations with default scope instance is always Singleton.
Your ConfigService class has the problem.
Your are mixing up things, you should create a separate config class with #configuration and create Bean for the class ConfigServiceDAO, something like below
#Configuration
Class Config{
#Bean
public ConfigServiceDAO configServiceDAO( ){
return ConfigServiceDAO.getInstance();
}
}
then autowire the ConfigServiceDAO in the ConfigService class. With this Spring will resolve all of the dependency in correct order and DAOBuilder shouldn't be null.

AnnotationConfigApplicationContext has not been refreshed yet using ApplicationContextAware

I am using Spring beans in a non-spring framework and for that I have implemented the ApplicationContextAware to access the spring beans.
#Service
public class ApplicationContextProviderService implements ApplicationContextAware {
private static ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ApplicationContextProviderService.applicationContext = applicationContext;
}
public static <T> T getBean(Class<T> beanType) {
System.out.println("bean out: " + applicationContext);
return applicationContext.getBean(beanType);
}
}
I try to access a Spring service: ConnectionStateService from the non-spring class:
this.connectionStateService = ApplicationContextProviderService.getBean(ConnectionStateService.class);
I get the following error:
java.lang.IllegalStateException:
**org.springframework.context.annotation.AnnotationConfigApplicationContext#7f485fda has not been refreshed yet at
** org.springframework.context.support.AbstractApplicationContext.assertBeanFactoryActive(AbstractApplicationContext.java:1072) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE] at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1102) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE] at com.example.app.service.ApplicationContextProviderService.getBean(ApplicationContextProviderService.java:19) ~[classes/:na] at com.example.app.CustomFilter.<init>(CustomFilter.java:26) ~[classes/:na]
How to resolve this issue?
The issue is related when the application tries to call to ApplicationContext before the application runs, so you won't be able to have application context because it wasn't created. The solution is create a singleton for ConnectionStateService class so you won't need to create a service for calling the ApplicationContext.
public class ConnectionStateService {
// static variable instance of type Singleton
private static ConnectionStateService single_instance = null;
// private constructor restricted to this class itself
private ConnectionStateService() {
// some stuffs for initialize here
}
// static method to create instance of Singleton class
public static ConnectionStateService getInstance() {
if (instance == null)
instance = new ConnectionStateService();
return instance;
}
}
I think you are getting the bean when the context is not initialized
So actually be sure you are invoking this piece of code :
this.connectionStateService = ApplicationContextProviderService.getBean(ConnectionStateService.class);
after context is initialized

Why isn't #PostConstruct invoked on a bean implemented BeanFactoryPostProcessor & ApplicationContextAware?

I've tried to create a Spring bean and log all possible stages of his lifecycle.
But I've noticed that in this case a method annotated with #PostConstruct isn't invoked.
I suppose it happens because BeanFactoryPostProcessor and ApplicationContextAware force container to instantiate it as early as possible, but I don't know exactly.
Could you explain me, please?
Please consider a code snippet below:
public class F implements BeanFactoryPostProcessor, ApplicationContextAware {
#PostConstruct
private void postConstruct() {
System.out.println("Hi from postContruct()");
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("Hi from setApplicationContext()");
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("Hi from postProcessBeanFactory()");
}
}
And the output is:
Hi from setApplicationContext()
Hi from postProcessBeanFactory()
Try implementing org.springframework.beans.factory.InitializingBean instead of using #PostConstruct annotation. BeanFactoryPostProcessor may be one peculiar case which happens early enough in the lifecycle so that the lifecycle annotations don't function yet.
I know I had the same issue with #PreDestroy on my BeanFactoryPostProcessor configuration class. Using org.springframework.beans.factory.DisposableBean solved it.

Categories

Resources