xcode
xcode

Reputation: 1717

Unused Bean Get Called in Spring App

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) to ParentB.java (after @Component)

It will not make ParentB PostConstruct were called appear when i run ForPackageA.java.

and also, if i add those line to ChildB.java it will also not make ChildB were called appear when i run ForPackageA.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

Answers (3)

Niby
Niby

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

StuPointerException
StuPointerException

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

Vikas Suryawanshi
Vikas Suryawanshi

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

Related Questions