SpringBoot 自动配置原理以及启动流程

@SpringbootApplication

这个注解标记的类为Springboot程序的主配置类,进入这个注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
}

可以发现有两个注解比较关键@SpringBootConfiguration @EnableAutoConfiguration

根据字面意思为 Springboot配置 和 自动配置

@SpringBootConfiguration

进入这个注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}

发现有@Configuration注解 该注解在Spring中也有接触就是表示一个配置类的意思 javaConfig来代替applicationContext.xml这种配置文件的

再进入@Configuration

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
}

发现有@Conponent注解 这是注解会被Spring自动装载成bean组件

所以可以知道@SpringbootApplication这个注解标记的类是一个Spring的配置类,且会被注册成bean

@EnableAutoConfiguration

使能够自动装配

进入该注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}

有一个@AutoConfigurationPackage 和导入了一个类@Import(AutoConfigurationImportSelector.class)

自动配置包注解 自动配置导入选择器****类

@AutoConfigurationPackage

进入该注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

}

导入了一个AutoConfigurationPackages.Registrar.class

自动配置包类的静态内部类 注册器类 打个的断点debug

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

    //将注解元数据进行注册 getPackageName
        @Override
        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            register(registry, new PackageImport(metadata).getPackageName());
        }

        @Override
        public Set determineImports(AnnotationMetadata metadata) {
            return Collections.singleton(new PackageImport(metadata));
        }

    }
 
 
SpringBoot 自动配置原理以及启动流程_第1张图片
SpringBoot 自动配置原理以及启动流程

可以发现已经能够获取到****包名

说白了就是将主配置类(即@SpringBootApplication标注的类)的所在包及子包里面所有组件扫描加载到Spring容器。所以****包名****一定要注意。

SpringBoot 自动配置原理以及启动流程_第2张图片
SpringBoot 自动配置原理以及启动流程

@Import(AutoConfigurationImportSelector.class)

进入该类

protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
            AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        }
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        List configurations = getCandidateConfigurations(annotationMetadata, attributes);
        configurations = removeDuplicates(configurations);
        Set exclusions = getExclusions(annotationMetadata, attributes);
        checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        configurations = filter(configurations, autoConfigurationMetadata);
        fireAutoConfigurationImportEvents(configurations, exclusions);
        return new AutoConfigurationEntry(configurations, exclusions);
    }

List configurations = getCandidateConfigurations(annotationMetadata, attributes);

可发现配置都通过这个方法获得

protected List getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
                getBeanClassLoader());
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
                + "are using a custom packaging, make sure that file is correct.");
        return configurations;
    }

protected Class getSpringFactoriesLoaderFactoryClass() {
        return EnableAutoConfiguration.class;
    }

    protected ClassLoader getBeanClassLoader() {
        return this.beanClassLoader;
    }

//进入SpringFactoriesLoader.loadFactoryNames()方法
public static List loadFactoryNames(Class factoryType, @Nullable ClassLoader classLoader) {
        String factoryTypeName = factoryType.getName();
        return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    }

//进入  loadSpringFactories(classLoader)
private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap result = cache.get(classLoader);
        if (result != null) {
            return result;
        }

        try {
            Enumeration urls = (classLoader != null ?

                                     //public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
                    classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            result = new LinkedMultiValueMap<>();
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                for (Map.Entry entry : properties.entrySet()) {
                    String factoryTypeName = ((String) entry.getKey()).trim();
                    for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                        result.add(factoryTypeName, factoryImplementationName.trim());
                    }
                }
            }
            cache.put(classLoader, result);
            return result;
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" +
                    FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
    }

//public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

List configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());

通过ClassLoader加载使用了这个@EnableAutoConfiguration注解的类

这个方法会加载jar包中 META-INF/spring.factories 文件中配置的配置对象

SpringBoot 自动配置原理以及启动流程_第3张图片
SpringBoot 自动配置原理以及启动流程

SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作。以前我们需要自己配置的东西,自动配置类都帮我们完成了。

spring.factories文件也是一组一组的key=value的形式,其中一个key是EnableAutoConfiguration类的全类名,而它的value是一个xxxxAutoConfiguration的类名的列表,这些类名以逗号分隔,如下图所示:

SpringBoot 自动配置原理以及启动流程_第4张图片
SpringBoot 自动配置原理以及启动流程

接下来看看都导入了哪些组件被添加到容器中

selectImports方法

@Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
                .loadMetadata(this.beanClassLoader);
        AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
                annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }

//进入 getAutoConfigurationEntry(autoConfigurationMetadata,annotationMetadata) 打个断点看看;

可以看见每个要导入的组件都以全类名的方法返回

SpringBoot 自动配置原理以及启动流程_第5张图片
SpringBoot 自动配置原理以及启动流程

@Conditional xxx

条件注解,通过判断类路径下有没有相应配置的 jar 包来确定是否加载和自动配置这个类。

具体几个@Conditon*注解的含义

@ConditionalOnBean : 仅仅在当前上下文中存在某个对象时,才会实例化一个Bean

@ConditionalOnMissingBean : DI容器中不存在该类型Bean时起效

@ConditionalOnClass : 某个class位于类路径上,才会实例化一个Bean),该注解的参数对应的类必须存在,否则不解析该注解修饰的配置类

@ConditionalOnMissingClass : classpath中不存在该类时起效

@ConditionalOnExpression :当表达式为true的时候,才会实例化一个Bean

@ConditionalOnMissingBean :仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean,该注解表示,如果存在它修饰的类的bean,则不需要再创建这个bean,可以给该注解传入参数例如

@ConditionOnMissingBean(name = "example"),这个表示如果name为“example”的bean存在,这该注解修饰的代码块不执行

@ConditionalOnMissingClass :某个class类路径上不存在的时候,才会实例化一个Bean

@ConditionalOnSingleCandidate : DI容器中该类型Bean只有一个或@Primary的只有一个时起效

@ConditionalOnProperty : 参数设置或者值一致时起效

@ConditionalOnResource : 指定的文件存在时起效

@ConditionalOnJndi : 指定的JNDI存在时起效

@ConditionalOnJava : 指定的Java版本存在时起效

@ConditionalOnWebApplication : Web应用环境下起效

@ConditionalOnNotWebApplication : 非Web应用环境下起效

结论

  1. SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值
  2. 将这些值作为自动配置类导入容器 , 自动配置类就生效 , 帮我们进行自动配置工作;
  3. 整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中;
  4. 它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件 , 并配置好这些组件 ;
  5. 有了自动配置类 , 免去了我们手动编写配置注入功能组件等的工作;

SpringApplication.run分析

分析该方法主要分两部分,一部分是SpringApplication的实例化,二是run方法的执行;

SpringApplication

这个类主要做了以下四件事情:

1、推断应用的类型是普通的项目还是Web项目

2、查找并加载所有可用初始化器 , 设置到initializers属性中

3、找出所有的应用程序监听器,设置到listeners属性中

4、推断并设置main方法的定义类,找到运行的主类

查看构造器:

public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {    // ......   
 this.webApplicationType = WebApplicationType.deduceFromClasspath();    this.setInitializers(this.getSpringFactoriesInstances();    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));    this.mainApplicationClass = this.deduceMainApplicationClass();
}

run 方法流程分析

SpringBoot 自动配置原理以及启动流程_第6张图片
SpringBoot 自动配置原理以及启动流程

来源:https://www.tuicool.com/articles/yABRzyF

你可能感兴趣的:(SpringBoot 自动配置原理以及启动流程)