[Cultivation of internal skills] [spring-framework] [7] How the annotations in the Spring Framework work

Posted Jun 26, 202022 min read

This article has been included [Cultivation of Internal Power]The Road to Transition

spring-framework.jpg

   .png

WeChat search Lin Xiaoxiao, Lin Xiaoer take you to talk about technology


As of this article, we have introduced Resource , BeanDefinitionReader , BeanFactory , ApplicationContext , AOP , etc., this article focuses Introduce the principle of annotation-based bean assembly in Spring Framework

The use of annotations greatly simplifies the configuration process, and can better express the direct relationship between the code and the configuration, but at the same time, it also loses the ability to manage part of the configuration and is also intrusive to the code.

This does not seem to affect the developer s enthusiasm for the use of annotations. There is no uniform conclusion about the use of annotations or XML for configuration development. The method of development depends on what the specific project is suitable for.

Are annotations better than XML for configuring Spring?

The introduction of annotation-based configuration raised the question of whether this approach is better than XML. The short answer is it depends. The long answer is that each approach has its pros and cons, and, usually, it is up to the developer to decide which strategy suits them better. Due to the way they are defined, annotations provide a lot of context in their declaration, leading to shorter and more concise configuration. However, XML excels at wiring up components without touching their source code or recompiling them. Some developers prefer having the wiring close to the source while others argue that annotated classes are no longer POJOs and, furthermore, that the configuration becomes decentralized and harder to control.

Spring Framework provides many configuration class annotations, but I don t know if you have questions similar to the following during use

  1. @Service, @Repository and other annotations themselves are still modified by @Component, why should this be the case? what's the effect?

     //Service
     @Target({ElementType.TYPE})
     @Retention(RetentionPolicy.RUNTIME)
     @Documented
     @Component //Decorated by @Component
     public @interface Service {
         @AliasFor(annotation = Component.class)
         String value() default "";
     }
    
     //Repository
     @Target({ElementType.TYPE})
     @Retention(RetentionPolicy.RUNTIME)
     @Documented
     @Component //Decorated by @Component
     public @interface Repository {...}
  2. What is the purpose of @AliasFor in @Service? Why does setting the value of @Service have the same effect as setting the value of @Component directly(both bean-name can be specified)?

     @Service("myService")
     public class MyService {...}
    
     @Component("myService")
     pulbic class MyService {...}
  3. Custom annotation @TransactionalService is modified by @Transactional and @Service at the same time, why use @TransactionalService and @Transactional and @ The effect of Service is the same?

     @Target({ElementType.TYPE})
     @Retention(RetentionPolicy.RUNTIME)
     @Documented
     @Transactional
     @Service
     public @interface TransactionalService {...}
    
     @TransactionalService
     public class MyService {...}
    
     @Service
     @Transactional
     pulbic class MyService {...}

Can you see some clues from the above example? It seems that Spring implements the "inheritance" of annotations, and can rewrite the attributes in the "parent annotations"

Before in-depth introduction, it is necessary to first understand the annotation programming model in Spring Spring Annotation Programming Model

1. Annotated programming model in Spring

1.1 Meta Annotations

Annotations used to modify other annotations, such as @Documented, @Component in @Service, etc. Here, the meta-annotation can be understood as the "parent" of the modified annotation

1.2 Stereotype Annotations

Annotations used to declare Spring components, such as @Component, @Service, @Repository, @Controller, etc. If you look closely, you will find that except @Component In addition to, all annotations modified directly/indirectly by @Component are constructive annotations(both can be used to declare Spring components). It is easier to understand if this relationship is regarded as inheritance

1.3 Composed Annotations

It is modified by multiple(meta) annotations at the same time, and the ability to implement multiple annotations at the same time, such as @TransactionalService mentioned in the above example, is equivalent to using @Transactional and @Service at the same time.

1.4 Attribute aliases and rewriting

Spring introduces a new annotation @AliasFor to achieve attribute aliasing(between different attributes within the same annotation) and rewriting of attributes(attributes in meta-annotations)

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Service
@Transactional(propagation = Propagation.NESTED) //Set the default value
public @interface TransactionalService {
    //Rewrite @Service#value
    @AliasFor(annotation = Service.class)
    String value() default "";

    //Rewrite @Transactional#rollbackFor
    @AliasFor(annotation = Transactional.class, attribute = "rollbackFor")
    Class<? extends Throwable>[]transFor() default {};
}

@TransactionalService(value = "myTransactionalService", transFor = RuntimeException.class)
public class MyService {}

//Construct MergedAnnotations
MergedAnnotations mergedAnnotations = MergedAnnotations.from(MyService.class);

//construct
MergedAnnotation<Component> mergedComponent = mergedAnnotations.get(Component.class);
MergedAnnotation<Service> mergedService = mergedAnnotations.get(Service.class);
MergedAnnotation<Transactional> mergedTransactional = mergedAnnotations.get(Transactional.class);

System.out.println("Component#value:"+ mergedComponent.getString("value"));
System.out.println("Service#value:"+ mergedService.getString("value"));
System.out.println("Transactional#rollbackFor:"+ Arrays.toString(mergedTransactional.getClassArray("rollbackFor")));
System.out.println("Transactional#propagation:"+ mergedTransactional.getEnum("propagation", Propagation.class));

Output

Component#value:myTransactionalService
Service#value:myTransactionalService
Transactional#rollbackFor:[class java.lang.RuntimeException]
Transactional#propagation:NESTED

In the annotation system of Spring, you will find a lot of usage methods as above. Spring uses MergedAnnotation(s) to integrate the above-mentioned meta-annotations, combined annotations, aliases and rewrites of attributes and other information to achieve a set of similar Annotation class inheritance mechanism to improve annotation reuse and simplify development

ClassAnnotation

2. Bean assembly based on annotations

2.1 How to enable

How to make Spring recognize annotations and register/assemble?

2.1.1 Based on ClassPathXmlApplicationContext

When using Spring with xml configuration, there will be two tags <context:annotation-config> and <context:component-scan> to enable annotation recognition

The former is used to register some processors to process annotations on registered beans in the container, such as configuration classes @Configuration, @Import, @Autowired, etc.

The latter is used to scan the classes in the specified package that are decorated with stereotype annotations(@Component and all annotations modified directly/indirectly by @Component) and register them in the container, among others Outside <context:component-scan> will also complete the function of <context:annotation-config>

The two configured parsers correspond to AnnotationConfigBeanDefinitionParser and ComponentScanBeanDefinitionParser, which are registered in ContextNamespaceHandler respectively
The logic of xml custom tags is introduced in Spring Framework 2 BeanDefinitionReader

  • AnnotationConfigBeanDefinitionParser will call AnnotationConfigUtils.registerAnnotationConfigProcessors to complete the registration of various processors
  • ComponentScanBeanDefinitionParser will create a ClassPathBeanDefinitionScanner and call its doScan method to complete the discovery and registration of the structural annotation modification class, and will also call AnnotationConfigUtils.registerAnnotationConfigProcessors to complete various processors Registration

2.1.2 Based on AnnotationConfigApplicationContext

AnnotationConfigApplicationContext creates two registrars AnnotatedBeanDefinitionReader and ClassPathBeanDefinitionScanner

The former is used to specify BeanClass(es) for registration, the latter is used to complete the discovery and registration of stereotype annotation decoration classes, and both will call AnnotationConfigUtils.registerAnnotationConfigProcessors to complete the registration of various processors

2.2 Annotation-based bean discovery & registration

So far, you can see that annotation-based bean discovery, registration, and assembly are concentrated in three places

  • AnnotatedBeanDefinitionReader#register
  • ClassPathBeanDefinitionScanner#scan
  • AnnotationConfigUtils#registerAnnotationConfigProcessors

2.2.1 AnnotatedBeanDefinitionReader

AnnotatedBeanDefinitionReader can specify one or more classes to register, the specific registration logic is in AnnotatedBeanDefinitionReader#registerBean, the general process is

  1. Encapsulate the target class as AnnotatedGenericBeanDefinition
  2. Determine whether to ignore according to @Condition annotation
  3. Set the properties of BeanDefinition according to other annotations on the target class
  4. Register BeanDefinition to the container

registerBean

Each framed part of the flowchart is a relatively independent logic block, which will appear frequently in the following(the details of each logic block will not be expanded below), and several logic blocks need to be discussed here

2.2.1.1 @Conditional

Spring can use the Condition implementation class specified in @Conditional to determine whether it is necessary to ignore the bean registration of the current class, so you need to specify one or more Condition* in@Conditional** * Implementation class, as long as there is a Condition that does not meet the conditions, it will ignore the registration of this bean

@FunctionalInterface
public interface Condition {
    /**
     * Determine if the condition matches.
     */
    boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}

Condition interface definition is very simple, you can get BeanDefinitionRegistry, ConfigurableListableBeanFactory, Environment, ResourceLoader, ClassLoader and other information from ConditionContext, AnnotatedTypeMetadata contains the annotation information on the target class, and the Condition logic can be easily written through the above information

ProfileCondition is a typical example, which obtains the @Profile annotation on the target class to determine whether the value specified by @Profile includes the profile specified by the current system(* -Dspring.active.profiles*), to decide whether to ignore the registration of beans, so that the registration of special Beans can be isolated as an environment, and special Beans(such as applied to Different service implementations for online and offline environment registration)

Generally speaking, the Condition implementation class will be used in conjunction with a corresponding custom annotation

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ProfileCondition.class) //Meta annotation is @Conditional, and specifies Condition as ProfileCondition
public @interface Profile {
    String[]value();
}

class ProfileCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //Obtain the @Profile annotation on the target class to determine whether the value specified by @Profile contains the profile specified by the current system
    }
}

@Conditional is used as a meta annotation in @Profile, and Condition is specified as ProfileCondition, and ProfileCondition is passed through the parameters in @Profile To determine whether the environment matches, @Profile and ProfileCondition complement each other

Similarly, you can find many similar Annotation + Condition combinations in SpringBoot, such as @ConditionalOnClass, @ConditionalOnProperty, etc.

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {
    Class<?>[]value() default {};
    String[]name() default {};
}

@Order(Ordered.HIGHEST_PRECEDENCE)
class OnClassCondition extends FilteringSpringBootCondition {
    //Obtain the @ConditionalOnClass annotation on the target class to determine whether the class specified by @ConditionalOnClass is in the classpath
}

Others @ConditionalXxx are no longer listed and can be viewed in the org.springframework.boot.autoconfigure.condition package

Q:How to create Annotation + Condition that fits your business meaning

2.2.1.2 @Scope

In Spring Framwork 3 Bean is how to create article introduced the concept of Scope, Spring can be set by @Scope Bean Scope, Bean s The life cycle is determined by the corresponding Scope implementation class

In addition, there is an important parameter in @Scope**proxyMode, why does the Bean modified by @Scope need a proxy?

Taking SessionScope as an example(WebApplicationContext.SCOPE_SESSION), the Bean is generated when the Session is created and destroyed when the Session is destroyed

@Service
@Scope(scopeName = WebApplicationContext.SCOPE_SESSION)
public class SessionScopeService {
    public String hello() {
        return "Hello";
    }
}

What happens if the SessionScopeService needs to be injected into the Controller at this time?

@Controller
public class ScopesController {
    @Autowired
    SessionScopeService sessionScopeService;
}

Caused by:org.springframework.beans.factory.BeanCreationException:Error creating bean with name'sessionScopeService':Scope'session' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton;

When the SessionScopeService needs to be injected during the Spring initialization process, the Session is not generated(no request comes in), and a Bean of the SessionScopeService type cannot be created. To solve this problem, a scope proxyMode can be set for the SessionScopeService, and a proxy class(generated by the ScopedProxyFactoryBean) is generated in advance. The proxy class will delay the creation of the target bean(delay the call of getBean)

What happens if the method is called immediately after injection?

@RestController
public class TestController {
    private SessionScopeService sessionScopeService;

    @Autowired
    public void setSessionScopeService(SessionScopeService sessionScopeService) {
        this.sessionScopeService = sessionScopeService;
        sessionScopeService.hello();
    }
}

Caused by:org.springframework.beans.factory.BeanCreationException:Error creating bean with name'scopedTarget.sessionScopeService':Scope'session' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton;

Setting @Scope's proxyMode can solve the problem of early injection during the Spring initialization process, but use it still within the lifecycle of Scope

Examples of using @Scope can be found in Quick Guide to Spring Bean Scopes

2.2.2 ClassPathBeanDefinitionScanner

AnnotatedBeanDefinitionReader can register one or more specified classes in the container(the target class is not required to be modified by stereo annotations), and ClassPathBeanDefinitionScanner can scan all modified by stereo annotations in the specified packages class and register in the container

Stereotype annotations:@Component, @ManagedBean, @Named or annotations modified by the above annotations(in the form of meta-annotations)

scanBean

The logic of ClassPathBeanDefinitionScanner is relatively simple(as shown above), and no more expansion will be done. The details of the highlighted parts in the above picture are introduced in detail in the section AnnotatedBeanDefinitionReader

At this point, AnnotatedBeanDefinitionReader and ClassPathBeanDefinitionScanner have only completed the registration of the bean. For the analysis of the configuration class(@Configuration), the processing of annotations such as @Bean, @Import, @PropertySource, etc. has not been completed in the above logic

With the above questions, let's see what AnnotationConfigUtils did

2.2.3 AnnotationConfigUtils

AnnotationConfigUtils#registerAnnotationConfigProcessors does not have very special logic, just register a few beans

  • ConfigurationClassPostProcessor
  • AutowiredAnnotationBeanPostProcessor
  • CommonAnnotationBeanPostProcessor
  • PersistenceAnnotationBeanPostProcessor(if there is javax.persistence.EntityManagerFactory in the classpath)
  • EventListenerMethodProcessor
  • DefaultEventListenerFactory

Do you still have an impression about AutowiredAnnotationBeanPostProcessor and CommonAnnotationBeanPostProcessor? In How Spring Framwork 3 Bean was created , there is an introduction to @Autowoired, @Value, @Inject and @Resource, @WebServiceRef, @EJB, etc. Logic, but in this article only the specific analysis process is introduced, and the registration logic of the above two processes is not introduced

EventListenerMethodProcessor and DefaultEventListenerFactory, with the help of SmartInitializingSingleton( Spring Framework 4 ApplicationContext provides developers with which(default) extensions Introduced) After all Singleton Beans are initialized, look for the method of @EventListener annotation modification and encapsulate it as ApplicationListener to applicationListeners in ApplicationContext(ApplicationListener s See What(default) extensions are provided by Spring Framework 4 ApplicationContext for developers )

Next, focus on ConfigurationClassPostProcessor(processing configuration class @Configuration, @Bean, @Import, etc.)

2.3 Processing of configuration classes

It is learned from the above that the processing of the configuration class is in ConfigurationClassPostProcessor, which implements BeanDefinitionRegistryPostProcessor(when the trigger timing of BeanDefinitionRegistryPostProcessor is which(default) extensions are provided to developers by [Spring Framework 4 ApplicationContext] has an introduction), need to pay attention to two methods ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry and ConfigurationClassPostProcessor#postProcessBeanFactory, which are executed after the BeanDefinition registration is completed and before the bean is instantiated

2.3.1 ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry

ConfigurationClassPostProcessor#processConfigBeanDefinitions

The flow chart of this step is as follows, it seems cumbersome, but there are only three main points

  • Three cases will define registered BeanDefinition as configuration class

    • Decorated by @Configuration

    • Randomly modified by @Components, @ComponentScan, @Import, @ImportResource

    • There is a method modified by @Bean

      But only modified by @Configuration and set proxyBeanMethods to true will be marked as CONFIGURATION_CLASS_FULL, the rest will be marked as CONFIGURATION_CLASS_LITE, the role of these two tags will be explained below

  • In the configuration class, the following annotations will be parsed in order

    • @PropertySource(s)
    • @ComponentScan(s)
    • @Import
    • @ImportResource
    • @Bean
  • At the same time, the parent class will be recursive for the above analysis

  • After the above annotations are parsed, BeanDefinition will be registered in order according to the contents of the annotations

processConfigBeanDefinitions

The explanation of CONFIGURATION_CLASS_FULL and CONFIGURATION_CLASS_LITE will be described below

Therefore, the analysis of the configuration class and the Bean registration are mainly divided into two parts

  • The blue part in the figure only analyzes the content of the annotation
  • Like the bell red part, register BeanDefinition for the content parsed by the annotation

Next, expand the various annotation analysis and registration logic related to the configuration class in order.

(Below, when Spring searches for annotations, it will search in all parent and child classes)

2.3.1.1 @PropertySource

Spring will get all the value in @PropertySource on the target class, and get all the corresponding Resources, encapsulate it as a PropertySource and add it to the PropertySources of Environment one by one

Parse_PropertySource

There is no detailed expansion of the logic in ConfigurationClassParser#addPropertySource, if you are interested, you can check the Spring source code, and you will have a deeper understanding of the structure of propertySources in Environment

2.3.1.2@ComponentScan

Spring's analysis of @ComponentScan essentially uses ClassPathBeanDefinitionScanner. For the introduction of ClassPathBeanDefinitionScanner, see above

Parse_ComponentScan

@ComponentScan The parameters in the annotation can be customized for ClassPathBeanDefinitionScanner, more commonly used are

  • userDefaultFilters can decide whether to configure the default TypeFilter, for the default TypeFilter registration see above(ClassPathScanningCandidateComponentProvider#registerDefaultFilters)
  • includeFilters and excludeFilters can add your own include and exclude TypeFilter
  • Scanning packages can be specified by basePackages and basePackageClasses. For the latter, Spring will convert to the packages where the specified classes are located
  • If no scan packages are specified, Spring will use the package where the target class is located

2.3.1.3 @Import

Parse_Import

@Import annotation accepts three types of classes, ImportSelector, ImportBeanDefinitionRegistrar, General Configuration Class(ConfigurationClass)

  • ImportSelector

    ImportSelector is to return the classes that need to be imported importClasses(ImportSelector#selectImports), and then recursively call processImports method to parse importClasses in turn, so ImportSelector#selectImports can return ImportSelector, ImportBeanDefinitionRegistrar, ConfigurationClass any of the three types

    • DeferredImportSelector

      A subclass of ImportSelector will be added to deferredImportSelectors of ConfigurationClassParser#deferredImportSelectorHandler for this type, and will be delayed until all annotations in the configuration class are parsed and executed before the bean configuration of the annotation configuration(ConfigurationClassParser# deferredImportSelectorHandler.process())

  • ImportBeanDefinitionRegistrar

    The registerBeanDefinitions method of ImportBeanDefinitionRegistrar can be used to dynamically register beans, but the registration logic is put into ConfigurationClassParser#importBeanDefinitionRegistrars, which is delayed until all annotations in the configuration class are resolved(ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsFromRegistrars)

  • ConfigurationClass

    If it is not the above two cases, it will be regarded as a configuration class, and the configuration class will be recursively called ConfigurationClassParser#processConfigurationClass. In addition, the ConfigurationClass imported through @Import will be registered as Bean, regardless of whether the target class is Modified by stereo annotations

Therefore, ImportSelector and ConfigurationClass will be directly parsed recursively, and DeferredImportSelector and ImportBeanDefinitionRegistrar will be temporarily stored for delayed execution

DeferredImportSelector processing

The processing logic of DeferredImportSelector is similar to ImportSelector, but it will delay execution

DeferredImportSelectorHandler.process

ImportBeanDefinitionRegistrar processing

There is no special logic here, and the registerBeanDefinitions method of ImportBeanDefinitionRegistrar will be called in turn.

ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromRegistrars

2.3.1.4 @ImportResource

For @ImportResource, Spring just does a simple analysis and temporarily stores the locations specified in the annotation

The locations in @ImportResource point to the path of the configuration file to be loaded, such as the xml configuration file, groovy configuration file, or even a custom special configuration file format(the reader in @ImportResource needs to be configured at the same time, which is used to specify the BeanDefinitionReader to parse the configuration file)

Parse_ImportResource

The specific configuration file analysis loading logic is in ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsFromImportedResources

ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromImportedResources

The logic is simple, no more detailed

2.3.1.5 @Bean

@Bean is mainly used in the method of the configuration class, the method must return a Bean entity for registration, method parameters can be automatically injected, you can use @Qualifier and other annotations

So, is the method of registering Bean with @Bean method very similar to the method of registering Bean with factory method(see Spring Framwork 3 Bean is created )? In fact

Parse_Bean

@Bean's parsing logic is so simple that there is no need to say more, @Bean's registration logic is in ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForBeanMethod

ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForBeanMethod

The above logic is also not complicated. It mainly uses factory-method in BeanDefinition. For static methods, bean-class will be set, and for non-static methods, factory-bean will be set. For specific factory-method, see related introduction How Spring Framwork 3 Bean was created

2.3.2 ConfigurationClassPostProcessor#postProcessBeanFactory

At this point, the analysis of all configuration classes and the registration of related BeanDefinitions have been completed

As mentioned above, the configuration classes are divided into two modes:CONFIGURATION_CLASS_FULL and CONFIGURATION_CLASS_LITE. Only when they are modified by @Configuration and proxyBeanMethods is set to true will it be marked as CONFIGURATION_CLASS_FULL, What are the similarities and differences between different models?

There are two logics in ConfigurationClassPostProcessor#postProcessBeanFactory to enhance the Configuration class; register ImportAwareBeanPostProcessor

ImportAwareBeanPostProcessor is used to implement the setImportMetadata method implemented by the ImportAware interface, which can be used on Beans similar to those imported through @Import to pass in the annotation information on the original Configuration class

The following focuses on the enhanced logic of the Configuration class

enhanceConfigurationClasses

For the configuration class in the CONFIGURATION_CLASS_FULL mode, the ConfigurationClassEnhancer will be used to enhance the configuration class to generate the proxy configuration class and set it to the original BeanDefinition, which means that the enhanced proxy class is used in the initialization phase of the bean

For the configuration class of the CONFIGURATION_CLASS_LITE mode, no enhanced proxy is used, and the original target configuration class is used in the bean initialization phase

Why do you need to enhance the proxy for the configuration class in the CONFIGURATION_CLASS_FULL mode(@Configuration decoration and set proxyBeanMethods to true)? Spring official documentation gives a short explanation [Full @Configuration vs lite @Bean mode]( https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans- java-basic-concepts)

When @Bean methods are declared within classes that are not annotated with @Configuration, they are referred to as being processed in a lite mode. Bean methods declared in a @Component or even in a plain old class are considered to be lite , with a different primary purpose of the containing class and a @Bean method being a sort of bonus there. For example, service components may expose management views to the container through an additional @Bean method on each applicable component class. In such scenarios, @Bean methods are a general-purpose factory method mechanism.

Unlike full @Configuration, lite @Bean methods cannot declare inter-bean dependencies. Instead, they operate on their containing component's internal state and, optionally, on arguments that they may declare. Such a @Bean method should therefore not invoke other @Bean methods. Each such method is literally only a factory method for a particular bean reference, without any special runtime semantics. The positive side-effect here is that no CGLIB subclassing has to be applied at runtime, so there are no limitations in terms of class design(that is, the containing class may be final and so forth).

In common scenarios, @Bean methods are to be declared within @Configuration classes, ensuring that full mode is always used and that cross-method references therefore get redirected to the container's lifecycle management. This prevents the same @Bean method from accidentally being invoked through a regular Java call, which helps to reduce subtle bugs that can be hard to track down when operating in lite mode.

In simple terms, in the configuration class of the CONFIGURATION_CLASS_FULL mode, multiple calls to the same @Bean method will only be executed once, and the beans returned by each call are the same

public class MyComponent {
    private static int initCount = 0;
    public MyComponent() {
        //Count creation times
        MyComponent.initCount++;
    }

    public static int initCount() {
        return MyComponent.initCount
    }
}

public class MyServiceA {
    private MyComponent myComponent;
    public MyServiceA(MyComponent myComponent) {
        this.myComponent = myComponent;
        System.out.println("In MyServiceA, MyCompoent Init Count "+ myComponent.initCount +" times.")
    }
}

public class MyServiceB {
    private MyComponent myComponent;
    public MyServiceB(MyComponent myComponent) {
        this.myComponent = myComponent;
        System.out.println("In MyServiceB, MyCompoent Init Count "+ myComponent.initCount +" times.")
    }
}

@Configuration
public class MyConfiguration {
    @Bean
    public MyComponent myComponent() {
        return new MyComponent();
    }

    @Bean
    @DependsOn("myComponent")
    public MyServiceA myServiceA() {
        //call the internal method, the myComponent method will only be called once
        MyComponent myComp = myComponent();
        //Output In MyServiceA, MyCompoent Init Count 1 times.
        return new MyServiceA(myComp);
    }

    @Bean
    @DependsOn("myServiceA")
    public MyServiceB myServiceB() {
        //call the internal method, the myComponent method will only be called once
        MyComponent myComp = myComponent();
        //Output In MyServiceB, MyCompoent Init Count 1 times.
        return new MyServiceB(myComp);
    }
}

However, if the above configuration logic is put into CONFIGURATION_CLASS_LITE mode, multiple calls to the same @Bean method will actually be executed multiple times, and the beans returned by each call are different new objects

@Configuration(proxyBeanMethods = false)
public class MyConfiguration {
    @Bean
    public MyComponent myComponent() {
        return new MyComponent();
    }

    @Bean
    @DependsOn("myComponent")
    public MyServiceA myServiceA() {
        //Call the internal method, the myComponent method will be executed every time
        MyComponent myComp = myComponent();
        //Output In MyServiceA, MyCompoent Init Count 2 times.
        return new MyServiceA(myComp);
    }

    @Bean
    @DependsOn("myServiceA")
    public MyServiceB myServiceB() {
        //Call the internal method, the myComponent method will be executed every time
        MyComponent myComp = myComponent();
        //Output In MyServiceB, MyCompoent Init Count 3 times.
        return new MyServiceB(myComp);
    }
}

or

@Component
public class MyConfiguration {
    @Bean
    public MyComponent myComponent() {
        return new MyComponent();
    }

    @Bean 
    public MyServiceA myServiceA() {
        //      myComponent        
        MyComponent myComp = myComponent();
        //  In MyServiceA, MyCompoent Init Count 2 times.
        return new MyServiceA(myComp);
    }

    @Bean 
    @DependsOn("myServiceA")
    public MyServiceB myServiceB() {
        //      myComponent        
        MyComponent myComp = myComponent();
        //  In MyServiceB, MyCompoent Init Count 3 times.
        return new MyServiceB(myComp);
    }
}

           @Bean         Spring     

@Component
public class MyConfiguration {
    @Bean
    public MyComponent myComponent() {
        return new MyComponent();
    }

    @Bean 
    public MyServiceA myServiceA(MyComponent myComp) {
        //  In MyServiceA, MyCompoent Init Count 1 times.
        return new MyServiceA(myComp);
    }

    @Bean 
    @DependsOn("myServiceA")
    public MyServiceB myServiceB(MyComponent myComp) {
        //  In MyServiceB, MyCompoent Init Count 1 times.
        return new MyServiceB(myComp);
    }
}
     Bean                    

@Import(ImportSelector) -> @Import(DeferredImportSelector) -> @Import(ConfigClass) -> @Bean -> @ImportResource -> @Import(ImportBeanDefinitionRegistrar)

3.

  • Spring

  • Bean AnnotationConfigApplicationContext ClassPathXmlApplicationContext <context:annotation-config> <context:component-scan>

  • ClassPathBeanDefinitionScanner package Bean @Component @ManagedBean @Named @Component @ManagedBean @Named @Repository @Service @Configuration @Controller

  • @Conditional Bean Annotation + Condition org.springframework.boot.autoconfigure.condition

  • Bean

    • @Configuration
    • @Components @ComponentScan @Import @ImportResource
    • @Bean
  • @PropertySource @ComponentScan @Import @ImportResource @Bean

  • @Import class ImportSelector ImportBeanDefinitionRegistrar _ ConfigurationClass _