Reputation: 1717
I have some strange behavior in my Spring App, here is my Java Spring Boot application structure:
in package com.somethingsomething.packageA
, I have 2 files
First is ParentA.java
package com.somethingsomething.packageA;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class ParentA {
@Autowired
private ChildA childA;
public ChildA getChildA() {
return childA;
}
@PostConstruct
public void ParentAPostConstruct() {
System.out.println("ParentA PostConstruct were called");
}
}
Second is ChildA.java
package com.somethingsomething.packageA;
import org.springframework.stereotype.Component;
@Component
public class ChildA {
public ChildA() {
System.out.println("ChildA were called");
}
}
and then under package com.somethingsomething.packageB
, I also have two similar files.
First is ParentB.java
package com.somethingsomething.packageB;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class ParentB {
@Autowired
private ChildB childB;
public ChildB getChildB() {
return childB;
}
@PostConstruct
public void ParentBPostConstruct() {
System.out.println("ParentB PostConstruct were called");
}
}
Second is ChildB.java
package com.somethingsomething.packageB;
import org.springframework.stereotype.Component;
@Component
public class ChildB {
public ChildB() {
System.out.println("ChildB were called");
}
}
Both of packageA
and packageB
have similar structure. Then under com.somethingsomething
I have two main function for both package:
ForPackageA.java
package com.somethingsomething;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import com.somethingsomething.packageA.ParentA;
@SpringBootApplication
public class ForPackageA {
public static void main(String[] args) {
ApplicationContext applicationContext =
SpringApplication.run(ForPackageA.class, args);
ParentA parentA =
applicationContext.getBean(ParentA.class);
System.out.println(parentA);
System.out.println(parentA.getChildA());
}
}
and ForPackageB.java
package com.somethingsomething;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import com.somethingsomething.packageB.ParentB;
@SpringBootApplication
public class ForPackageB {
public static void main(String[] args) {
ApplicationContext applicationContext =
SpringApplication.run(ForPackageB.class, args);
ParentB parentB =
applicationContext.getBean(ParentB.class);
System.out.println(parentB);
System.out.println(parentB.getChildB());
}
}
When i run ForPackageA.java
, this will appear in log:
ChildA were called
ParentA PostConstruct were called
ChildB were called
ParentB PostConstruct were called
com.somethingsomething.packageA.ParentA@88d6f9b
com.somethingsomething.packageA.ChildA@47d93e0d
The ChildB were called
and ParentB PostConstruct were called
were not suppose to be there, since ForPackageA.java
doesn't depend on those beans. Why is this happening?
The same thing also happening when i run ForPackageB.java
, which will log following:
ChildA were called
ParentA PostConstruct were called
ChildB were called
ParentB PostConstruct were called
com.somethingsomething.packageB.ParentB@610db97e
com.somethingsomething.packageB.ChildB@6f0628de
But in this case ChildA were called
and ParentA PostConstruct were called
were not suppose to be logged.
So, why is this peculiar behavior is happening? Is this default behavior of Spring?
Edit:
If let say i add following line
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
toParentB.java
(after@Component
)It will not make
ParentB PostConstruct were called
appear when i runForPackageA.java
.and also, if i add those line to
ChildB.java
it will also not makeChildB were called
appear when i runForPackageA.java
Why are the beans act differently in prototype mode, i.e. they are not getting called like when they are in singleton mode?
Upvotes: 1
Views: 708
Reputation: 494
Yes, this is default behaviour of Spring. At application start all Beans with a @Component
annotation get created, no matter if you use them or not.
The call applicationContext.getBean(ParentB.class)
then simply returns the already created Bean.
To answer your edit:
Spring Beans are by default Singletons, so theres always only one instance of the bean per applicationContext
. This is Inversion of Control, meaning that Spring handles object instantiation, not you.
Beans with the Prototype scope can have multiple object instances and can, in a way, be instantiated by you. (By calling applicationContext.getBean(ParentA.class)
). This is similar to doing something like ParentA a = new ParentA()
.
I suggest you read this to get a deeper understanding of scopes.
Upvotes: 2
Reputation: 7267
Why is this happening?
When you start your Spring app the ApplicationContext
is initialised by component scanning your application and registering all Spring annotated beans in the context. This allows them to be injected as required.
Is this default behaviour of Spring?
Yes. You can change this behaviour by configuring the component scanning to only look at specified packages if you wish (although the use cases for this are few and far between).
Upvotes: 1
Reputation: 522
The bean object creation doesn't depends on which object you are trying to get. Whenever spring application gets launched, its all components object are created automatically and you are getting already created component object using applicationContext.getBean(componentClass)
method. Hope this will help you to understand why you are getting logs for every object.
Upvotes: 1