Differences between @ Bean annotations and @ Configuration @ Component annotations in combination

  • 2021-12-09 08:47:59
  • OfStack

Directory 1. @ Bean's "full" mode and "lite" mode 2. Differences between the two modes 1. @ Bean methods call each other in "full" mode 2. @ Bean methods call each other in "lite" mode 3. Summary

1. @ Bean's "full" mode and "lite" mode

In one common case, the @ Bean annotation is declared in the @ Configuration class, which is called the "full" pattern; When @ Bean annotation and @ Component annotation are used in combination, they are called "lite" mode.

When I first read the document, I didn't understand it. After reading it several times and running the test code, I roughly understood the difference.

2. Differences between the two models

If only the @ Bean annotation is used on the method, and there is no call between the methods of the @ Bean annotation, the above two modes achieve basically the same effect, and the object returned by the @ Bean annotation method can be registered in the container as bean.

If each @ Bean annotated method calls each other, the two modes are quite different-unlike @ Configuration in full mode, @ Bean method calls in lite mode cannot declare dependencies between Bean.

This is also explained in the @ Bean annotation source code annotation.

1. @ Bean methods call each other in "full" mode


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;  
@Configuration
public class Config {
 
    @Bean()
    public C c(){
        return new C();
    }
 
    @Bean
    public B b(){
        return new B(c());
    } 
}

Class B:


public class B { 
    public C c; 
    public B(C a){this.c=a;} 
    public void shutdown(){
        System.out.println("b close...");
    }
}

Class C:


public class C { 
    private String name; 
    @PostConstruct
    public void init(){
        this.name = "I am a bean";
    }
 
    @Override
    public String toString(){
        return "c say:" + name;
    }
}

In the code shown above, we have two classes, B and C. In the @ Configuration class, two @ Bean methods are used to define bean b and bean c, respectively, but it should be noted that the @ Bean method where new B (c ()) is located calls another @ Bean method.

Test the main program class:


import com.dxc.opentalk.springtest.config.B;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy; 
 
@EnableAspectJAutoProxy
@ComponentScan("com.dxc.opentalk.springtest")
public class BootStrap { 
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext
                = new AnnotationConfigApplicationContext(BootStrap.class);
        B b = (B)applicationContext.getBean("b");
        System.out.println(b.c);
        System.out.println(applicationContext.getBean("c"));
        System.out.println(applicationContext.getBean("c").equals(b.c));
    }
}

Program output results:

c say:I am a bean
c say:I am a bean
true

It can be seen that bean c is injected into bean b, and c, a member of bean b, is indeed bean in Spring container. (In fact, the debug program tracks the singleton pool in Spring more clearly, which is explained by the output results here.)

2. @ Bean methods call each other in "lite" mode

Again, we just replace the annotation on the Config class with @ Component, leaving the rest of the code unchanged:


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;  
@Component
public class Config {
 
    @Bean()
    public C c(){
        return new C();
    }
 
    @Bean
    public B b(){
        return new B(c());
    }
}

Look at the output result of the main program class under 1 test:

c say:null
c say:I am a bean
false

It can be seen that c in bean b is just an ordinary c object, not bean in Spring container (c in b does not execute the initialization callback method of bean and is not equal to c bean in singleton pool). Therefore, in this mode, the @ Bean method calls to each other cannot complete the injection of interdependency between bean.

3. Summary

To sum up, we should pay attention to the difference between the two modes when using @ Bean annotation. In general, @ Bean is used with @ Configuration annotation.

However, classes annotated by @ Configuration are subclassed by Spring using CGLIB, so classes annotated by @ Configuration cannot be decorated with final, which is not restricted by @ Component annotations.

If interdependency injection between bean is required by using the @ Bean annotation and @ Component annotation combination, the official recommendation is to use the construction method or method level dependency injection, as follows:


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
 
@Component
public final class Config {
 
    @Bean()
    public C c(){
        return new C();
    } 
    @Bean
    public B b(C c){// Construct method injection 
        return new B(c);
    } 
}

Related articles: