Spring dynamic bean definition autowire - java

So first short introduction:
I have a working application context, now I want to create a new bean factory that extends it with some dynamic bean definitions.
So i create a new instance of DefaultListableBeanFactory passing base application context as parent.
Then I create a new bean definition:
BeanDefinition beanDef = BeanDefinitionBuilder.rootBeanDefinition(beanType)
.setScope(BeanDefinition.SCOPE_PROTOTYPE)
.setLazyInit(false)
.setAbstract(false)
.setDependencyCheck(AbstractBeanDefinition.DEPENDENCY_CHECK_ALL)
.getBeanDefinition();
and at the end I register it with newly created bean factory
beanFactory.registerBeanDefinition(beanName, beanDef);
then some time later i would like to get new instance of that bean so I do:
Object beanInstance = beanFactory.getBean(jobType);
now i would expect that fields annotated with #Autowired are initialized.. but no. Calling beanFactory.autowireBean(beanInstance) does not help.
After looking up some other bean definitions in base application context i can see that my definitoin does not have any attributes and that I can add them by calling beanDef.setAttribute() but that requires me to know them in advance.
Now question. Is there a way to create fully initialized bean definition programmatically so it is autowired correctly?

So so i found out what i was missing:
AutowiredAnnotationBeanPostProcessor
it needs to be added to bean factory to fire up the #Autowired and #Value annotations.
also for #PostConstruct and #PreDestroy you need CommonAnnotationBeanPostProcessor
Bean factory created for application context by spring boot has total of 12 bean post processors so it is possible that some other are needed to get all features.

Related

Why does #Value as parameter of constructor fill the property correctly?

My question is: Why does this piece of code correctly set the constructor parameter property port:
private final RedisServer redisServer;
public RedisTestConfiguration(#Value("${cache.port}") final int port) {
this.redisServer = new RedisServer(port);
}
As of my understanding, #Value("${cache.port}") is resolved by a BeanPostProcessor called AutowiredAnnotationBeanPostProcessor. Spring bean lifecycle works in a way that the constructor method is called before any BeanPostProcess, see picture below. Note The constructor is called before BeanPostProcessor.postProcessBeforeInitialization().
How come this is still working?
Regards,
Bart
This behavior is supported by PropertySourcesPlaceholderConfigurer
Ref: https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-factory-placeholderconfigurer
PropertySourcesPlaceholderConfigurer is a BeanFactoryPostProcessor. It collects #Value fields and updates bean definition in spring container. PropertySourcesPlaceholderConfigurer should be created in the container before any bean is initialized.
Bean metadata is described here in the documentation:
https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-factory-metadata
So, the flow is the following:
1. Bean definition readers collect beans declarations in xml files or from java classes. Example, XmlBeanDefinitionReader.
Ref: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.html
Bean Factory Post Processors update bean definitions. Example, PropertySourcesPlaceholderConfigurer.
Spring container looks at the bean definition and creates beans(calls constructor) according to bean definition values.
Ref: https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-factory-class
So, the problem is that
As of my understanding, #Value("${cache.port}") is resolved by a BeanPostProcessor is not correct. #Value is managed by BeanFactoryPostProcessor, not BeanPostProcessor
Actually, documentation states:
A default lenient embedded value resolver is provided by Spring. It will try to resolve the property value and if it cannot be resolved, the property name (for example ${catalog.name}) will be injected as the value. If you want to maintain strict control over nonexistent values, you should declare a PropertySourcesPlaceholderConfigurer bean, as the following example shows:...
Spring has some kind of default property resolver, that is another BeanFactoryPostProcessor. But it is possible to override it with PropertySourcesPlaceholderConfigurer

Can I instantiate bean programmatically in Spring?

Suppose I have an instance of Class<?> and I am already in Spring. Can I instantiate a bean for Spring in this situation?
I want to simulate situation as if I had the following bean definition:
#Bean
MyClass myBean() {
return new MyClass();
}
i.e. only call no-arg constructor and autowire dependencies.
Is this possible?
Probably this is somehow related with programmatic creation of bean definitions.
This is modification of the following question: Can I inject properties to third-party beans?
UPDATE
I used
bean = getApplicationContext().getAutowireCapableBeanFactory().createBean(klass);
and for now it is apparently working.
Is this correct?

If one bean is managed by Spring does this mean all created beans also have to be managed

If I have a Spring bean that contains another non Spring bean like so :
#Component
class SpringBean {
private MyBean mb = new MyBean();
}
I receive error : unable to wire bean MyBean
But if I declare the Bean within a method :
#Component
class SpringBean {
private void myMethod(){
MyBean mb = new MyBean();
}
}
Then the bean is initialized correctly when the bean is called.
So does Spring not allow non Spring beans to be created by Spring beans ? What is the reasoning behind this?
Only beans created by the Spring container will be managed by Spring and provide dependency injection and all the benefits (and problems) that Spring gives you.
So does Spring not allow non Spring beans to be created by Spring beans ?
Spring beans are POJOs after all, so they can be manually created like any class with a public constructor. This means, you can do:
public void foo() {
SpringBean springBeanNotManagedBySpring = new SpringBean();
//use springBeanNotManagedBySpring...
}
But, as stated at the beginning of my answer, springBeanNotManagedBySpring is not managed by Spring, so it won't have the benefits of a Spring managed bean.
Note that you're doing two different things in your example:
In the first code, you have a field in the class of type MyBean and Spring tries to inject it but looks like doesn't find a Spring bean to inject. You should post the configuration of your bean in order for us to do a better analysis on this.
In the second code, you're creating a local variable (completely different from a field in class) of type MyBean, where the instantiation is up to the programmer.

spring singleton scope-- per container per bean

I am asking this Question in reference to my question:
spring singleton scope
Spring singleton is defined in reference manual as per container per bean.
per container means if we do like:
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml")
MyBean myobj=(MyBean)context.getBean("myBean"); //myBean is of singleton scope.
MyBean myobj1=(MyBean)context.getBean("myBean");
Beans.xml:
<bean id="myBean" class="MyBean"/>
Then myobj==myobj1 will come out to true.Means both pointing to same instance.
For per bean part of phrase per container per bean i was somewhat confused. Am i right in following for per bean :
If we do like
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml")
MyBean myobj=(MyBean)context.getBean("myBean");
MyBean myobj1=(MyBean)context.getBean("mySecondBean");
Beans.xml:
<bean id="myBean" class="MyBean"/>
<bean id="mySecondBean" class="MyBean"/>
Then myobj==myobj1 will come out to false. Means then they are two different instances?
That is correct.
If it helps, you can also think of Spring beans as Instances that you would've otherwise created manually in your Java code using the constructor.
By defining the bean in the Spring XML file, that bean (Instance) gets registered with Spring's App Context and then that instance can be passed around to the other areas of the code.
By creating a new bean, you are effectively creating a new instance. So potentially you could create any number of beans (Instances) of the same class
myBean is a Spring singleton in the sense of every call to beans.getBean("myBean") will allways return the same instance. And mySecondBeanhaving a different id is another Spring singleton. You can have different singleton beans of same class in the same ApplicationContext .
Yes, you're right. Testing it would have told you.

when is a spring bean instantiated

ApplicationContext ctx = new ClassPathXmlApplicationContext(
"com/springinaction/springidol/spring-idol.xml");
Performer performer = (Performer) ctx.getBean("duke");
performer.perform();
In the above, when are the beans instantiated, when the ApplicationContext is created or when the getBean() is called?
Assuming the bean is a singleton, and isn't configured for lazy initialisation, then it's created when the context is started up. getBean() just fishes it out.
Lazy-init beans will only be initialised when first referenced, but this is not the default. Scoped beans (e.g. prototype-scoped) will also only be created when first referenced.
According to Spring documentation,
The default behavior for ApplicationContext implementations is to eagerly pre-instantiate all singleton beans at startup.
Also, you can set them to load lazily.
For reference, see
Lazy-initialized beans and
Bean scopes
Here's a brief description of when beans are created:
A singleton bean (which is the default scope) that does not have the lazy-init property set to true (default is false) is constructed when the application context is created
A singleton bean that does have the lazy-init property set to true is constructed when it is first requested
A bean set in any other scope is created when it is first requested (for that scope).
By default, all beans are singletons, so whenever Application context gets created, they are all pre-loaded. If, specifically, any singleton bean has an attribute lazy-init="true" set, it will be lazy-loaded, i.e. it will be instantiated when the getBean method is called for the first time.
For other scopes, beans will be instantiated whenever they are requested.
It depends what is the scope of the bean you are calling with getBean() method.
If it is 'Singleton', it is pre-instantiated by the ApplicationContext.
If you are using BeanFactory as an IOC Container, then it uses lazy initialization and the beans will be instantiated only when you call the getBean() method.
This is an advantage of ApplicationContext over BeanFactory that it solves Circular Dependency problem.
By default, Spring ApplicationContext eagerly creates and initializes all ‘singleton scoped‘ beans during application startup itself. ApplicationContext makes the bean available in BeanFactory. getBean() returns the instance of the bean.
By default it's created when the context is started up but the order depends on dependencies.
If we have the following classes :
#Component
public class A{
}
#Component
public class B{
#Autowired
A a;
}
Class A will be created before class B because class B depends on class A.
Basically, when you run ApplicationContext, it automatically creates all the Annotated beans and makes ready them to use for you.
If you don't need some of them, you can annotate them with #Lazy and they will not instantiated when you run the application

Categories