# 概述

Spring Boot 是由 Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。

# 发展历程

  • 2013年,Pivotal团队开始研发SpringBoot
  • 2014年4月,发布springBoot的第一个版本

# 特点

  • 开箱即用
    指在开发过程中,通过MAVEN项目的pom文件添加相关依赖包,然后使用对应注解来代替繁琐的XML配置文件,以管理对象的生命周期;
  • 约定优于配置
    一种由SpringBoot本身来配置目标结构,由开发者在结构中添加信息的软件设计范式。

# 核心流程

public class SpringApplication {
    
    /*xxx: 静态run方法,跟内部的run方法有着本质区别*/
    public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
        return run(new Class<?>[] { primarySource }, args);
    }

    
    /*xxx: 进行spring应员工的实例化,并调用内部run方法*/
    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        return new SpringApplication(primarySources).run(args);
    }

    /*xxx: spring应用的实例化*/
    /*xxx: springApplication实例化时,会自动做一些初始化操作*/
    /*xxx: 实例化参数为 可变长参数 ,可以指定多个配置源*/
    public SpringApplication(Class<?>... primarySources) {
        this(null, primarySources);
    }

    /*xxx: resourceLoader 用于加载默认的banner信息,默认采用的是 DefaultResourceLoader*/
    /*xxx: primarySources 默认传入SpringBoot的入口类,如果作为springBoot项目引导类,需要被注解 @EnableAutoConfiguration标注*/
    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        /*xxx: 资源加载器*/
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        /*xxx: 推断web应用类型*/
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        /*xxx: 通过spring工厂,加载 bootstrap注册表初始化器*/
        this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories();
        /*xxx: 通过spring工厂,加载并初始化 ApplicationContextInitializer及相关实现类*/
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        /*xxx: 通过spring工厂,加载并初始化 ApplicationContextListener及相关实现类*/
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        /*xxx: 推断main方法 Class类*/
        this.mainApplicationClass = deduceMainApplicationClass();
    }
    
    public ConfigurableApplicationContext run(String... args) {
        
        ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);

        Banner printedBanner = printBanner(environment);

        /*xxx: 通过工厂创建上下文容器*/
        context = createApplicationContext();

        /*xxx: 准备容器,组件对象之间进行关联*/
        prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);

        /*xxx: 初始化容器,通常情况下,只是把最原始的启动bean给加载进来*/
        refreshContext(context);

        /*xxx: 初始化之后执行,默认实现为空*/
        afterRefresh(context, applicationArguments);

        /*xxx: 执行所有的runner接口,实现springBoot上下文启动完成后的后置动作*/
        callRunners(context, applicationArguments);
    }
}

可以简单的概括为这样的几步:

  1. spring应用的初始化 (通过spirng工厂加载BootStrapRegistryInitializer,应用上下文初始化器(ApplicationContextInitializer),应用监听器(ApplicationListener))
  2. spring应用的启动;
    1. 保证启动过程的监听机制生效(SpringApplicationRunListener)
    2. 启动的参数准备(所以说参数不是上下文本身准备的,而是在上下文创建之前就已经准备好);
    3. 创建上下文
    4. 准备上下文
    5. 启动上下文(刷新上下文)
  3. spring应用启动反馈(执行spring应用的运行器,实现上下文创建完成后的后置动作)
    在springBoot环境中,大量的使用到了工厂加载器(SpringFactoriesLoader),这个类在spring源码中进行定义,但是用的比较少。

# 应用初始化

# primarySources

它是spring应用中,唯一不可缺少的参数,实际上表明了,上下文中的bean应该从何处开始得来.

# 推断应用类型,通过枚举类的静态方法(该用法还比较少见)

public enum WebApplicationType {
    NONE,
    SERVLET,
    REACTIVE;

    static WebApplicationType deduceFromClasspath() {
        if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
                && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
            return WebApplicationType.REACTIVE;
        }
        for (String className : SERVLET_INDICATOR_CLASSES) {
            /*xxx: 基于classPath的web应用类型推断,核心实现方法为 ClassUtils.isPresent()*/
            if (!ClassUtils.isPresent(className, null)) {
                return WebApplicationType.NONE;
            }
        }
        return WebApplicationType.SERVLET;
    }
}

# 加载应用上下文初始化器,并缓存在本地

public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
    void initialize(C applicationContext);
}

# 加载应用监听器,并缓存在本地

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    void onApplicationEvent(E event);
}

# 应用启动

# 运行过程监听机制的生效(SpringApplicationRunListener)

  • 抽象定义
public interface SpringApplicationRunListener {
    /*xxx: 当run方法第一次被执行时,会被立即调用,可用于非常早期的初始化工作*/
    default void starting(ConfigurableBootstrapContext bootstrapContext) {
    }

    /*xxx: 当environment准备完成,在 ApplicationContext创建之前,该方法被调用*/
    default void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
                                     ConfigurableEnvironment environment) {
    }

    /*xxx: 当ApplicationContext构建完成,资源还未被加载时,该方法被调用*/
    default void contextPrepared(ConfigurableApplicationContext context) {
    }

    /*xxx: 当ApplicationContext加载完成,未被刷新之前,该方法被调用*/
    default void contextLoaded(ConfigurableApplicationContext context) {
    }

    /*xxx: 当ApplicationContext刷新并启动之后,CommandLindeRunner 和 ApplicationRunner未被调用之前,该方法被调用*/
    default void started(ConfigurableApplicationContext context) {
    }
}
  • 实现
/*xxx: 是 springBoot针对 SpringApplicationRunListener接口的唯一内建实现*/
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
    /*xxx: 事件广播器 */
    private final SimpleApplicationEventMulticaster initialMulticaster;
    
    //xxx: 它的作用是,将spring应用的所有监听器进行收集,当接收到事件通知后,进行广播
    //xxx: 实际上就是将所有的事件,派发给经由spring工厂注册的 应用监听器
}

# 上下文初始化前的参数准备

public class SpringApplication {
    
    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
                                                       DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
        /*xxx: 获取或创建环境 */
        ConfigurableEnvironment environment = getOrCreateEnvironment();

        /*xxx: 通知环境已经准备完成*/
        listeners.environmentPrepared(bootstrapContext, environment);

        return environment;
    }
}
  • 加载正常的配置文件(通过环境后置处理器监听器进行委派-EnvironmentPostProcessorApplicationListener)
public class EnvironmentPostProcessorApplicationListener implements SmartApplicationListener, Ordered {
    
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationEnvironmentPreparedEvent) {
            /*xxx: 环境准备完成,后置动作*/
            onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
        }
    }

    private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
        ConfigurableEnvironment environment = event.getEnvironment();
        SpringApplication application = event.getSpringApplication();
        /*xxx: 委派所有的 环境后置处理器进行处理器 */
        for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(event.getBootstrapContext())) {
            postProcessor.postProcessEnvironment(environment, application);
        }
    }
}

/*xxx: 顶级接口,环境后置处理器*/
public interface EnvironmentPostProcessor {
    /*xxx: 后置处理环境信息*/
    void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application);
}

实现(ConfigDataEnvironmentPostProcessor)

/*xxx: 配置数据环境后置处理器,用于加载 启动参数 */
public class ConfigDataEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered {
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        /*xxx: 变量后置处理器*/
        postProcessEnvironment(environment, application.getResourceLoader(), application.getAdditionalProfiles());
    }

    /*xxx: 配置参数的本质: configDataEnvironment.processAndApply */
    void postProcessEnvironment(ConfigurableEnvironment environment, ResourceLoader resourceLoader,
                                Collection<String> additionalProfiles) {
        getConfigDataEnvironment(environment, resourceLoader, additionalProfiles).processAndApply();
    }

    ConfigDataEnvironment getConfigDataEnvironment(ConfigurableEnvironment environment, ResourceLoader resourceLoader,
                                                   Collection<String> additionalProfiles) {
        return new ConfigDataEnvironment(this.logFactory, this.bootstrapContext, environment, resourceLoader,
                additionalProfiles, this.environmentUpdateListener);
    }
}

核心实现

/*xxx: 配置数据环境,用于读取.yml,以及.properties配置文件*/
class ConfigDataEnvironment {
    /*xxx: 上下文配置数据的 默认搜索路径*/
    static final ConfigDataLocation[] DEFAULT_SEARCH_LOCATIONS;
    static {
        /*xxx: 首先从 classpath中加载,其次还会从相对路径的 config目录下加载*/
        List<ConfigDataLocation> locations = new ArrayList<>();
        /*xxx: 将路径封装为 特定的 location,optional标明该配置是否必须存在*/
        locations.add(ConfigDataLocation.of("optional:classpath:/"));
        locations.add(ConfigDataLocation.of("optional:classpath:/config/"));
        locations.add(ConfigDataLocation.of("optional:file:./"));
        locations.add(ConfigDataLocation.of("optional:file:./config/"));
        locations.add(ConfigDataLocation.of("optional:file:./config/*/"));
        DEFAULT_SEARCH_LOCATIONS = locations.toArray(new ConfigDataLocation[0]);
    }

    /*xxx: 处理所有的属性源,并将新引入的配置应用于 environment */
    void processAndApply() {
        /*xxx: 在该阶段,将会读取 默认的配置项,如 application.yml,application.properties等*/
        ConfigDataEnvironmentContributors contributors = processInitial(this.contributors, importer);

        /*xxx: 将contributor应用于环境中 */
        applyToEnvironment(contributors, activationContext);
    }

    private ConfigDataEnvironmentContributors processInitial(ConfigDataEnvironmentContributors contributors,
                                                             ConfigDataImporter importer) {
        contributors = contributors.withProcessedImports(importer, null);
        return contributors;
    }

    private void applyToEnvironment(ConfigDataEnvironmentContributors contributors,
                                    ConfigDataActivationContext activationContext) {
        MutablePropertySources propertySources = this.environment.getPropertySources();

        for (ConfigDataEnvironmentContributor contributor : contributors) {
            PropertySource<?> propertySource = contributor.getPropertySource();
            /*xxx: 省略若干抽象...*/
            
            /*xxx: 如果是 active 的状态,则将其加入 environment中 */
            propertySources.addLast(propertySource);
        }
    }
    
}
class ConfigDataEnvironmentContributors implements Iterable<ConfigDataEnvironmentContributor> {
    
    ConfigDataEnvironmentContributors withProcessedImports(ConfigDataImporter importer,
                                                           ConfigDataActivationContext activationContext) {
        while (true) {
            ConfigDataEnvironmentContributor contributor = getNextToProcess(result, activationContext, importPhase);
            
            /*xxx: 省略其他抽象...*/
            
            List<ConfigDataLocation> imports = contributor.getImports();
            /*xxx: 实际加载配置的动作由 此方法完成 */
            Map<ConfigDataResolutionResult, ConfigData> imported = importer.resolveAndLoad(activationContext,
                    locationResolverContext, loaderContext, imports);
            /*xxx: 省略其他抽象...*/
        }
    }
}
class ConfigDataImporter {

    private final ConfigDataLocationResolvers resolvers;
    
    /*xxx: 实际加载配置的动作由 此方法完成 */
    Map<ConfigDataResolutionResult, ConfigData> resolveAndLoad(ConfigDataActivationContext activationContext,
                                                               ConfigDataLocationResolverContext locationResolverContext, ConfigDataLoaderContext loaderContext,
                                                               List<ConfigDataLocation> locations) {
        /*xxx: 加载配置文件的时候,会根据profiles一起去加载 该配置文件 */
        Profiles profiles = (activationContext != null) ? activationContext.getProfiles() : null;
        List<ConfigDataResolutionResult> resolved = resolve(locationResolverContext, profiles, locations);
        return load(loaderContext, resolved);
    }
    
    //xxx: resole方法进行多层次的多态,这里直接贴最核心的方法
    private List<ConfigDataResolutionResult> resolve(ConfigDataLocationResolverContext locationResolverContext,
                                                     Profiles profiles, ConfigDataLocation location) {
        /*xxx: 加载配置文件,最终还是委派给 解析器完成的 */
        return this.resolvers.resolve(locationResolverContext, location, profiles);
    }
}
/*xxx: 根据位置获取配置数据解析器 */
class ConfigDataLocationResolvers {
    
    /*xxx: 实际上配置文件的加载分为了两步 */
    private List<ConfigDataResolutionResult> resolve(ConfigDataLocationResolver<?> resolver,
                                                     ConfigDataLocationResolverContext context, ConfigDataLocation location, Profiles profiles) {
        /*xxx: 第一次,首先将application.yml,properties等加载,之后再来处理 profiles的情况*/
        List<ConfigDataResolutionResult> resolved = resolve(location, false, () -> resolver.resolve(context, location));

        /*xxx: 如果没有指定profiles,则直接返回 */
        if (profiles == null) {
            return resolved;
        }

        List<ConfigDataResolutionResult> profileSpecific = resolve(location, true,
                () -> resolver.resolveProfileSpecific(context, location, profiles));
        
        return merge(resolved, profileSpecific);
    }

    //xxx: resolve方法,实际上是对解析器加载后的后置处理 
    private List<ConfigDataResolutionResult> resolve(ConfigDataLocation location, boolean profileSpecific,
                                                     Supplier<List<? extends ConfigDataResource>> resolveAction) {
        List<ConfigDataResource> resources = nonNullList(resolveAction.get());
        List<ConfigDataResolutionResult> resolved = new ArrayList<>(resources.size());
        for (ConfigDataResource resource : resources) {
            resolved.add(new ConfigDataResolutionResult(location, resource, profileSpecific));
        }
        return resolved;
    }
}
/*xxx: 实际配置的解析加载,都是由该类完成的 */
public class StandardConfigDataLocationResolver
		implements ConfigDataLocationResolver<StandardConfigDataResource>, Ordered {
    
    @Override
    public List<StandardConfigDataResource> resolve(ConfigDataLocationResolverContext context,
                                                    ConfigDataLocation location) throws ConfigDataNotFoundException {
        /*xxx: 在解析前,首先将配置整合到一起,并构造为 引用配置信息*/
        return resolve(getReferences(context, location));
    }

    private Set<StandardConfigDataReference> getReferences(ConfigDataLocationResolverContext context,
                                                           ConfigDataLocation configDataLocation) {
        String resourceLocation = getResourceLocation(context, configDataLocation);
        /*xxx: 省略其他抽象...*/
        
        return getReferencesForDirectory(configDataLocation, resourceLocation, NO_PROFILE);
    }

    /*xxx: 从目录中加载配置,如果有profile,则会进行相应的处理 */
    private Set<StandardConfigDataReference> getReferencesForDirectory(ConfigDataLocation configDataLocation,
                                                                       String directory, String profile) {
        /*xxx: 将spring.config.name 的名字,与路径相互结合,获取初始化配置*/
        Set<StandardConfigDataReference> references = new LinkedHashSet<>();
        for (String name : this.configNames) {
            Deque<StandardConfigDataReference> referencesForName = getReferencesForConfigName(name, configDataLocation,
                    directory, profile);
            references.addAll(referencesForName);
        }
        return references;
    }

    private Deque<StandardConfigDataReference> getReferencesForConfigName(String name,
                                                                          ConfigDataLocation configDataLocation, String directory, String profile) {
        Deque<StandardConfigDataReference> references = new ArrayDeque<>();
        for (PropertySourceLoader propertySourceLoader : this.propertySourceLoaders) {
            for (String extension : propertySourceLoader.getFileExtensions()) {
                /*xxx: 默认情况下,加载顺序的双端队列情况为: yaml,yml,xml,properties */
                /*xxx: 加载好后,后续有一步加载时,出队的顺序是从队尾开始出队*/
                StandardConfigDataReference reference = new StandardConfigDataReference(configDataLocation, directory,
                        directory + name, profile, extension, propertySourceLoader);
                if (!references.contains(reference)) {
                    references.addFirst(reference);
                }
            }
        }
        return references;
    }

    /*xxx: 加载引用资源里面的实际内容 */
    private List<StandardConfigDataResource> resolve(StandardConfigDataReference reference) {
        if (!this.resourceLoader.isPattern(reference.getResourceLocation())) {
            return resolveNonPattern(reference);
        }
        return resolvePattern(reference);
    }

    /*xxx: 实际加载资源的动作。 */
    private List<StandardConfigDataResource> resolveNonPattern(StandardConfigDataReference reference) {
        Resource resource = this.resourceLoader.getResource(reference.getResourceLocation());
        /*xxx: 如果资源不存在,则返回空*/
        if (!resource.exists() && reference.isSkippable()) {
            logSkippingResource(reference);
            return Collections.emptyList();
        }
        /*xxx: 根据实际加载到的资源,封装 StandardConfigDataResource */
        return Collections.singletonList(createConfigResourceLocation(reference, resource));
    }
}
  • 配置优先级
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
/*xxx: 实际配置的解析加载,都是由该类完成的 */
public class StandardConfigDataLocationResolver
    implements ConfigDataLocationResolver<StandardConfigDataResource>, Ordered {
    
    private Deque<StandardConfigDataReference> getReferencesForConfigName(String name,
                                                                          ConfigDataLocation configDataLocation, String directory, String profile) {
        Deque<StandardConfigDataReference> references = new ArrayDeque<>();
        for (PropertySourceLoader propertySourceLoader : this.propertySourceLoaders) {
            for (String extension : propertySourceLoader.getFileExtensions()) {
                /*xxx: 默认情况下,加载顺序的双端队列情况为: yaml,yml,xml,properties */
                /*xxx: 加载好后,后续有一步加载时,出队的顺序是从队尾开始出队*/
                StandardConfigDataReference reference = new StandardConfigDataReference(configDataLocation, directory,
                        directory + name, profile, extension, propertySourceLoader);
                if (!references.contains(reference)) {
                    references.addFirst(reference);
                }
            }
        }
        return references;
    }
}

class ConfigDataImporter {
    
    /*xxx: 从资源句柄中,加载内容*/
    private Map<ConfigDataResolutionResult, ConfigData> load(ConfigDataLoaderContext loaderContext,
                                                             List<ConfigDataResolutionResult> candidates) throws IOException {
        Map<ConfigDataResolutionResult, ConfigData> result = new LinkedHashMap<>();
        /*xxx: 正常情况下,资源句柄的序列为: yaml,yml,xml,properties*/
        /*xxx: 这里由队尾开始处理,使得优先级从队尾依次排开 */
        for (int i = candidates.size() - 1; i >= 0; i--) {
            if (this.loaded.add(resource)) {
                /*xxx: 省略其他抽象...*/
            }
        }
    }
}
  • 加载远程配置(属于是springCloud部分的内容,此处可以先简单的接触下)
public class BootstrapApplicationListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {
    
    @Override
    /*xxx: bootstrap父类应用监听器,会以bootstrap为名称,实例化一个轻量级的容器 */
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {

        ConfigurableApplicationContext context = bootstrapServiceContext(environment, event.getSpringApplication(), configName);

        apply(context, event.getSpringApplication(), environment);
    }

    /*xxx: 配置并创建 bootstrap上下文容器 */
    private ConfigurableApplicationContext bootstrapServiceContext(ConfigurableEnvironment environment,
                                                                   final SpringApplication application, String configName) {
        /*xxx: 实例化父级上下文 */
        SpringApplicationBuilder builder = new SpringApplicationBuilder().profiles(environment.getActiveProfiles())
                .bannerMode(Mode.OFF).environment(bootstrapEnvironment)
                .registerShutdownHook(false).logStartupInfo(false).web(WebApplicationType.NONE);
        builder.sources(BootstrapImportSelectorConfiguration.class);
        /*xxx: 创建bootstrap上下文容器 */
        final ConfigurableApplicationContext context = builder.run();
        return context;
    }

    private void apply(ConfigurableApplicationContext context, SpringApplication application,
                       ConfigurableEnvironment environment) {
        Set target = new LinkedHashSet<>(application.getInitializers());
        target.addAll(getOrderedBeansOfType(context, ApplicationContextInitializer.class));
        application.setInitializers(target);
        /*xxx: 配置解密的初始化器,略*/
        addBootstrapDecryptInitializer(application);
    }
}

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(PropertySourceBootstrapProperties.class)
public class PropertySourceBootstrapConfiguration
        implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
    @Autowired(required = false)
    /*xxx: 属性源,该属性源 由 springCloud提供*/
    private List<PropertySourceLocator> propertySourceLocators = new ArrayList<>();

    @Override
    /*xxx: 核心初始化流程,其主要作用就是基于特定规则,将多个配置合并*/
    public void initialize(ConfigurableApplicationContext applicationContext) {
        List<PropertySource<?>> composite = new ArrayList<>();
        /*xxx: 对所有属性源进行排序*/
        AnnotationAwareOrderComparator.sort(this.propertySourceLocators);
        /*xxx: 筛选有效属性源配置,并收集*/
        for (PropertySourceLocator locator : this.propertySourceLocators) {
            Collection<PropertySource<?>> source = locator.locateCollection(environment);
            List<PropertySource<?>> sourceList = new ArrayList<>();
            for (PropertySource<?> p : source) {
                sourceList.add(new SimpleBootstrapPropertySource(p));
            }
            /*xxx: 省略了部分抽象...*/
            
            composite.addAll(sourceList);
        }
        MutablePropertySources propertySources = environment.getPropertySources();
        /*xxx: 配置属性源*/
        insertPropertySources(propertySources, composite);
    }
}
# springCloud本身的配置, nacos 与 spring-cloud-config
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration,\
com.alibaba.cloud.nacos.NacosConfigBootstrapConfiguration,\
org.springframework.cloud.config.client.ConfigServiceBootstrapConfiguration
#spirng-config基于ConfigServicePropertySourceLocator,nacos基于 NacosPropertySourceLocator

需要注意的是,BootstrapApplicationListener优于EnvironmentPostProcessorApplicationListener而率先实例化,所以application中的属性,是不能为bootstrap使用的,反之则可以

public class EnvironmentPostProcessorApplicationListener implements SmartApplicationListener, Ordered {
    /**
     * The default order for the processor.
     */
    public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 10;
}

public class BootstrapApplicationListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {
    /**
     * The default order for this listener.
     */
    public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 5;
}

# 上下文的创建

public class SpringApplication {
    protected ConfigurableApplicationContext createApplicationContext() {
        return this.applicationContextFactory.create(this.webApplicationType);
    }
}
/*xxx: 顶级接口,应用上下文工厂*/
public interface ApplicationContextFactory {
    /*xxx: 应用上下文工厂的默认实现,包括servlet应用,reactive应用 以及 常规应用 */
    ApplicationContextFactory DEFAULT = (webApplicationType) -> {
        try {
            switch (webApplicationType) {
                case SERVLET:
                    /*xxx: 当环境为 servlet环境时,使用的是 annotationConfigServletWebServer上下文 */
                    return new AnnotationConfigServletWebServerApplicationContext();
                case REACTIVE:
                    return new AnnotationConfigReactiveWebServerApplicationContext();
                default:
                    /*xxx: 非web环境下,使用的是 annotationConfig上下文*/
                    return new AnnotationConfigApplicationContext();
            }
        }
        catch (Exception ex) {
            throw new IllegalStateException("Unable create a default ApplicationContext instance, "
                    + "you may need a custom ApplicationContextFactory", ex);
        }
    };
    /*xxx: 根据给定的应用类型,为springApplication 创建相应的上下文工厂*/
    ConfigurableApplicationContext create(WebApplicationType webApplicationType);
}

可以知道的结论是:

  • 上下文的创建,是根据spring应用的对于当前环境的感知判断而来;
  • 不论是web环境,还是非web环境,这些上下文都具有扫描注解包注册bean的功能;

# 上下文的准备

public class SpringApplication {
    private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
                                ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
                                ApplicationArguments applicationArguments, Banner printedBanner) {
        /*xxx: 上下文装配阶段*/
        
        /*xxx: 通知上下文装配完成*/
        listeners.contextPrepared(context);
        
        /*xxx:  上下文加载阶段*/
        
        /*xxx: 通知上下文加载完成*/
        listeners.contextLoaded(context);
    }
}

# 上下文装配阶段

  • 绑定环境配置信息
public class SpringApplication {
    /*xxx: 准备上下文,由springBoot提供, spring中也提供了 prepareBeanFactory方法*/
    private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
                                ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
                                ApplicationArguments applicationArguments, Banner printedBanner) {
        /*xxx: 设置上下文的配置环境 */
        context.setEnvironment(environment);
    }
}
  • 装配基础设施类
public class SpringApplication {
    /*xxx: 准备上下文,由springBoot提供, spring中也提供了 prepareBeanFactory方法*/
    private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
                                ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
                                ApplicationArguments applicationArguments, Banner printedBanner) {
        /*xxx: 应用上下文后置处理*/
        postProcessApplicationContext(context);
    }

    protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
        if (this.beanNameGenerator != null) {
            context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
                    this.beanNameGenerator);
        }
        if (this.resourceLoader != null) {
            if (context instanceof GenericApplicationContext) {
                ((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
            }
            if (context instanceof DefaultResourceLoader) {
                ((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
            }
        }
        if (this.addConversionService) {
            context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
        }
    }
}
  • 上下文定制化配置
public class SpringApplication {
    /*xxx: 准备上下文,由springBoot提供, spring中也提供了 prepareBeanFactory方法*/
    private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
                                ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
                                ApplicationArguments applicationArguments, Banner printedBanner) {
        /*xxx: 应用上下文刷新之前,ApplicationContextInitializer初始化context*/
        applyInitializers(context);
    }

    /*xxx: 通过上下文初始化器,可以对上下文进行定制化修改*/
    protected void applyInitializers(ConfigurableApplicationContext context) {
        for (ApplicationContextInitializer initializer : getInitializers()) {
            Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
                    ApplicationContextInitializer.class);
            Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
            initializer.initialize(context);
        }
    }
}

# 上下文加载阶段

public class SpringApplication {
    
    /*xxx: 准备上下文,由springBoot提供, spring中也提供了 prepareBeanFactory方法*/
    private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
                                ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
                                ApplicationArguments applicationArguments, Banner printedBanner) {
        /*xxx: 获取全部配置源,其中包含 primarySources 和 sources */
        Set<Object> sources = getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");

        /*xxx: 将sources中的bean加载到context中*/
        load(context, sources.toArray(new Object[0]));
    }

    protected void load(ApplicationContext context, Object[] sources) {
        BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
        loader.load();
    }
}

# 上下文的启动(上下文的刷新)

public class SpringApplication {
    private void refreshContext(ConfigurableApplicationContext context) {
        refresh(context);
    }

    protected void refresh(ConfigurableApplicationContext applicationContext) {
        applicationContext.refresh();
    }
}
  • 可以看出,上下文的启动阶段,由spring本身完成;

# 应用启动反馈

public class SpringApplication {
    public ConfigurableApplicationContext run(String... args) {
        /*xxx: 通知监听器,容器启动完成,出发回调 */
        listeners.started(context);

        /*xxx: 执行所有的runner接口,实现springBoot上下文启动完成后的后置动作*/
        callRunners(context, applicationArguments);
    }

    private void callRunners(ApplicationContext context, ApplicationArguments args) {
        List<Object> runners = new ArrayList<>();
        runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
        runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
        AnnotationAwareOrderComparator.sort(runners);
        /*xxx: 提供了两个层面的runner,都会被调用*/
        for (Object runner : new LinkedHashSet<>(runners)) {
            if (runner instanceof ApplicationRunner) {
                callRunner((ApplicationRunner) runner, args);
            }
            if (runner instanceof CommandLineRunner) {
                callRunner((CommandLineRunner) runner, args);
            }
        }
    }
}

# 自动装配

# 流程

/*xxx: 被该注解注释后,当前的包路径会作为扫描的根路径*/
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    /*xxx: 根据类排除指定的自动配置*/
    Class<?>[] exclude() default {};

    /*xxx: 根据类名,排除指定的自动配置*/
    String[] excludeName() default {};
}
/*xxx: 具有 资源加载器,bean工厂,环境,类加载器的感知能力*/
/*xxx: order可以自定义加载的顺序*/
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
        ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {

    /*xxx: 根据条件以及配置,spring.factories 获取需要配置的bean */
    protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        }
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        /*xxx: 通过SpringFactoriesLoader类提供的方法,加载类路径中 META-INF 目录下的 spring.factories文件中,针对EnableAutoConfiguration的注册配置类*/
        List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
        /*xxx: 对获得的注册配置类集合进行去重处理,防止 多个模块引入相同的配置类*/
        configurations = removeDuplicates(configurations);
        /*xxx: 获取注解中被 exclude 或 excludeName 所排除的类的集合*/
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        /*xxx: 检查排除的类是否可实例化,是否被自动注册配置所使用,不符合条件则抛异常*/
        checkExcludedClasses(configurations, exclusions);
        /*xxx: 从自动配置类集合中去除被排除的类*/
        configurations.removeAll(exclusions);
        /*xxx: 检查配置类的注解,是否符合 spring.factories文件中 AutoConfigurationImportFilter指定的注解检查条件*/
        configurations = getConfigurationClassFilter().filter(configurations);
        /*xxx: 将筛选完成的配置类和排查了的配置类构建为事件类,并传入监听器  监听器的配置位于 spring.factories文件的 AutoConfigurationImportListener项*/
        fireAutoConfigurationImportEvents(configurations, exclusions);
        return new AutoConfigurationEntry(configurations, exclusions);
    }

    private static class AutoConfigurationGroup
            implements DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {
        @Override
        /*xxx: 填充entries*/
        public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
            /*xxx: 通过spring工厂,将自动配置项配置,进行加载,以及根据配置进行过滤 */
            AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
                    .getAutoConfigurationEntry(annotationMetadata);
            this.autoConfigurationEntries.add(autoConfigurationEntry);
            
        }

        @Override
        /*xxx: 获取entries*/
        public Iterable<Entry> selectImports() {
            /*xxx: 去重*/
            Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
                    .map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
                    .collect(Collectors.toCollection(LinkedHashSet::new));
            processedConfigurations.removeAll(allExclusions);

            /*xxx: 将自动配置项进行 排序 */
            return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
                    .map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
                    .collect(Collectors.toList());
        }

        /*xxx: 对autoConfiguration进行排序,如果有些配置项有先后顺序,则采用该方式能够满足需求*/
        private List<String> sortAutoConfigurations(Set<String> configurations,
                                                    AutoConfigurationMetadata autoConfigurationMetadata) {
            return new AutoConfigurationSorter(getMetadataReaderFactory(), autoConfigurationMetadata)
                    .getInPriorityOrder(configurations);
        }
    }
}

# 自动装配的优先级排序

class AutoConfigurationSorter {
    AutoConfigurationSorter(MetadataReaderFactory metadataReaderFactory,
                            AutoConfigurationMetadata autoConfigurationMetadata) {
        Assert.notNull(metadataReaderFactory, "MetadataReaderFactory must not be null");
        this.metadataReaderFactory = metadataReaderFactory;
        this.autoConfigurationMetadata = autoConfigurationMetadata;
    }

    /*xxx: 进行优先顺序排序 */
    List<String> getInPriorityOrder(Collection<String> classNames) {
        AutoConfigurationClasses classes = new AutoConfigurationClasses(this.metadataReaderFactory,
                this.autoConfigurationMetadata, classNames);
        List<String> orderedClassNames = new ArrayList<>(classNames);
        // Initially sort alphabetically
        /*xxx: 首先进行 自然序排序 */
        Collections.sort(orderedClassNames);
        // Then sort by order
        /*xxx: 其次,通过 order 进行优先级排序 */
        orderedClassNames.sort((o1, o2) -> {
            int i1 = classes.get(o1).getOrder();
            int i2 = classes.get(o2).getOrder();
            return Integer.compare(i1, i2);
        });
        // Then respect @AutoConfigureBefore @AutoConfigureAfter
        /*xxx: 最后 根据 @AutoConfigureBefore  以及 @AutoConfigureAfter 的注解 进行排序 */
        orderedClassNames = sortByAnnotation(classes, orderedClassNames);
        return orderedClassNames;
    }
}
  • 根据 @AutoConfigureBefore 以及 @AutoConfigureAfter 排序的实现 (本质上,是图算法的 深度图遍历)
class AutoConfigurationSorter {
    private List<String> sortByAnnotation(AutoConfigurationClasses classes, List<String> classNames) {
        List<String> toSort = new ArrayList<>(classNames);
        /*xxx: toSort里面,存储了两遍数据 */
        toSort.addAll(classes.getAllNames());
        /*xxx: 存储已经被排好序的数据*/
        Set<String> sorted = new LinkedHashSet<>();
        /*xxx: 存储某次处理过程中,正在处理的数据*/
        Set<String> processing = new LinkedHashSet<>();
        while (!toSort.isEmpty()) {
            /*xxx: 查找某个配置元素的 直接 后继元素 */
            doSortByAfterAnnotation(classes, toSort, sorted, processing, null);
        }
        sorted.retainAll(classNames);
        return new ArrayList<>(sorted);
    }

    private void doSortByAfterAnnotation(AutoConfigurationClasses classes, List<String> toSort, Set<String> sorted,
                                         Set<String> processing, String current) {
        if (current == null) {
            /*xxx: 进行遍历 */
            current = toSort.remove(0);
        }
        /*xxx: 正在处理元素入队*/
        processing.add(current);
        /*xxx: 根据某一个配置元素 的直接前驱 以及 直接后继元素,进行深度树检索。 检索过程中,会进行循环依赖的判断*/
        for (String after : classes.getClassesRequestedAfter(current)) {
            checkForCycles(processing, current, after);
            if (!sorted.contains(after) && toSort.contains(after)) {
                doSortByAfterAnnotation(classes, toSort, sorted, processing, after);
            }
        }
        processing.remove(current);
        sorted.add(current);
    }

    /*xxx: 查找某一个元素的直接前驱以及后继元素 */
    Set<String> getClassesRequestedAfter(String className) {
        /*xxx: 查找这个类的 前驱配置 */
        Set<String> classesRequestedAfter = new LinkedHashSet<>(get(className).getAfter());
        this.classes.forEach((name, autoConfigurationClass) -> {
            /*xxx: 查找这个类的  直接后继 配置*/
            if (autoConfigurationClass.getBefore().contains(className)) {
                classesRequestedAfter.add(name);
            }
        });
        return classesRequestedAfter;
    }
}

# 参数配置

# 集中配置的使用

  • 使用 @Configuration 使某一个类成为配置类
  • 使用 @ConfigurationProperties 使某前缀配置生效(@EnableConfigurationProperties 可以与扫描注册configurationProperties,也可指定注册)

# 配置元数据的生成

  • 引用模块: spring-boot-configuration-processor
  • 配置元数据文件: META-INF/spring-configuration-metadata.json,也可通过IDE使用 Annotation Processors自动生成配置元数据

# 元数据的作用

在使用配置项进行配置时,IDE会自动进行提示;

# 参数绑定

# @Confiration参数绑定

  • 松散绑定
    • 参数绑定时,把中划线-,下划线_都去掉,且不区分大小写,称为松散绑定(宽松绑定)
    • @ConfigurationProperties默认提供宽松绑定
    • 虽然松散绑定很随意,但是推荐使用烤肉串模式如super-admin
    • @Value不支持松散绑定
    • @ConfigurationProperties的属性prefix前缀命名规范仅支持纯小写字母、数字、下划线、中划线

# 绑定规则

  • Map绑定
    • key只能包含大小写字母、数字和短横线、下划线
    • 非以上字符将会被去掉
    • 如果key需要出现其它字符,需要包含在中括号里
demo:
  map:
    key3: va1
    /key4: dafd
    FDS: dsfds
    DK-FJD_Ss/df: sdfdsf
    "[/sf#jd]": sdfd
    "[dskf_1_]": fdsf
    
#显示结果如下(为了防止语法冲突,根节点后加了个1)
demo1:
  map:
    key3: va1
    key4: dafd
    FDS: dsfds
    DK-FJD_Ssdf: sdfdsf
    "[/sf#jd]": sdfd
    "[dskf_1_]": fdsf
  • 数组绑定
user:
    girlFriends:
        - 1
        - 2
        - 3
        - 4
  • 参数优先级
    • 详见springCloud相关笔记;

# @Value参数绑定

# 命令行参数绑定

  • 通过使用--开头的参数,会自动被识别为springBoot参数,如spirng.config.location=./application.yml
  • 命令行参数的优先级最高(因为其排在队列最前面)

# @PropertySource参数引入

  • 只支持配置*.properties配置文件到容器中
  • 该参数用于指定配置来源,主要是为辅助参数绑定

# Binder参数绑定工具

# 使用示例

class Test{
    static{
        Map result= Binder.get(environment).bind("demo.map", Bindable.mapOf(String.class,Object.class)).get();
    }
}

# 使用指南

  • 绑定到对象实例:Bindable.of(T instance)

  • 绑定到对象:Bindable.of(Class<T> clazz)

  • 绑定到数组:Bindable.Listof(Class<T> clazz)

  • 绑定到集合:Bindable.Setof(Class<T> clazz)

  • 绑定到map字典:Bindable.Mapof(Class<T> clazz)

# 条件装配

# 条件装配的注释事项

  • 条件装配,需要定义在**@Configuration配置类**上

# 条件解析流程

/*xxx: 配置类解析器 */
class ConfigurationClassParser {
    
    /*xxx: 条件评估器*/
	private final ConditionEvaluator conditionEvaluator;
    
    public ConfigurationClassParser(MetadataReaderFactory metadataReaderFactory,
			ProblemReporter problemReporter, Environment environment, ResourceLoader resourceLoader,
			BeanNameGenerator componentScanBeanNameGenerator, BeanDefinitionRegistry registry) {
     //xxx: 省略其它抽象...
        this.conditionEvaluator = new ConditionEvaluator(registry, environment, resourceLoader);
    }
    
    /*xxx: 实际解析配置类的方法*/
	protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
		/*xxx: 首先,如果当前的 配置类,不符合配置要求,则跳过*/
		if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
			return;
		}
        
        //xxx: 省略其它抽象...
    }
}
class ConditionEvaluator {
    
 	public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
		/*xxx: 没有Conditional注解,则肯定不会跳过解析 */
		if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
			return false;
		}

		if (phase == null) {
			if (metadata instanceof AnnotationMetadata &&
					ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
				//xxx: 解析 配置类的条件判定
				return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
			}
			//xxx: 注册bean的条件判定
			return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
		}

		List<Condition> conditions = new ArrayList<>();
		/*xxx: 实例化条件对象实例*/
		for (String[] conditionClasses : getConditionClasses(metadata)) {
			for (String conditionClass : conditionClasses) {
				Condition condition = getCondition(conditionClass, this.context.getClassLoader());
				conditions.add(condition);
			}
		}

		/*xxx: 对条件进行排序*/
		AnnotationAwareOrderComparator.sort(conditions);

		/*xxx: 判定条件 */
		for (Condition condition : conditions) {
			ConfigurationPhase requiredPhase = null;
			/*xxx: 对于配置条件,判定其phase是否一致 */
			if (condition instanceof ConfigurationCondition) {
				requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
			}
			/*xxx: 对于通用条件,判定其matches 方法是否匹配, 只要有一个不匹配,则会跳过配置 */
			if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
				return true;
			}
		}

		return false;
	}
    
    //xxx: 省略其它抽象...
}

# SpringBoot的条件体系

  • 配置定义
@Conditional(OnBeanCondition.class)
/*xxx: 在容器中有指定bean的条件下满足*/
public @interface ConditionalOnBean {
	//xxx: 内部配置可以自定义,并无强制约束
}
@Conditional(OnBeanCondition.class)
/*xxx: 当容器中没有指定bean时,满足条件*/
public @interface ConditionalOnMissingBean {}
@Conditional(OnClassCondition.class)
/*xxx: 在classpath类路径下有指定类满足条件*/
public @interface ConditionalOnClass {}
@Conditional(OnClassCondition.class)
/*xxx: 当类路径中没有指定类时满足条件*/
public @interface ConditionalOnMissingClass {}
  • 条件定义
/*xxx: springBoot条件抽象*/
public abstract class SpringBootCondition implements Condition {
 	/*xxx: 重写 matches方法 */
	public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
     	/*xxx: 获取匹配结果*/
		ConditionOutcome outcome = getMatchOutcome(context, metadata);
        return outcome.isMatch();
    }
    
    /*xxx: 获取匹配结果,本质上就是一个 true or false,只是同时存储了 匹配信息*/
	public abstract ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata);
}
@Order(Ordered.LOWEST_PRECEDENCE)
/*xxx: 判定bean是否存在的条件 */
class OnBeanCondition extends FilteringSpringBootCondition implements ConfigurationCondition {
 	
    @Override
	public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
        ConditionMessage matchMessage = ConditionMessage.empty();
     	//xxx: 可以处理多个条件注解
		if (annotations.isPresent(ConditionalOnBean.class)) {
            //xxx: 省略抽象..., 不匹配时直接返回
        }
        if (metadata.isAnnotated(ConditionalOnSingleCandidate.class.getName())) {
            //xxx: 省略抽象... 不匹配时直接返回
        }
        if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {
            //xxx: 省略抽象... 不匹配时直接返回
        }
        
        return ConditionOutcome.match(matchMessage);
        
    }
}
@Order(Ordered.HIGHEST_PRECEDENCE)
/*xxx: 过滤逻辑是,使用类加载器去加载指定的类*/
	/*xxx: 根据指定的类加载的条件 */
class OnClassCondition extends FilteringSpringBootCondition {
 	@Override
	public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
		/*xxx: 获取类加载器 */
     	ClassLoader classLoader = context.getClassLoader();
		ConditionMessage matchMessage = ConditionMessage.empty();
        
        List<String> onClasses = getCandidates(metadata, ConditionalOnClass.class);
		if (onClasses != null) {
         	   //xxx: 省略抽象..., 不匹配时直接返回
        }
        
        List<String> onMissingClasses = getCandidates(metadata, ConditionalOnMissingClass.class);
		if (onMissingClasses != null) {
         	   //xxx: 省略抽象..., 不匹配时直接返回
        }
        
        return ConditionOutcome.match(matchMessage);
    }
}

# cache自动装备

# cache自动装配的条件

  • @ConditionalOnBean(CacheAspectSupport.class):上下文中开启了cache功能,即使用了EnableCaching
  • @ConditionalOnMissingBean(value = CacheManager.class, name = "cacheResolver"):不存在自定义的CacheManager,以及自定义的cacheResolver

# 源码解析

@ConditionalOnClass(CacheManager.class)
@ConditionalOnBean(CacheAspectSupport.class)
@ConditionalOnMissingBean(value = CacheManager.class, name = "cacheResolver")
/*xxx: 缓存自动装配的要求: 1.使用了 @EnableCaching注解 2.未自定义CacheManager以及cacheResolver*/
@Import({ CacheConfigurationImportSelector.class })
public class CacheAutoConfiguration {
    
    static class CacheConfigurationImportSelector implements ImportSelector {

		@Override
		/*xxx: 此处通过 ImportSelector,同时引入多个配置类*/
		/*xxx: 类型包括: generic,ehcache,hazelcast,infinispan,cache,couchbase,redis,caffeine,simple,none
		xxx: 		如果同时满足多个条件,位置在前的配置,具有更高的优先级   
		*   */
		public String[] selectImports(AnnotationMetadata importingClassMetadata) {
			CacheType[] types = CacheType.values();
			String[] imports = new String[types.length];
			for (int i = 0; i < types.length; i++) {
				imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
			}
			return imports;
		}

	}
}
@ConditionalOnBean(Cache.class)
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
/*xxx: 收集cache进行管理*/
class GenericCacheConfiguration {

	@Bean
	SimpleCacheManager cacheManager(CacheManagerCustomizers customizers, Collection<Cache> caches) {
		SimpleCacheManager cacheManager = new SimpleCacheManager();
		cacheManager.setCaches(caches);
		return customizers.customize(cacheManager);
	}

}
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
/*xxx: 默认的Cache管理器实现*/
class SimpleCacheConfiguration {

	@Bean
	ConcurrentMapCacheManager cacheManager(CacheProperties cacheProperties,
			CacheManagerCustomizers cacheManagerCustomizers) {
		ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
		List<String> cacheNames = cacheProperties.getCacheNames();
		/*xxx: 如果设置了缓存名称,则是静态的,不可新增其它的缓存*/
		if (!cacheNames.isEmpty()) {
			cacheManager.setCacheNames(cacheNames);
		}
		return cacheManagerCustomizers.customize(cacheManager);
	}

}
class CacheCondition extends SpringBootCondition {

	@Override
	public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //xxx: 可以不配置,但不能乱配置
 		BindResult<CacheType> specified = Binder.get(environment).bind("spring.cache.type", CacheType.class);
			if (!specified.isBound()) {
				/*xxx: 使用默认的 缓存类型返回 */
				return ConditionOutcome.match(message.because("automatic cache type"));
			}       
    }
    
}

# SpringWebMVC自动装配

# 自动装配的条件

  • 项目为web项目,且存在spring-webmvc模块
  • 用户未自定义WebMvcConfigurationSupport配置类

# 自动装配抽象层级

  • web容器层面的自动装配;
  • SpringWeb基础设施层面的自动装配
  • SpringWebMvc应用层面的自动装配

# web容器自动装配

  • 略,依赖于web容器的选型,如果一个容器都没有,则不会装配;

# 基础设施层面自动装配

  • 生效条件是,没有自定义DispatcherServlet
  • 可通过配置项,配置DispatcherServlet的匹配路径,配置项为spring.mvc.servlet.path
  • Dispatcher本身具有一定的应用配置功能,详细可看其initStrategies方法
public class DispatcherServletAutoConfiguration {
 	
    /*xxx: 主要用来将 DispatcherServlet注册到系统中。  本质上是通过 ServletContextInitializer实现的,该接口由 spring-web提供 */
    protected static class DispatcherServletRegistrationConfiguration {
        
        @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
		@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
		public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
				WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
			/*xxx: 通过 ServletRegistrationBean将 dispatcherServlet 注册为 servlet*/
            //xxx: 默认匹配的路径为 / 根路径
			DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
					webMvcProperties.getServlet().getPath());
			/*xxx: 设置名称为 dispatcherServlet*/
			registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
			/*xxx: 设置加载优先级,默认为-1*/
			registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
			multipartConfig.ifAvailable(registration::setMultipartConfig);
			return registration;
		}
    }
    
}

# 应用层面的自动装配(重点)

  • 自定义配置器,且其顺序号为0;
/*xxx: webMvc的自动配置项 */
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
//xxx: 官方的优先级低于用户自定义的优先级
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
public class WebMvcAutoConfiguration {
    
    //xxx: 可通过配置,配置 hiddenMetod过滤器,formContent过滤器
 	@Order(0)
    /*xxx: webMvc的自动配置, 适配器,相当于插件化配置 */
    //xxx: 主要用于装配相应的属性
    public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {

    }   
    
    //xxx: webMvc的实际配置项 该类主要配置了 基于 RequestMapping的mvc套件
	//xxx: 其父类会配置 springMvc的其它机制的套件
    public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
    }
 	   
}

# 视图解析器装配

@Order(0)
/*xxx: webMvc的自动配置, 适配器,相当于插件化配置 */
//xxx: 主要用于装配相应的属性
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
     	   
        @Bean
		@ConditionalOnMissingBean
        //xxx: 装配视图解析器
		public InternalResourceViewResolver defaultViewResolver() {
			InternalResourceViewResolver resolver = new InternalResourceViewResolver();
			resolver.setPrefix(this.mvcProperties.getView().getPrefix());
			resolver.setSuffix(this.mvcProperties.getView().getSuffix());
			return resolver;
		}
        
        @Bean
        //xxx: 如果定义了 view的bean
		@ConditionalOnBean(View.class)
		@ConditionalOnMissingBean
		/*xxx: 通过逻辑视图名称匹配定义好的视图bean对象*/
		public BeanNameViewResolver beanNameViewResolver() {
			BeanNameViewResolver resolver = new BeanNameViewResolver();
			resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
			return resolver;
		}
        
        @Bean
		@ConditionalOnBean(ViewResolver.class)
		@ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
		/*xxx: 它并不直接处理视图,而是委派给其它其它解析器完成*/
		public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
			ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
			resolver.setContentNegotiationManager(beanFactory.getBean(ContentNegotiationManager.class));
			resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
			return resolver;
		}
    
    	@Bean
		@ConditionalOnMissingBean({ RequestContextListener.class, RequestContextFilter.class })
    	//xxx: 装载 请求上下文过滤器
		@ConditionalOnMissingFilterBean(RequestContextFilter.class)
		public static RequestContextFilter requestContextFilter() {
			return new OrderedRequestContextFilter();
		} 
}

# MVC套件装配

  • 该配置类,是WebMvcConfigurationSupport的子类
//xxx: webMvc的实际配置项 该类主要配置了 基于 RequestMapping的mvc套件
//xxx: 其父类会配置 springMvc的其它机制的套件
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
}
  • HandlerMapping套件,它的一些顺序与原生springMVC配置几乎一致,默认的HandlerMapping顺序如下
    • RequestMappingHandlerMapping,其顺序为0
    • viewControllerHandlerMapping,其顺序为1
    • WelcomePageHandlerMapping,其顺序为2(优先级更高)
    • BeanNameUrlHandlerMapping,其顺序为2
    • resourceHandlerMapping,其顺序为Order.LOWEST_PRECEDENCE - 1
    • defaultServletHandlerMapping,其顺序为Order.LOWEST_PRECEDENCE
    • 这些配置是一起的,要么共同遵循,要么全部自定义
//xxx: webMvc的实际配置项 该类主要配置了 基于 RequestMapping的mvc套件
//xxx: 其父类会配置 springMvc的其它机制的套件
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
    
	
		@Bean
		@Primary
		@Override
    	// xxx: requestMapping性质的 handlerMapping
    	// xxx: 其处理顺序为 0
  		public RequestMappingHandlerMapping requestMappingHandlerMapping(
				@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
				@Qualifier("mvcConversionService") FormattingConversionService conversionService,
				@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
			return super.requestMappingHandlerMapping(contentNegotiationManager, conversionService,
					resourceUrlProvider);
		}
    
    @Bean
	@Nullable
	public HandlerMapping viewControllerHandlerMapping(
			@Qualifier("mvcConversionService") FormattingConversionService conversionService,
			@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) 	{
   		//xxx:省略其它抽象...
        ViewControllerRegistry registry = new ViewControllerRegistry(this.applicationContext);
        
        AbstractHandlerMapping handlerMapping = registry.buildHandlerMapping();
        return handlerMapping;
    }
    
    @Bean
	public BeanNameUrlHandlerMapping beanNameHandlerMapping(
			@Qualifier("mvcConversionService") FormattingConversionService conversionService,
			@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
     BeanNameUrlHandlerMapping mapping = new BeanNameUrlHandlerMapping();
		mapping.setOrder(2);
        return mapping;
    }
    
     	@Bean
		public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
				FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
         	//xxx:略, 顺序为2
            WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
					new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),
					this.mvcProperties.getStaticPathPattern());
            return welcomePageHandlerMapping;
        }
    
    @Bean
	@Nullable
	/*xxx: 处理资源的 HandlerMapping,本质上为 SimpleUrlHandlerMapping */
	public HandlerMapping resourceHandlerMapping(
			@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
			@Qualifier("mvcConversionService") FormattingConversionService conversionService,
			@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
     	//xxx: 其顺序位于 : Ordered.LOWEST_PRECEDENCE - 1
        ResourceHandlerRegistry registry = new ResourceHandlerRegistry(this.applicationContext,
				this.servletContext, contentNegotiationManager, pathConfig.getUrlPathHelper());
		addResourceHandlers(registry);
        
        AbstractHandlerMapping handlerMapping = registry.getHandlerMapping();
        return handlerMapping;
    }
    
    @Bean
	@Nullable
	public HandlerMapping defaultServletHandlerMapping() {
		DefaultServletHandlerConfigurer configurer = new DefaultServletHandlerConfigurer(this.servletContext);
		configureDefaultServletHandling(configurer);
        //xxx: 其顺序位于 Ordered.LOWEST_PRECEDENCE
		return configurer.buildHandlerMapping();
	}

}
  • HandlerAdapter套件装配
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
    
    	@Bean
		@Override
		public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
				@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
				@Qualifier("mvcConversionService") FormattingConversionService conversionService,
				@Qualifier("mvcValidator") Validator validator) {
			RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter(contentNegotiationManager,
					conversionService, validator);
			adapter.setIgnoreDefaultModelOnRedirect(
					this.mvcProperties == null || this.mvcProperties.isIgnoreDefaultModelOnRedirect());
			return adapter;
		}
    
    @Bean
	/*xxx: 处理资源的 适配器套件 */
	public HttpRequestHandlerAdapter httpRequestHandlerAdapter() {
		return new HttpRequestHandlerAdapter();
	}
    
    @Bean
	public SimpleControllerHandlerAdapter simpleControllerHandlerAdapter() {
		return new SimpleControllerHandlerAdapter();
	}
}
  • 其它套件装载,略

# 自定义化的启示

  • 需要自定义时,可以实现多个WebMvcConfigurer,但不要自定义WebMvcConfigurationSupport

# SpringSecurity自动装配

# 配置生效条件

  • SpringSecurityCore模块存在;
  • 未自定义WebSecurityConfigurerAdapter

# 源码

//xxx: springSecurityCore模块存在时,自动配置
@ConditionalOnClass(DefaultAuthenticationEventPublisher.class)
@EnableConfigurationProperties(SecurityProperties.class)
@Import({ SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class,
		SecurityDataConfiguration.class })
public class SecurityAutoConfiguration {
    
 	@Bean
	@ConditionalOnMissingBean(AuthenticationEventPublisher.class)
	public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {
		return new DefaultAuthenticationEventPublisher(publisher);
	}
}
/*xxx: 生效条件,未自定义 springSecurity的过滤器链, 即未自定义 WebSecurityConfigurerAdapter */
@ConditionalOnDefaultWebSecurity
@ConditionalOnWebApplication(type = Type.SERVLET)
class SpringBootWebSecurityConfiguration {
 	@Bean
	@Order(SecurityProperties.BASIC_AUTH_ORDER)
	/*xxx: 默认过滤链,顺序点位为 Ordered.LOWEST_PRECEDENCE - 5  */
	/*xxx: 默认的过滤链, 所有的 接口都需要认证才能够访问, 认证的方式以 form形式 */
	SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
		http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic();
		return http.build();
	}   
}
@ConditionalOnMissingBean(name = BeanIds.SPRING_SECURITY_FILTER_CHAIN)
@ConditionalOnClass(EnableWebSecurity.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@EnableWebSecurity
/*xxx: 如果没用 springSecurityFilterChain的bean,则使用 @EnableWebSecurity*/
    /*xxx: springSecurity本身具有默认的过滤链, 它的默认顺序为 100, 且配置了多个特性,
       包括: csrf,异常处理,session管理,securityContext */
    /*xxx: 但是它未对权限作拦截 */
class WebSecurityEnablerConfiguration {
}

# 基础设施装配

@ConditionalOnClass({
		AbstractSecurityWebApplicationInitializer.class,
		SessionCreationPolicy.class })
@AutoConfigureAfter(SecurityAutoConfiguration.class)
//xxx:基础设施配置
/*xxx: 生效条件为  同时存在 spring-security-web模块以及 spring-security-config模块*/
public class SecurityFilterAutoConfiguration {
    @Bean
    @ConditionalOnBean(name = DEFAULT_FILTER_NAME)
    public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(
            SecurityProperties securityProperties) {
        /*xxx: 动态代理过滤链,  本质上,也可以直接通过 FilterRegistrationBean 完成filter的注册*/
        /*xxx: springboot环境下, springSecurity默认过滤链的顺序为 REQUEST_WRAPPER_FILTER_MAX_ORDER -100, 也就是 -100*/
        DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
                DEFAULT_FILTER_NAME);
        registration.setOrder(securityProperties.getFilter().getOrder());
        registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
        return registration;
    }
}

# 注意事项

  • 使用WebSecurityConfiguerAdapter时,该虚拟内部过滤链的默认顺序为100,存在多个过滤链且未自定义顺序时,会报错
  • SpringBoot环境下,默认的过滤链, 所有的 接口都需要认证才能够访问, 认证的方式以 form形式;
  • SpringBoot环境下,springSecurity自动生效;
  • springSecurity在非springBoot环境下,仅仅使用@EnableWebSecurity,并不能拦截接口,因为默认的配置项中,未对接口进行拦截,但配置了csrf等安全功能;
  • springSecurity默认的基础设施过滤链为**-100**
  • 虚拟过滤链与实际的过滤链,构成一条完整的链,而不是虚拟过滤链出栈后再执行实际过滤链!!!
  • 所有定义的bean,如果未指定order装配顺序,则默认按照最低优先级,即Integer.MAX_VALUE处理.其它的bean,也适用该规则。 由OrderComparatorgetOrder方法得到最终的值;

# springSession自动装配

# 配置生效条件

  • 具备spring-session-core依赖
  • 定义了SessionRepositoryFilter时生效

# 源码解析

@ConditionalOnClass(Session.class)
/*xxx: 当前环境下,需要具备 spring-session-core依赖 */
public class SessionAutoConfiguration {
 	
    /*xxx: 基础设施配置 */
	@Import({ ServletSessionRepositoryValidator.class, SessionRepositoryFilterConfiguration.class })
	static class ServletSessionConfiguration {
     	
        @Bean
		@Conditional(DefaultCookieSerializerCondition.class)
		/*xxx: 未自定义 HttpSessionIdResolver,CookieSerializer时生效  */
		DefaultCookieSerializer cookieSerializer(ServerProperties serverProperties,
				ObjectProvider<DefaultCookieSerializerCustomizer> cookieSerializerCustomizers) {
            //xxx: 省略其它抽象...
        }
        
        @Configuration(proxyBeanMethods = false)
		@ConditionalOnClass(RememberMeServices.class)
		/*xxx: 存在springSecurity时,配置记住我 令牌的序列化器 */
		static class RememberMeServicesConfiguration {

			@Bean
			DefaultCookieSerializerCustomizer rememberMeServicesCookieSerializerCustomizer() {
				return (cookieSerializer) -> cookieSerializer
						.setRememberMeRequestAttribute(SpringSessionRememberMeServices.REMEMBER_ME_LOGIN_ATTR);
			}

		}
        
        @Configuration(proxyBeanMethods = false)
		@ConditionalOnMissingBean(SessionRepository.class)
		@Import({ ServletSessionRepositoryImplementationValidator.class,
				ServletSessionConfigurationImportSelector.class })
		/*xxx: 未自定义 sessionRepository时生效, 有多个条件存在时,自动生效,优先级为  redis>mongodb>hazelcast>jdbc */
		/*xxx: 本质上就是用配置值 覆盖相应的 sessionRepository 属性 */
		static class ServletSessionRepositoryConfiguration {
		}
    }
}
/*xxx: spring-session-data-redis模块,spirng-data-redis模块存在时 生效*/
@ConditionalOnMissingBean(SessionRepository.class)
class RedisSessionConfiguration {
 	
    @Configuration(proxyBeanMethods = false)
	public static class SpringBootRedisHttpSessionConfiguration extends RedisHttpSessionConfiguration {
        
     	@Autowired
		/*xxx: 本质上,还是使用的spring-session的配置,但是要注意,
		xxx:	配置项: spring.session.timeout的优先级大于 server.servlet.session.timeout的优先级
		xxx:    如果自定义了 session,则spring.session.timeout  或者 server.servlet.session.timeout能否生效? */
		public void customize(SessionProperties sessionProperties, RedisSessionProperties redisSessionProperties,
				ServerProperties serverProperties) {
			Duration timeout = sessionProperties
					.determineTimeout(() -> serverProperties.getServlet().getSession().getTimeout());
			if (timeout != null) {
				setMaxInactiveIntervalInSeconds((int) timeout.getSeconds());
			}
			setRedisNamespace(redisSessionProperties.getNamespace());
			setFlushMode(redisSessionProperties.getFlushMode());
			setSaveMode(redisSessionProperties.getSaveMode());
			setCleanupCron(redisSessionProperties.getCleanupCron());
		}   
    }
}

# 注意事项

  • SpringSession,本质上,就是利用外观模式,对HttpServletRequest进行了一层包装;
  • 包装的实质,是用spring-session的sessionRepository替换了原生的SessionMapping,原生的sessionMapping是耦合在servlet容器中的;

# springBoot打包依赖优先级

# fatJar优先级

  • Spring Boot的可执行JAR使用了一个特殊的类加载器,LaunchedURLClassLoader,它依赖于classpath.idx文件来定义类路径的顺序。
  • 这个文件列出了所有依赖的JAR,按照优先级排序,确保加载顺序正确。这对于解决依赖冲突很重要,比如当两个库有相同类时,先加载的会被使用。

# war优先级

  • WAR文件通常是部署到Servlet容器(比如Tomcat)中运行的,这时候应用的类加载机制由容器管理。
  • Servlet容器一般有自己的类加载顺序,通常是先加载WEB-INF/classes下的类,然后是WEB-INF/lib下的JAR,按字母顺序加载。这可能会导致依赖加载顺序的问题,因为字母顺序不一定符合项目需要的优先级。

# 解决war依赖优先级的方案(依赖不同,仅用于替换类的情况)

  • 将需要覆盖的类,放到starter目录的源码中;
  • 修改依赖包名的首字母,尽量靠前