I've read that dynamic bean definition change. I try it in a simple code example (see code below), and I find it very attractive in situations where I don't want to stop server but add/change bean definition.
Questions:
I've read that it is possible to achieve bean definition change in runtime with help of StaticApplicationContex
or BeanPostProcessor
or BeanFactoryPostProcessor
? So what is the difference?
public class Main {
final static String header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<beans xmlns=\"http://www.springframework.org/schema/beans\"\n" +
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
" xmlns:context=\"http://www.springframework.org/schema/context\"\n" +
" xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd\">\n" +
" <context:annotation-config />\n" +
" <context:component-scan base-package=\"vbah\"/>";
final static String contextA =
"<bean id=\"test\" class=\"java.lang.String\">\n" +
"\t\t<constructor-arg value=\"fromContextA\"/>\n" +
"</bean></beans>";
final static String contextB =
"<bean id=\"test\" class=\"java.lang.String\">\n" +
"\t\t<constructor-arg value=\"fromContextB\"/>\n" +
"</bean></beans>";
public static void main(String[] args) throws IOException {
//create a single context file
final File contextFile = new File("src/resources/spring-config.xml");
//write the first context into it
FileUtils.writeStringToFile(contextFile, header + contextA);
//create a spring context
FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext(
new String[]{contextFile.getPath()}
);
//echo "fromContextA"
System.out.println(context.getBean("test"));
//write the second context into it
FileUtils.writeStringToFile(contextFile, header + contextB);
//refresh the context
context.refresh();
//echo "fromContextB"
System.out.println(context.getBean("test"));
}
}
EDIT:
Can you answer the questions below:
BeanPostProcess
allow you to modify already existed bean instances at runtime by wrapping the object with proxy. Am I right?AbstractApplicationContext#refresh() drop all singleton beans and recreate them.
null
dependency. Am I right?StaticApplicationContext
and BeanFactoryPostProcessor
both allow me to change a bean definition in runtime. But what are the difference, pros/cons?
AbstractApplicationContext#refresh()
, StaticApplicationContext
and BeanFactoryPostProcessor
please.To change properties in a file during runtime, we should place that file somewhere outside the jar. Then, we'll tell Spring where it is with the command-line parameter –spring.config.location=file:// {path to file}. Or, we can put it in application.properties. In file-based properties, we'll have to choose a way to reload the file.
Furthermore, it provides more enterprise-specific functionalities. The important features of ApplicationContext are resolving messages, supporting internationalization, publishing events, and application-layer specific contexts. This is why we use it as the default Spring container.
For dynamic context paths there really is no out-of-box configuration since the context path is static. Now, why would you want multiple or dynamic context paths?
Reloading Properties from External File To change properties in a file during runtime, we should place that file somewhere outside the jar. Then, we'll tell Spring where it is with the command-line parameter –spring.config.location=file:// {path to file}.
Is it safe do to so (see code below)?
You'll have to define safe.
The AbstractApplicationContext#refresh()
method javadoc states
As this is a startup method, it should destroy already created singletons if it fails, to avoid dangling resources. In other words, after invocation of that method, either all or no singletons at all should be instantiated.
Basically every bean in your context will be destroyed and all references to them will be dropped, making them candidates for garbage collection. You need to make sure that those beans have appropriate ways to release any resources they might have. There are different ways to do that
DisposableBean
interface. destroy-method
attribute to your <bean>
or @Bean
definition.@PreDestroy
.Note that refresh()
will typically eagerly refresh your ApplicationContext
, ie. re-instantiate all the beans immediately. You may notice some slow down in your application while that happens.
I've read that it is possible to achieve bean definition change in runtime with help of
StaticApplicationContext
orBeanPostProcessor
orBeanFactoryPostProcessor
? So what is the difference?
StaticApplicationContext
is one of the ApplicationContext
classes where you register the bean definitions yourself. In your example, the bean definitions are parsed from your XML file and registered behind the scenes. With StaticApplicationContext
, you use registerBeanDefinition(..)
or the other registerXxx()
methods to explicitly register a bean definition.
A BeanFactoryPostProcessor
has access to the BeanFactory
being used and therefore all the bean definitions that have been registered. As such, you can retrieve any BeanDefinition
you want and modify it. As the javadoc for BeanFactoryPostProcess#postProcessBeanFactory(..)
states
All bean definitions will have been loaded, but no beans will have been instantiated yet. This allows for overriding or adding properties even to eager-initializing beans.
You can change the bean definition before the ApplicationContext
actually uses it.
Finally, a BeanPostProcessor
doesn't change the bean definition. You can use a BeanPostProcessor
to change how a bean is created but the underlying BeanDefinition
will stay the same.
For your edit (which is bigger than the actual answer :) )
As I understand BeanPostProcess allow you to modify already existed bean instances at runtime by wrapping the object with proxy. Am I right?
It's not just for proxying, you can do anything you want with the object: modify its properties, register it in some other context, make it null
, etc. This goes around the bean definition.
AbstractApplicationContext#refresh()
drop all singleton beans and recreate them.But If I want to change the definition of prototype/custom scoped bean? If I've got two beans: A and B. A has reference to B. If I change the bean definition in such way that it doesn't contain definition of B. Than B instances will be destroyed, but new instances won't be created. Than A will get a null dependency. Am I right?
In an ApplicationContext
, you declare your bean definitions. If you're going to change a bean definition, change it in a BeanFactoryPostProcessor
or declare it differently in the context configuration.
For dependencies, if you destroy the B
bean definition, there won't be a bean to inject into A
and Spring will complain, throwing NoSuchBeanDefinitionException
. Bean injection never injects null
unless you explicitly tell it to.
StaticApplicationContext
andBeanFactoryPostProcessor
both allow me to change a bean definition in runtime. But what are the difference, pros/cons?
The two serve completely different purposes. StaticApplicationContext
is an ApplicationContext
implementation. Here, you declare bean definitions. A BeanFactoryPostProcessor
serves to modify those bean definitions in any way, based on whatever condition you care to implement.
Why Spring has 3 mechanism to achieve the same goal. Can you make a brief comparison (or usecases examples) between
AbstractApplicationContext#refresh()
,StaticApplicationContext
andBeanFactoryPostProcessor
please.
The goal is not the same. An ApplicationContext
is different than a BeanFactoryPostProcessor
and comes into play at a different time in the context life cycle (see that nice graph you had in a previous question).
I don't have use cases for you. Learn what each of the above can do and you'll know when to apply them when you get specific requirements.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With