Spring Boot 源码分析(一)

Spring Boot 源码分析(一)

sschrodinger

2019/05/28


Spring boot 简介


Spring boot 采用约定大于配置的思想对应用程序进行默认配置,减少了大量的配置时间。

Spring Boot 包含的特性如下:

  • 创建可以独立运行的 Spring 应用
  • 直接嵌入 Tomcat 或 Jetty 服务器,不需要部署 WAR 文件
  • 提供推荐的基础 POM 文件来简化 Apache Maven 配置
  • 尽可能的根据项目依赖来自动配置 Spring 框架
  • 提供可以直接在生产环境中使用的功能,如性能指标、应用信息和应用健康检查
  • 没有代码生成,也没有 XML 配置文件

通过 Spring Boot,创建新的 Spring 应用变得非常容易,而且创建出的 Spring 应用符合通用的最佳实践。只需要简单的几个步骤就可以创建出一个 Web 应用。对于 spring boot 的创建,参见 spring 官方启动器


Spring boot 加载


因为 Spring boot 内置了 Tomcat 或 Jetty 服务器,不需要直接部署 War 文件,所以 spring boot 的程序起点为一个普通的主函数,主函数类如下:

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

看似和普通的程序没有什么区别,其实他最主要的步骤都通过注解 @SpringBootApplication 和方法 SpringApplication.run() 完成了。

其实所有的依赖在这一步就可以完成注入,主要的步骤是 spring 读取所有依赖中 META-INF/spring.factories 文件,该文件指明了哪些依赖可以被自动加载,然后根据 ImportSelector 类选择加载哪些依赖,加载到 IoC 容器中

META-INF/spring.factories 文件的读取

主函数中,只有一个方法,即 SpringApplication.run(),如下:

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

run 方法只是一个静态方法,调用的是 run(new Class<?>[] { primarySource }, args) 方法。如下:

public static ConfigurableApplicationContext run(Class<?>[] primarySources,
            String[] args) {
    return new SpringApplication(primarySources).run(args);
}

其中,读取配置文件的操作就发生在 SpringApplication 类的实例化过程中。实例化的代码如下:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    setInitializers((Collection) getSpringFactoriesInstances(
            ApplicationContextInitializer.class));
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
}

getSpringFactoriesInstances(ApplicationContextInitializer.class)) 中 spring 读取了spring.factories 文件。

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    MultiValueMap<String, String> result = cache.get(classLoader);
    if (result != null) {
        return result;
    } try {
        Enumeration<URL> urls = (classLoader != null ?
            // 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 factoryClassName = ((String) entry.getKey()).trim();
                    for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                    result.add(factoryClassName, factoryName.trim());
                }
            }
        }
        cache.put(classLoader, result);
        return result;
    }
    catch (IOException ex) {
        throw new IllegalArgumentException("Unable to load factories from location [" +
                FACTORIES_RESOURCE_LOCATION + "]", ex);
    }
    
    // output
    // like:
    // org.springframework.boot.autoconfigure.EnableAutoConfiguration -> {LinkedList@1446} size = 118
    //   |
    //   |- org.springframework.boot.autoconfigure.EnableAutoConfiguration
        
}

该方法会将所有的 META-INF/spring.factories 缓存到 SpringFactoriesLoader 类中,在需要读取的时候进行读取。

总结

  • SpringFactoriesLoader 类的静态方法实现了依赖文件的读取,读取所有的配置类的全限定名名称

@SpringBootApplication 注解

@SpringBootApplication 是一个复合注解,如下:

@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 {
    // ...
}

注解一代表配置注解,如下:

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

有了这个注解,我们就可以把由 @SpringBootApplication 注解修饰的类作为配置类来使用,即在类 DemoApplication 中由 @Bean 注解修饰的代码都会被注入到 IoC 容器中由 Spring 统一管理。

注解三会自动扫描该类所在包,将各种组件注入到 IoC 容器中让其管理,所以我们也可以类 DemoApplication 上增加诸如 @Controller 的注解,将其作为一个 Controller。

最重要的是 @EnableAutoConfiguration 注解。

在 Spring 中,由 Enable 开头的注解的理念和做事方式其实一脉相承,简单概括一下就是,借助@Import的支持,收集和注册特定场景相关的bean定义

@EnableAutoConfiguration 注解

@EnableAutoConfiguration 的定义如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage  // 注解一
@Import(AutoConfigurationImportSelector.class) // 注解二
public @interface EnableAutoConfiguration {

    // ...

}

@Import

@Import 注解可以导入三种类:

  1. @Configuration 注解修饰的类
  2. ImportSelectorImportBeanDefinitionRegistrar 的实现类
  3. 普通的组件类,即由 @Componet 注解修饰的类。

ImportSelector 接口定义如下:

public interface ImportSelector {
    // 返回哪些类需要被创建
    String[] selectImports(AnnotationMetadata importingClassMetadata);

}

该接口的主要作用是根据给定的选择条件(通常是一个或多个注解属性)导入哪个 @Configuration 类。

note

  • 如果该接口的子类实现了如下四个接口,会先执行如下四个接口的函数:
  • EnvironmentAware
  • BeanFactoryAware
  • BeanClassLoaderAware
  • ResourceLoaderAware
  • 如果希望在所有的 @Configuration 类都导入后再导入该类,则使用其子接口 DeferredImportSelector

ImportSelector 类在 BeanFactory.refresh() 方法中调用,具体到应该是在 BeanFactory.refresh() 函数的 invokeBeanFactoryPostProcessors 中,具体参见 [Spring 源码分析(二)Core],invokeBeanFactoryPostProcessors 的主要作用是注册所有的 PostProcessors,即 bean 的后处理器,需要在 sigleton 实例化之前调用。

主要方法如下:

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

    // ...
}

下面是 PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors() 的方法代码,部分代码如下:

public static void invokeBeanFactoryPostProcessors(
            ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {

    // Invoke BeanDefinitionRegistryPostProcessors first, if any.
    Set<String> processedBeans = new HashSet<>();

    if (beanFactory instanceof BeanDefinitionRegistry) {
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
        List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
        List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();

        // 记录是否是定义类的 Processor 或者普通的 Processor

        // Do not initialize FactoryBeans here: We need to leave all regular beans
        // uninitialized to let the bean factory post-processors apply to them!
        // Separate between BeanDefinitionRegistryPostProcessors that implement
        // PriorityOrdered, Ordered, and the rest.
        List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

        // ...
        // 应用 Bean 定义类的后置处理器
        invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
        // ...

}

private static void invokeBeanDefinitionRegistryPostProcessors(
            Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {

    for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
        postProcessor.postProcessBeanDefinitionRegistry(registry);
    }
}

invokeBeanDefinitionRegistryPostProcessors 函数对每一个定义类的后置处理器分别进行应用, @Configure 的解析就在这个函数中。如下:

// 从注册表中的配置类派生更多的bean定义
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    // ...
    this.registriesPostProcessed.add(registryId);
    // Build and validate a configuration model based on the registry of Configuration classes.
    processConfigBeanDefinitions(registry);
}

进入最关键的类 ConfigurationClassPostProcessor,这个类用户来注册所有的 @Configure@Bean。他的 processConfigBeanDefinitions 函数如下:

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
    String[] candidateNames = registry.getBeanDefinitionNames();

    // 记录所有候选的未加载的配置

    // Return immediately if no @Configuration classes were found
    if (configCandidates.isEmpty()) {
        return;
    }

    // 按照 Ordered 对配置进行排序

    // 加载自定义 bean 名命策略

    if (this.environment == null) {
        this.environment = new StandardEnvironment();
    }

        // Parse each @Configuration class
    ConfigurationClassParser parser = new ConfigurationClassParser(
            this.metadataReaderFactory, this.problemReporter, this.environment,
            this.resourceLoader, this.componentScanBeanNameGenerator, registry);

    Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
    Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
    do {
        // 解译候选集
        parser.parse(candidates);
        parser.validate();

        Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
        configClasses.removeAll(alreadyParsed);

        // Read the model and create bean definitions based on its content
        this.reader.loadBeanDefinitions(configClasses);
        alreadyParsed.addAll(configClasses);

        // ...
    } while (!candidates.isEmpty());

    // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
    if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
        sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
    }

    if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
        // Clear cache in externally provided MetadataReaderFactory; this is a no-op
        // for a shared cache since it'll be cleared by the ApplicationContext.
        ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
    }
}

注意 process 中的 this.deferredImportSelectorHandler.process() 方法:

public void parse(Set<BeanDefinitionHolder> configCandidates) {
    for (BeanDefinitionHolder holder : configCandidates) {
        BeanDefinition bd = holder.getBeanDefinition();
        try {
            if (bd instanceof AnnotatedBeanDefinition) {
                parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
            }
            else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
                parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
            }
            else {
                parse(bd.getBeanClassName(), holder.getBeanName());
            }
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(
                    "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
        }
    }

    this.deferredImportSelectorHandler.process();
}

这个方法就是处理实现了 deferredImportSelector 接口的方法。该方法会读取所有满足要求的类名并将其加入到 IoC 框架中。

该方法会读取静态类变量 SpringFactoriesLoader.cache 获得所有需要加载的类的全限定名。方法栈如下:

selectImports:10, RoleImportSelector (com.example.demo.config)
/^\
process:889, ConfigurationClassParser$DefaultDeferredImportSelectorGroup (org.springframework.context.annotation)
/^\
getImports:875, ConfigurationClassParser$DeferredImportSelectorGrouping (org.springframework.context.annotation)
/^\
processGroupImports:801, ConfigurationClassParser$DeferredImportSelectorGroupingHandler (org.springframework.context.annotation)
/^\
process:771, ConfigurationClassParser$DeferredImportSelectorHandler (org.springframework.context.annotation)
/^\
parse:185, ConfigurationClassParser (org.springframework.context.annotation)
/^\
processConfigBeanDefinitions:315, ConfigurationClassPostProcessor (org.springframework.context.annotation)
/^\
postProcessBeanDefinitionRegistry:232, ConfigurationClassPostProcessor (org.springframework.context.annotation)
/^\
invokeBeanDefinitionRegistryPostProcessors:275, PostProcessorRegistrationDelegate (org.springframework.context.support)
/^\
invokeBeanFactoryPostProcessors:95, PostProcessorRegistrationDelegate (org.springframework.context.support)
/^\
invokeBeanFactoryPostProcessors:705, AbstractApplicationContext (org.springframework.context.support)
/^\

根据 @EnableAutoConfiguration 的完整类名 org.springframework.boot.autoconfigure.EnableAutoConfiguration 作为查找的 Key,获取对应的一组 @Configuration 类。

即从 classpath 中搜寻所有的 META-INF/spring.factories 配置文件,并将其中 org.springframework.boot.autoconfigure.EnableutoConfiguration 对应的配置项通过反射(Java Refletion)实例化为对应的标注了 @Configuration 的 JavaConfig 形式的 IoC 容器配置类,然后汇总为一个并加载到 IoC 容器。

转载于:https://www.jianshu.com/p/e7a33e9eec0e

猜你喜欢

转载自blog.csdn.net/weixin_34061482/article/details/91227168
今日推荐