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?
Related
I have a Managerclass annotated with #Component
and #Scope
#Component
#Scope(value = "prototype")
public class Manager {
...
}
So I expect a new instance of the Manager bean will be created each time the bean is requested.
Then I have an Adapter class which uses this Manager bean. To use it, I have two ways of Autowire: 1. on the property or 2. on the constructor:
#Component
public class Adapter {
#Autowired
Manager m_Manager;
...
}
Or
#Component
public class Adapter {
Manager m_manager;
#Autowired
public Adapter(Manager manager) {
m_manager = manager;
}
...
}
Since the Adaptor class is a singleton bean, so both #Autowire the Manageron the property or on the constructor will only create one instance of the Manager? Meaning Managerbean is actually used as a singleton bean instead of prototype bean, right?
#Autowire behaves in the same way as ApplicationContext.getBean
It creates a prototype bean for each autowired instance. you can see that the prototype object in two singletons has a different identifier
So each singleton has its own prototype instance. It doesn't have any difference if you do it with #Autowire in the constructor or field.
Do Autowire on constructor is just more convenient way to avoid annotation duplication.
P.S. To define scope is better to use
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
If you don't have any other injection points, where you inject a Manager instance, then you will only have one instance in the application context, that's correct.
I have a bean declared in by config file as:
#Bean
#Lazy
#Scope("prototype")
public SomeClass someClass()
{
return new SomeClass();
}
Then inside my test class I have the following code:
#Autowired
private SomeClass someClass;
#Before
public void setUp()
{
xyzObject.someMethod(someClass);
}
My question here is inside my setUp() method I am just referencing the autowired SomeClass bean but not getting it from the spring context using getBean(), but still it is creating a new bean everytime the setUp() is running before a test. Why is this happening? Does prototype scope means a new bean gets created whenever the bean's reference is used anywhere in the code? I haven't seen anything specified like that in the documentation.
That is the essence of Inversion of Control. Because you injected it into another bean, every time you instantiate another bean your injected bean would be created and provided by spring as part of instantiation of your containing bean. You don't need to call getBean() here. However if in some place you just need an instance of your SomeClass bean you may call getBean() to tell your spring environment to give you that bean to you. However, It is better to avoid such practice and rely on injection. Or create a Factory that can provide you such Bean (Still relaying on spring though) That would make your code not aware of Spring which is IMHO is more ellegant
If each bean has name, and we have getBean() method, which receives bean name and in XML config we are also injecting beans by name, then why in Java config we are limited to #Autowired annotation which wires by class?
What is conventional way to inject beans into one configuration from another one? Is it possible to refer bean by name and not use #Qualifier?
UPDATE
I found a way to autowire by name between configurations.
First I autowire entire configuration by class:
#Autowired
MySeparateConfig mySeparateConfig;
Then I just call instantiation method from that bean:
#Bean
MyDependentBean myDependentBean() {
MyDependentBean ans = new MyDependentBean();
ans.setProperty( mySeparateConfig.myNamedBeanInDifferentConfig() );
return ans;
}
Configs are of different classes by definition.
Actually, there are several ways to inject bean by annotation.
given that we have this bean
<bean id="standardPasswordEncoder" class="org.springframework.security.crypto.password.StandardPasswordEncoder" />
and in java class we can use following ways to inject it as far as I know
#Autowired // by type
StandardPasswordEncoder standardPasswordEncoder;
#Autowired
#Qualifier("standardPasswordEncoder") // by bean id
StandardPasswordEncoder standardPasswordEncoder;
javax.annotation.#Resource // by bean id
StandardPasswordEncoder standardPasswordEncoder;
javax.inject.#Inject // by type
StandardPasswordEncoder standardPasswordEncoder;
or use spEL
#Value(#{standardPasswordEncoder}) // by bean id
StandardPasswordEncoder standardPasswordEncoder;
However, I don't know the reason why spring autowired default is by type, either, and also wondering why. I think it's dangerous to autowire by type. Hope this would help you.
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.
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.