New bean is injected even when not calling getBean from spring context - java

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

Related

How many instance(s) of #Autowired "prototype" bean is(are) created during the usage of a #Component

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.

Spring why #MockBean can't autowire an interface using profile

I've an interface with two implementations. Which implementaton is to be used depends of the environment (production, development, test, ...). I therefore use Spring profiles. I'm using a configuration file to instantiate the correct implementation.
#Configuration
public class BeanConfiguration {
#Profile({"develop","test-unit"})
#Bean(name = "customerEmailSender")
public CustomerEmailSender emailSenderImpl_1(){
return new EmailSenderImpl_1();
}
#Profile({"prod"})
#Bean(name = "customerEmailSender")
public CustomerEmailSender emailSenderImpl_2(){
return new EmailSenderImpl_2();
}
}
When the Spring container starts (with a specific profile), the correct bean is autowired into the class, and all works fine.
#Component
public class CustomerEmailProcessor {
#Autowire
private CustomerEmailSender customerEmailSender;
...
}
I also have a test class in which I want to autowire the bean. I'm using #Mock for autowiring.
The profile is set to "test-unit" in the test class. So, I'm expecting the spring container to lookup in the config class for the correct bean to be instantiated. But this doesn't happen.
Instead, an Exception is thrown :
Caused by: java.lang.IllegalStateException: Unable to register mock bean .... expected a single matching bean to replace but found [customerEmailSender, emailSenderImpl_1, emailSenderImpl_2]
When using #Autowire annotation, all goes fine. But of course the bean is not mocked anymore and that's what I need to have.
#RunWith(SpringRunner.class)
#ActiveProfiles(profiles = {"test-unit"})
#Import(BeanConfiguration.class)
public class CustomerEmailResourceTest {
#MockBean
private CustomerEmailSender customerEmailSender;
}
I've put a breakpoint in the config class, and I can see that when using #Autowire in the test class, the correct bean is instantiated (breaks at the line "return new EmailSenderImpl_1();".
When using #Mock, no bean at all is instantiated. Spring doesn't break at the line "return new EmailSenderImpl_1();"
Why is it that Spring can find the correct bean when using the #Mock annotation.
The #Mock annotation must be the reason that Spring doesn't use the config class "BeanConfiguration.java". That makes sence after all.

Define bean depending on Spring's bean

When using Spring Boot, a lot of beans are created implicitly by Spring Boot itself. For example, when I link the spring-boot-starter-data-redis dependency, the jedisConnectionFactory bean is created automatically under the hood.
What I'm looking for is the way to define my custom bean with a dependency on such an implicit bean, e.g. new MyService( jedisConnectionFactory ). The problem is that I don't have a variable or a method which would be resolved to that implicit bean.
For now I've come up with the following solution: create a separate BeanConfig class, autowire/inject ApplicationContext into it and then retrieve the required bean with ApplicationContext.getBean( Class<T> ) method call:
#Bean
public Transport eventTransport() {
final JedisConnectionFactory jedisConnectionFactory = context.getBean( JedisConnectionFactory.class );
return new RedisTransport( jedisConnectionFactory.getHostName(), jedisConnectionFactory.getPort() );
}
Is there any integrated way to get a reference to the beans defined internally? So that I could move this bean definition to MyApplication class without injecting the ApplicationContext instance.
First as a rule of thumb if you starting to resort to the ApplicationContext or BeanFactory to obtain beans you are, generally speaking, doing it wrong (at least when simply developing an application with Spring).
When using #Bean on a method, effectively making it a factory method for those beans, you can use 0 or more method arguments. (This is also explained in the reference guide). The arguments are resolved against the context and will have the beans injected (or fail starting if it cannot be found).
So in your case you can simply add JedisConnnectionFactory (or maybe the ConnectionFactory interface) as a method argument for your eventTransport method.
#Bean
public Transport eventTransport(final JedisConnectionFactory jedisConnectionFactory) {
return new RedisTransport( jedisConnectionFactory.getHostName(), jedisConnectionFactory.getPort() );
}
This also allows Spring to resolve dependencies between beans instead of hoping the bean is already constructed and fully ready for use.
If JedisConnectionFactory class is getting instantiated by spring then you can simply autowire this instance at the class level and use the same to create RedisTransport object.
#Autowired
private JedisConnectionFactory jedisConnectionFactory;
#Bean
public Transport eventTransport() {
return new RedisTransport( jedisConnectionFactory.getHostName(), jedisConnectionFactory.getPort() );
}

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.

Categories