Interpretation of Springboot's automatic assembly

Table of contents

 

1. Automatic assembly of Springboot

1.1 Assembly of components

1.1.1 Components

1.2 Module Assembly of Spring Framework

1.2.1 @Import annotation

1.2.2 BeanDefinition 

1.3 Conditional assembly of Spring Framework

1.3.1 @Profile

1.3.2 @Conditional 

1.3.3 MetaData metadata interface (supplement)

AnnotatedTypeMetadata

AnnotationMetadata

MethodMetadata

 1.4 SPI mechanism

1.5 Assembly mechanism of Springboot


 

1. Automatic assembly of Springboot

1.1 Assembly of components

The Spring Framework itself has an IOC container, which will uniformly manage the Bean objects in it. Bean objects can be understood as components. To understand component assembly, you must first understand the concept of components.

1.1.1 Components

When an application based on the Spring Framework integrates third-party technologies, the core API in the third-party framework must be configured in the configuration file or annotation configuration class of the Spring Framework for unified management by the Spring Framework. The configuration here is the key. By writing XML configuration files or annotation configuration classes, the core API in the third-party framework is registered in the IOC container in the form of objects. These core API objects will play their role in the appropriate position to support the normal operation of the project. The core API object in the IOC container is itself a bean object, that is, a component; the behavior of configuring the core API to an XML configuration file or an annotation configuration class is called component assembly.

The Spring Framework itself has only one component assembly method, that is, manual assembly, while Spring Boot is based on native manual assembly. Through module assembly + conditional assembly + SPI mechanism, the automatic assembly of components can be perfectly realized. Manual assembly means that developers register the required components in the IOC container, that is, the ApplicationContext, by writing XML configuration files, annotating configuration classes, and matching specific methods in the project. The core feature in Springboot is the automatic assembly of components. The core of automatic assembly is that the configuration that should have been written by the developer is transformed into a framework that automatically makes a reasonable judgment and assembles the appropriate Bean to the IOC according to the scene dependencies integrated in the project. in the container.

1.2 Module Assembly of Spring Framework

1.2.1 @Import annotation

You can refer to  Springboot core annotations and basic configuration interpretation_Shanhe Yiwenan's Blog-CSDN Blog

1.2.2 BeanDefinition 

BeanDefinition is the configuration meta-information interface that defines Bean. Here is an example to create Bean object through BeanDefinition. code show as below:

   DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class).addPropertyValue("username", "admin");
        defaultListableBeanFactory.registerBeanDefinition("user01",beanDefinitionBuilder.getBeanDefinition());
        User user01 = defaultListableBeanFactory.getBean("user01", User.class);
        System.out.println(user01);

        RootBeanDefinition beanDefinition = new RootBeanDefinition(User.class);
        //构造参数
        ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();
        constructorArgumentValues.addIndexedArgumentValue(0, "root");
        beanDefinition.setConstructorArgumentValues(constructorArgumentValues);
        defaultListableBeanFactory.registerBeanDefinition("user02", beanDefinition);
        User user2 = defaultListableBeanFactory.getBean("user02", User.class);
        System.out.println(user2);

1.3 Conditional assembly of Spring Framework

1.3.1 @Profile

The @Profile annotation can be annotated on components, and it will only take effect when a configuration property is activated. Profile provides an environment-based configuration. According to the different running environments of the current project, components that match the current running environment can be dynamically registered.

Use @Profile:

@Profile("test")
public class User {
    private String username;
}

Set Profile for ApplicationContext

 AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
        annotationConfigApplicationContext.getEnvironment().setActiveProfiles("test");
        annotationConfigApplicationContext.register(User.class);
        annotationConfigApplicationContext.refresh();
        User bean = annotationConfigApplicationContext.getBean(User.class);
        System.out.println(bean);

AnnotationConfigApplicationContext When creating an object, if the configuration class is directly passed in, the IOC container will be initialized immediately. If the configuration class is not passed in, the initialization logic will not be executed internally, but will wait until its refresh method is called manually. The IOC container will be initialized, and the environment configuration will be processed together during the initialization process.

1.3.2 @Conditional 

 You can refer to  Springboot core annotations and basic configuration interpretation_Shanhe Yiwenan's Blog-CSDN Blog

 The conditional judgment class for judging whether it exists:

public class MyConditional implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return context.getBeanFactory().containsBeanDefinition(Admin.class.getName());
    }
}

In the above matches method, BeanDefinition is used instead of Bean to make judgments. This is because the Admin object may not have been created when the conditions match, resulting in deviations in condition matching.

1.3.3 MetaData metadata interface (supplement)

Metadata is data about data, metadata is the description and context of data, it helps to organize, find, understand and use data.

e6deee4c832e90c56172734d8957588e.png

AnnotatedTypeMetadata

This interface represents the metadata of the annotation element (AnnotatedElement), as long as the element that can be annotated on it belongs to the annotation element.

public interface AnnotatedTypeMetadata {

    // 此元素是否标注有此注解,annotationName:注解全类名
    boolean isAnnotated(String annotationName);

    //取得指定类型注解的所有的属性 - 值(k-v)
    // annotationName:注解全类名
    // classValuesAsString:若是true表示 Class用它的字符串的全类名来表示。这样可以避免Class被提前加载
    @Nullable
    Map<String, Object> getAnnotationAttributes(String annotationName);
    @Nullable
    Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString);

}

AnnotationMetadata

It is a sub-interface of ClassMetadataand AnnotatedTypeMetadata, which has the common capabilities of both, and has added related methods for accessing annotations. It can be simply understood as an abstraction of annotations.

public interface AnnotationMetadata extends ClassMetadata, AnnotatedTypeMetadata {
 
	//拿到当前类上所有的注解的全类名(注意是全类名)
	Set<String> getAnnotationTypes();
	// 拿到指定的注解类型
	//annotationName:注解类型的全类名
	Set<String> getMetaAnnotationTypes(String annotationName);
	
	// 是否包含指定注解 (annotationName:全类名)
	boolean hasAnnotation(String annotationName);
	//这个厉害了,用于判断注解类型自己是否被某个元注解类型所标注
	//依赖于AnnotatedElementUtils#hasMetaAnnotationTypes
	boolean hasMetaAnnotation(String metaAnnotationName);
	
	// 类里面只有有一个方法标注有指定注解,就返回true
	//getDeclaredMethods获得所有方法, AnnotatedElementUtils.isAnnotated是否标注有指定注解
	boolean hasAnnotatedMethods(String annotationName);
	// 返回所有的标注有指定注解的方法元信息。注意返回的是MethodMetadata 原理基本同上
	Set<MethodMetadata> getAnnotatedMethods(String annotationName);
}

MethodMetadata

public interface MethodMetadata extends AnnotatedTypeMetadata {
    //方法名
	String getMethodName();
    //定义方法的类全限定名
	String getDeclaringClassName();
    //返回的值全限定名
	String getReturnTypeName();
}

MetadataReader  

public interface MetadataReader {

    /**
     * 返回class文件的IO资源引用
     */
    Resource getResource();

    /**
     * 为基础class读取基本类元数据,返回基础类的元数据。
     */
    ClassMetadata getClassMetadata();

    /**
     *为基础类读取完整的注释元数据,包括注释方法的元数据。返回基础类的完整注释元数据
     */
    AnnotationMetadata getAnnotationMetadata();
}

 MetadataReaderFactory

MetadataReaderFactory interface, the factory interface of MetadataReader. Allows caching of metadata sets per MetadataReader.

public interface MetadataReaderFactory {
    /**
     * 根据class名称创建MetadataReader
     */
    MetadataReader getMetadataReader(String className) throws IOException;

    /**
     * 根据class的Resource创建MetadataReader
     */
    MetadataReader getMetadataReader(Resource resource) throws IOException;

}

 

 1.4 SPI mechanism

The SPI in Spring is more advanced and practical than the native SPI of JDK, because it is not limited to interfaces or abstract classes, but can be any class, interface and annotation. The SPI mechanism is widely used in Springboot to load auto-configuration classes and special components.

Declare the SPI file

Put the SPI file in the META-INF directory of the project, and the file name must be spring.factories. code show as below:

com.example.demo.entity.User=\
com.example.demo.entity.User

The fully qualified name of the retrieved class, interface, and annotation in spring.factories is used as the key, and the fully qualified name of the class to be retrieved is used as the value, and multiple classes are separated by commas.

test acquisition

  List<User> users = SpringFactoriesLoader.loadFactories(User.class, Demo2Application.class.getClassLoader());
        for (User user : users) {
            System.out.println(user);
        }

1.5 Assembly mechanism of Springboot

Automatic assembly in Springboot is actually a combination of module assembly + conditional assembly + SPI mechanism

The composition of @SpringbootApplication can refer to   Springboot core annotations and basic configuration interpretation_Shanhe Yiwenan's Blog-CSDN Blog

Here we mainly explain @EnableAutoConfiguration, click on @EnableAutoConfiguration annotation

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

The @AutoConfigurationPackage annotation itself combines an @Import annotation

@Import({AutoConfigurationPackages.Registrar.class})

What the @AutoConfiguration annotation does is record the package where the main startup class is located and register it in AutoConfigurationPackages. After Springboot 2.3.0, there are two more attributes in the annotation, and you can manually specify the root package path of the application, as follows:

 String[] basePackages() default {};

 Class<?>[] basePackageClasses() default {};


After briefly introducing the @AutoConfigurationPackage annotation itself, the goal is shifted to the imported internal class
AutoConfigurationPackages.Registrar.class. Registrar itself is an ImportBeanDefinitionRegistrar, its function is to programmatically register bean objects in the IOC container, and the object to be registered by Registrar is actually the package path where the default main startup class is located (that is, the root package to be recorded by the @AutoConfiqurationPackage annotation) . Registrar code is as follows:

 static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
        Registrar() {
        }
        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            AutoConfigurationPackages.register(registry, (String[])(new PackageImports(metadata)).getPackageNames().toArray(new String[0]));
        }
    }

 Debug is as follows:

2b608075dc28441d86d43c7fab752216.png
 
Note the second parameter of the register method, which uses PackageImports to export a set of package names. The source of the package name is an AnnotationMetadata, which can be understood as the AutoConfigurationPackage annotation itself in essence. In other words, the PackageImports class extracts the two properties ( baserackages and basePackageClasses ) defined in the @AutoConfigurationPackage annotation. In fact, the same is true of the source code. It has a design that the convention is greater than the configuration. The construction method of PackageImports will first extract the two attributes in the annotation. If the two attributes are not defined, the package name where the main startup class is located will be extracted.

@Import({AutoConfigurationImportSelector.class}):

Import the AutoConfigurationImportSelector class into the spring container. AutoConfigurationImportSelector can help the springboot application load all eligible @Configuration configurations into the IOC container (ApplicationContext) created and used by the current SpringBoot. There are only three steps in the main logic: loading the automatic configuration class, removing the removed automatic configuration class, and encapsulating the Entry return. The action of loading the automatic configuration class here is to use the SPI mechanism of the Spring Framework. Extract all configuration values ​​corresponding to @EnableAutoConfiguration from spring.factories. The removed automatic configuration class can be removed in three ways: the exclude and excludeName attributes of @springBootApplication or @EnableAutoConfiguration annotations, and the spring.autoconfigure.exclude attribute configuration of the global configuration file. The underlying source code will extract the automatic configuration classes configured in these three locations and remove them. code show as below:

 protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            //加载注解属性配置
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            //加载自动配置类
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
           //获取显式配置了要移除的自动配置类
            configurations = this.removeDuplicates(configurations);
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions); 
            //移除要删除的自动配置类
            configurations.removeAll(exclusions);
            configurations = this.getConfigurationClassFilter().filter(configurations);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationEntry(configurations, exclusions);
        }
    }

The steps to implement automatic configuration at the bottom of springboot are:
1. @SpringBootApplication works;
2. @EnableAutoConfiguration;
3. @AutoConfigurationPackage: This combined annotation is mainly @Import(AutoConfigurationPackages.Registrar.class), which imports the Registrar class into the container , and the role of the Registrar class is to scan the same-level directory and subpackages of the main configuration class, and import the corresponding components into the container created and managed by springboot; 4. @Import(AutoConfigurationImportSelector.class): It imports the AutoConfigurationImportSelector
class into the container , the function of the AutoConfigurationImportSelector class is to pass the configuration class information to the SpringFactory loader through the selectImports method for a series of container creation processes.

 

 

Guess you like

Origin blog.csdn.net/qq_43649937/article/details/131399962