I am not good at English. And I'm a "Spring Boot" beginner. Please understand.
https://spring.io/blog/2019/03/14/lazy-initialization-in-spring-boot-2-2#enabling-lazy-initialization
I have a question in this article.
In the writing...
"It's possible to enable laser initialization in any version of Spring Boot if you're getting your hands dirty and write a BeanFactoryPostProcessor. "
I would like to know what the difference is to use the "Lazy Annotation."
Using "#Lazy Annotation"
"...dirty and write a BeanFactoryPostProcessor. "
If you just want to configure certain beans to be lazy initialised , you can annotate those beans using #Lazy.It is convenient but rather static. It cannot handle the cases that if you want to have more dynamic behaviour to configure some beans to be lazy based on certain conditions.
BeanFactoryPostProcessor provides a way to modify the bean definitions after Spring context is initialised which means that we can use it to configure which beans to be lazy programmatically .
All beans by default are not lazy. So if we want to configure all beans to be lazy in order to increase the Spring startup time , we have to manually annotate all beans with #Lazy.It is not so convenient if we have many beans. So what the articles mentioned is that in SpringBoot 2.2 will have a new feature to make all beans to be lazy by default such that so we don't need to manually annotate #Lazy for all beans. Behind the scene , it does it by registering this BeanFactoryPostProcessor which simply do the followings:
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
for (String name : beanFactory.getBeanDefinitionNames()) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(name);
if (beanDefinition instanceof AbstractBeanDefinition) {
Boolean lazyInit = ((AbstractBeanDefinition) beanDefinition).getLazyInit();
if (lazyInit != null && !lazyInit) {
continue;
}
}
beanDefinition.setLazyInit(true);
}
}
If BeanFactoryPostProcessor you can tweak about bean definition (NOT bean instance). One of the tweak is the laziness property:
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
beanFactory.getBeanDefinition("YourBeanName").setLazyInit(true);
}
}
It is equivalent to set #Lazy on the initialization of YourBeanName bean
Related
I am fairly new to Spring Boot and it is my sense from looking at sample applications that if a Bean Factory is ever used, it is used "under the covers" by Spring Boot. Or are there cases when using Spring Boot that you would in fact want to explicitly obtain a bean using the Bean Factory?
Every once in a while, I access Spring ApplicationContext from a bean that I initialize with new (basically a non-Spring managed bean) as follows:
#Component
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext context;
public static ApplicationContext getApplicationContext() {
return context;
}
#Override
public void setApplicationContext(ApplicationContext ac)
throws BeansException {
context = ac;
}
}
and in wherever I need it:
SomeBean someBean = ApplicationContextProvider.getApplicationContext().getBean("testBean", TestBean.class);
This is because:
I have to access a singleton (say, a #Service or a #Repository) from a bean I initialize (new) and I cannot make my bean Spring managed for that case. (so no #Autowired).
I don't want to introduce #Configurable to the project because it brings in AspectJ, which might be overkill to introduce for this simple case.
I have more than 50 odd classes marked with #Service #Lazy.With this configuration I can not load those beans eagerly if I need for any of my requirement. Is there any solution like #Lazy(${user.property.bena.loading.type)} so that I can chnage the loading style of beans at my will with a toggle boolean switch.
Otherwise I find two solutions:
1 - Replace all #lazy with #Lazy(false) - This is risky and not a good way for 50 odd files
2 - Create duplicate classes one with #Lazy(true) another with #Lazy(false) using diffrent spring profiles - This will be code duplicity
From the official spring docs:
If present and set to true, the #Bean or #Component will not be initialized until referenced by another bean or explicitly retrieved from the enclosing BeanFactory.
Can you please elaborate your comment:
I can not load those beans eagerly if I need for any of my requirement
Because if you need those beans and those are injected into your required classes, those will be available at the time of first reference to those. That's the purpose that #Lazy is serving.
For dynamic lazy initialization you can create a BeanFactoryPostProcessor which check whether a particular bean need to be lazy init or no. Below implementation should be suffice.
Create a marker annotation e.g. LazyMarker
#Retention(RUNTIME)
#Target({TYPE, METHOD})
public #interface LazyMarker {
}
Create BeanFactoryPostProcessor which set lazy flag.
#Component
public class SamplePostProcessor implements BeanFactoryPostProcessor, EnvironmentAware {
private Environment environment;
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
if (Boolean.valueOf(environment.getProperty("lazy"))) {
for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
Class<?> beanType = beanFactory.getType(beanDefinitionName);
LazyMarker lazyValue = AnnotationUtils.findAnnotation(beanType, LazyMarker.class);
if (lazyValue != null) {
beanFactory.getBeanDefinition(beanDefinitionName).setLazyInit(true);
}
}
}
}
#Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
}
I am working on my first Spring Boot application and I have the following problem.
I want to set the that for default all beans are lazy loaded. I know that I can add the #Lazy to all my #Component beans but I want that for default all beans are setted at lazy...
In Spring Boot I don't have an XML configuration file or a configuration class but I only have an application.properties configuration file.
So, how can I set that the default behavior for all the bean is lazy=true
To implement a BeanFactoryPostProcessor that sets lazy initialization by default (which can be required if you are e.g. defining some of the beans dynamically, outside of your #Configuration class(es)), the following approach worked for me:
#Component
public class LazyBeansFactoryPostProcessor implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory( ConfigurableListableBeanFactory beanFactory ) throws BeansException {
for ( String name : beanFactory.getBeanDefinitionNames() ) {
beanFactory.getBeanDefinition( name ).setLazyInit( true );
}
}
}
This essentially puts the #Lazy annotation on all your #Component and #Services. You might want to invent a mechanism to annotate classes with #Eager if you go this route, or just hardwire a list in the LazyBeansFactoryPostProcessor above.
Further reading
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/config/BeanFactoryPostProcessor.html
Since the version 2.2.2.RELEASE of spring-boot you can use the property below in your application.properties file
spring.main.lazy-initialization=true
for further reading and a good example please refer to
https://www.baeldung.com/spring-boot-lazy-initialization
https://spring.io/blog/2019/03/14/lazy-initialization-in-spring-boot-2-2
I have a Hibernate Search ClassBridge where I want to use #Inject to inject a Spring 4.1 managed DAO/Service class. I have annotated the ClassBridge with #Configurable. I noticed that Spring 4.2 adds some additional lifecycle methods that might do the trick, but I'm on Spring 4.1
The goal of this is to store a custom field into the index document based on a query result.
However, since the DAO, depends on the SessionFactory getting initialized, it doesn't get injected because it doesn't exist yet when the #Configurable bean gets processed.
Any suggestions on how to achieve this?
You might try to create a custom field bridge provider, which could get hold of the Spring application context through some static method. When provideFieldBridge() is called you may return a Spring-ified instance of that from the application context, assuming the timing is better and the DAO bean is available by then.
Not sure whether it'd fly, but it may be worth trying.
Hibernate Search 5.8.0 includes support for bean injection. You can see the issue https://hibernate.atlassian.net/browse/HSEARCH-1316.
However I couldn't make it work in my application and I had implemented a workaround.
I have created an application context provider to obtain the Spring application context.
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext context;
public static ApplicationContext getApplicationContext() {
return context;
}
#Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
ApplicationContextProvider.context = context;
}
}
I have added it to the configuration class.
#Configuration
public class RootConfig {
#Bean
public ApplicationContextProvider applicationContextProvider() {
return new ApplicationContextProvider();
}
}
Finally I have used it in a bridge to retrieve the spring beans.
public class AttachmentTikaBridge extends TikaBridge {
#Override
public void set(String name, Object value, Document document, LuceneOptions luceneOptions) {
// get service bean from the application context provider (to be replaced when HS bridges support beans injection)
ApplicationContext applicationContext = ApplicationContextProvider.getApplicationContext();
ExampleService exampleService = applicationContext.getBean(ExampleService .class);
// use exampleService ...
super.set(name, content, document, luceneOptions);
}
}
I think this workaround it's quite simple in comparision with other solutions and it doesn't have any big side effect except the bean injection happens in runtime.
in a project there is several applicationContext.xml file. there isn't any lazy definition for defined beans. then all singleton scoped beans instantiate in runtime.
Oops. it's very bad for development phase. near 2 minutes take time that server startup. Now i will know is there any solution for active lazy-instantiate in spring. For example a configuration in web.xml that set bean default-lazy-init="true".
Implement a custom BeanFactoryPostProcessor that sets lazy to true, e.g.:
public class BeanFactoryPostProcessorImpl implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory(final ConfigurableListableBeanFactory beanFactory) throws BeansException {
for (String beanName : beanFactory.getBeanDefinitionNames()) {
beanFactory.getBeanDefinition(beanName).setLazyInit(true);
}
}
}
To get it working, all you need to do then is to add it to your application context as a standard bean:
An ApplicationContext will detect any beans which are deployed into it
which implement the BeanFactoryPostProcessor interface, and
automatically use them as bean factory post-processors, at the
appropriate time. Nothing else needs to be done other than deploying
these post-processor in a similar fashion to any other bean.
In applicationContext.xml files you can add default-lazy-init attribute with value true on the <beans/> element. See reference.