I have the following scenario where I am running into null pointer exception because bean not getting initialized and leading to failure in my server failing to boot.
There is a newly introduced call in PostConstruct annotated method which is failing. The same call is being made in another method which is not in PostConstruct which executes correctly and not cause any issue.
#Component
#Lazy
#Primary
class Parent{
#Autowired
private DesignContextService designContextService;
#PostConstruct
private void init(){
designContextService.getMethod();// fails
}
private void someFunction(){
designContextService.getMethod();// executes successfully
}
}
}
Class DesignContextService{
#Autowired
private ContextService contextService;
public void getMethod(){
contextService.isContextCreated();
...
}
// Below classes present in another jar
class ContextService{
#Inject
public ContextAdapter contextAdapter;
public void isContextCreated(){
contextAdapter.isEstablished();// contextAdapter is null . Throws exception here
}
}
}
Error Stack trace :
at
Caused by org.springframework.beans.factory.BeanCreationException : Error creating bean ...
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:137)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:409)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1620)
This is because of the #lazy annotation. As specified in the documentation:
The default behavior for ApplicationContext implementations is to eagerly pre-instantiate all singleton beans at startup. Pre-instantiation means that an ApplicationContext will eagerly create and configure all of its singleton beans as part of its initialization process. Generally this is a good thing, because it means that any errors in the configuration or in the surrounding environment will be discovered immediately (as opposed to possibly hours or even days down the line).
Please look into the following link for reference: Using Spring #Lazy and #PostConstruct annotations
Spring: how to initialize related lazy beans after main bean creation
Related
I once read that for setter injection, the dependencies are not injected until they are needed. However, when I run a little test on that, I see that in using setter injection, dependencies are injected at application startup time. Actually, when is setter injection being called in the Spring bean life cycle ? and what does it mean by "dependencies are not injected until they are needed" ?
#Service
public class MainService {
private DependencyService dependencyService;
#Autowired
public void setDependencyService(DependencyService dependencyService) {
this.dependencyService = dependencyService;
}
#PostConstruct
public void afterConstruct() {
System.out.println("Created MainService bean");
if (service != null) {
System.out.println("DependencyService is injected");
}
}
}
#Service
public class DependencyService {
#PostConstruct
public void afterConstruct() {
System.out.println("created DependencyService bean");
}
}
On application startup, the console result:
Created DependencyService bean
Created MainService bean
DependencyService is injected
Dependencies are always injected right after or during the bean is instantiated no matter you use field injection , setter injection or constructor injection .
So it depends on when the bean is initialised. By default all beans will be initialised eagerly at startup which means that their dependencies are also injected at startup
It is generally a desirable behaviour as it allows you to discover error due to bean configuration at startup rather than several hours or even days later.
You can change a bean to be lazy initialised until they are needed by annotating it as #Lazy. So if you want MainService to be lazy initialised until it is accessed (i.e. its setter injection does not happen at start up) , you have to :
#Service
#Lazy
public class MainService {
}
I am writing junits for my spring boot based application, and my beans depends on some configuration parameters specified in application-.properties.
In my configuration class where I am generating beans,
#Configuration
public class AppConfig{
#Value("${MyProperty}")
private String myProperty;
#Bean
public myBean bean1() throws MyException{
if(myProperty.contentEquals("abc"){
throw MyException("Value abc not allowed for bean1");
}
}
In my junit I want to detect this scenario , if in my junit I set the profile and run it errors out saying 'Application startup failed' and not reach my before method or test method.
How do I handle this so that the junit does not fail and I am able to detect the myexception as well.
Basically what I need is the application context creation to fail, but my unit tests to pass.
Thanks !
beans depends on some configuration parameters specified in application-.properties
This can be controlled by the #ConditionalOnProperty annotation directly on your bean definition. Therefore instead of throwing an exception in your test bean, you won't have it registered at all provided your config is not there:
I am writing junits for my spring boot based application, and my beans depends on some configuration parameters specified in application-.properties. In my configuration class where I am generating beans,
#ConditionalOnProperty(name = "myProperty", havingValue = "'hiThere'")
#Bean
public myBean bean1() {
//..
}
If you want to invoke some SpEL operation on the property you can use #ConditionalOnExpression:
#ConditionalOnExpression("${myProperty}.contains('ere')")
#Bean
public myBean bean1() {
//..
}
This is an example from the Spring documentation, section 6.12.5:
#Configuration
public class ServiceConfig {
#Autowired
private AccountRepository accountRepository;
#Bean
public TransferService transferService() {
return new TransferServiceImpl(accountRepository);
}
}
My question is: why must it happen that accountRepository is created before it's used by new TransferServiceImpl()? Offhand, I don't see how Spring could know that the second one depends on the first one being set up (unless it goes through the transferService() bytecode). Is it because something about the order in which Spring does things guarantees that the #Autowired variable is processed before the #Bean method could possibly be called? What is the processing order? What kinds of circumstances could cause Spring to process these out of order?
The reason I'm asking is that I have a case where something like this isn't working, i.e. the new is being executed with a null argument. Either the #Autowired variable is being set up too late, or it isn't set up at all (my guess is the latter, based on some log4j.logger.org.springframework.beans debugging output, but I'm not sure). The situation is of course much more complex--it's a largish application, and there are a few more #Autowired and #Bean definitions in the configuration class. Using #DependsOn hasn't helped. It will take a lot of time to narrow down the problem by deleting code until I can get a minimal example, but I wanted to see if I could get some insight into the problem by learning more details about how Spring processes things, before starting down the difficult code reduction path.
why must it happen that accountRepository is created before it's used
by new TransferServiceImpl()?
It doesn't. accountRepository may be seen to be null.
From the note in the documentation you linked (its more current version)
Make sure that the dependencies you inject that way are of the
simplest kind only. #Configuration classes are processed quite early
during the initialization of the context and forcing a dependency to
be injected this way may lead to unexpected early initialization.
Whenever possible, resort to parameter-based injection as in the
example above.
Also, be particularly careful with BeanPostProcessor and
BeanFactoryPostProcessor definitions via #Bean. Those should usually
be declared as static #Bean methods, not triggering the instantiation
of their containing configuration class. Otherwise, #Autowired and
#Value won’t work on the configuration class itself since it is being
created as a bean instance too early.
In summary, a Configuration class will end up being just another bean in the application context. As such, it will be processed by all registered BeanPostProcessor beans.
#Autowired is processed by AutowiredAnnotationBeanPostProcessor. Presumably, you're using AnnotationConfigApplicationContext which registers one automatically.
In your example, which is incomplete since
...but determining exactly where the autowired bean definitions are
declared is still somewhat ambiguous
However, we can assume some other configuration provided a bean definition for a AccountRepository bean. Once the application context instantiates the ServiceConfig bean, it can then post process it and inject #Autowired targets.
The only reason an #Autowired target could be null in a #Configuration bean instance is that you tried to read it before an AutowiredAnnotationBeanPostProcessor could process/inject it.
Consider a circular dependency. Take the #Configuration class in your snippet with an additional #ComponentScan of the following classes
#Component
class AccountRepository {
public AccountRepository(Foo foo) {}
}
#Component
class Foo {
public Foo(TransferService ts) {}
}
The #Configuration bean get initialized. AutowiredAnnotationBeanPostProcessor kicks off to process the accountRepository field. It looks for an AccountRepository bean and tries to initialize it. It needs a Foo bean to instantiate it (for constructor injection). It looks for a Foo bean and tries to initialize it. It needs a TransferService bean to instantiate it (for constructor injection). It looks for a TransferService bean and finds the #Bean factory method. It invokes it. The accountRepository hasn't been initialized yet, so remains null. You can verify this by putting a breakpoint in the #Bean method and browsing the stack trace.
Had you used a parameter injection as suggested in the quote above
Whenever possible, resort to parameter-based injection as in the example above.
Spring would've crashed and warned you
Caused by:
org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'accountRepository': Requested bean is
currently in creation: Is there an unresolvable circular reference?
That's the workaround I ended up doing
I can't currently explain this.
I just move accountRepository to the method's param and annotated with #Autowired to solve this problem. But I don't know why. I think the reason is about Spring's init order.
#Configuration
public class ServiceConfig {
#Bean
public TransferService transferService(#Autowired AccountRepository accountRepository) {
return new TransferServiceImpl(accountRepository);
}
}
I claimed that:
Spring read bean definitions from java config
BeanFactory Create beans from defenitions
Then dependencies are injected by BeanPostProcessors
But it happened that it's not accurate:
#Configuration
#ImportResource("classpath:spring_config.xml")
public class JavaConfig {
#Autowired
MyBean bean;
#Bean
public Boolean isBeanAutowired(){
return bean != null;
}
}
The isBeanAutowired bean was initialized with true.
Question:
How does it happen that Autowired logic work before all beans in context were initialized?
Yes #Autowired is handled by a BeanPostProcessor. See org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor for more details and as an entrypoint if you try to find out more on this.
https://github.com/spring-projects/spring-framework/blob/master/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java
http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.html
In the end Spring is able to analyze the dependencies of a bean (the other beans that need to be wired) and determine an order, in which the beans will be initialized. Thereby it is possible, to autowire directly after creation of a bean. There is one exception, which occurs when Spring tries to resolve circular dependencies. Then Spring will create both beans and autowire them to each other. This works only limited though.
Follow up to my existing question, I am getting null session factory and hence my transaction is being failed. Here is what my class structure looks like
#Component
public class MyCachingObj extends HibernateMapper{
#PostConstruct
public void loadAll() {
...
getCurrentSession().getQuery(); //this method is in HibernateMapper
...
}
}
#Repository
public class HibernateMapper {
#Autowired
private SessionFactory _session;
public Session getCurrentSession() {
return _session;
}
}
What I have read so far, that #PostConstruct doesn't guarantee that spring is done with all beans. Then how can i make sure that spring is done processing and i have session ready to be picked up ?
EDIT
I ended up creating
#Component
#Transactional
#Repository
public class ApplicationStartup implements ApplicationListener<ContextRefreshedEvent>{..}
which had autowired sessions and autowired another Component class. However, i noticed that this onApplicationEvent method was being called twice. I ended up putting a boolean variable to load the underlying data only once.
Now few questions
Why onApplicationEvent is being called twice.
My cached object is annotated with #Component, Is it safe to assume that as long as that object is being used by #Autowired instance, it would remain singleton and would hold the data i loaded at startup ?