美文网首页Java基础
SpringBoot自动装配及启动

SpringBoot自动装配及启动

作者: 王侦 | 来源:发表于2023-01-02 21:17 被阅读0次

1.手写模拟SpringBoot核心流程

手写模拟SpringBoot关键点:

  • 1)创建一个Spring容器,将启动类作为配置类传入;并且加上@ComponentScan默认将启动类所在的包路径作为扫描路径;
  • 2)创建Servlet容器,比如Tomcat,并且配置DispatcherServlet
  • 3)根据依赖启动相应容器,比如引入Tomcat依赖就启动Tomcat容器,引入Jetty依赖就启动Jetty容器。实现自动装配。
    3-1)改为面向接口编程,先获取getWebServer(),然后调用webServer.start()启动;、
    3-2)自动配置类WebServerAutoConfiguration,条件注解@Conditional加在TomcatWebServer和JettyWebServer的Bean上面,判断条件注解上的类是否存在,不存在则不加载相应的bean;
    3-3)自己工程要导入自动配置类@Import(WebServerAutoConfiguration.class);
    3-4)如果有很多自动配置类,每一个自动配置类都要Import,不仅需要改代码,并且很麻烦。此时可以通过Java SPI(META-INF/services)或者Spring SPI(META-INF/spring.factories)机制从classpath下面工程及所有jar包的固定目录进行加载。方便第三方软件整合SpringBoot,提供对应的自动配置类。
    3-5)自动装配注解中的Import需要实现DeferredImprotSelector,这个的执行时机是在每一批次的配置类解析完之后才解析DeferredImprotSelector,此时DeferredImprotSelector才会通过SPI机制加载所有的自动配置类。是为了让程序员定义Bean优先级更好,自动配置类的Bean的条件注解会根据程序员是否定义了某个bean来决定当前bean是否注册到Spring容器。
    3-6)引入依赖时,是按照starter进行引入,starter会将某个组件需要的相关依赖一起进行引入。

自动配置类对于没有引入依赖的那些条件注解里面的class是怎么处理的,为什么不报错?

  • 比如我们依赖的springboot-web.jar(这里面引入了springboot-tomcat.jar),这些里面的类都是class格式,都是编译好的
  • 编译不报错:我们自己工程编译时,是不会报错的,只需要编译我们自己写的代码,依赖的jar包都是已经编译好的,不用编译
  • 运行不报错(跳过类加载机制获取注解上类的字符串信息,后面再通过类加载机制进行加载,如果抛异常则catch住并返回false):ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry()中在(ConfigurationClassParser#processConfigurationClass中的this.conditionEvaluator.shouldSkip())处理配置类条件注解时,使用的是ASM技术进行处理。不是使用反射,使用反射会加载类,就会报错。ASM技术处理的是字节码文件,拿到字符串(类的全名),再用类加载器去加载(OnClassCondition#getMatchOutcom),如果能加载则返回true,不能加载(抛异常)则返回false。

2.SpringBoot创建Servlet容器的地方

2.1 创建WebServer

AbstractApplicationContext#refresh
调用ServletWebServerApplicationContext#onRefresh

    protected void onRefresh() {
        super.onRefresh();
        try {
            createWebServer();
        }
        catch (Throwable ex) {
            throw new ApplicationContextException("Unable to start web server", ex);
        }
    }

ServletWebServerApplicationContext#createWebServer

    private void createWebServer() {
        WebServer webServer = this.webServer;
        ServletContext servletContext = getServletContext();
        if (webServer == null && servletContext == null) {
            StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
            ServletWebServerFactory factory = getWebServerFactory();
            createWebServer.tag("factory", factory.getClass().toString());
            this.webServer = factory.getWebServer(getSelfInitializer());
            createWebServer.end();
            getBeanFactory().registerSingleton("webServerGracefulShutdown",
                    new WebServerGracefulShutdownLifecycle(this.webServer));
            getBeanFactory().registerSingleton("webServerStartStop",
                    new WebServerStartStopLifecycle(this, this.webServer));
        }
        else if (servletContext != null) {
            try {
                getSelfInitializer().onStartup(servletContext);
            }
            catch (ServletException ex) {
                throw new ApplicationContextException("Cannot initialize servlet context", ex);
            }
        }
        initPropertySources();
    }

来看看ServletWebServerApplicationContext#getWebServerFactory,这里表示Spring容器里面只能有一个ServletWebServerFactory类型的BD。

    protected ServletWebServerFactory getWebServerFactory() {
        // Use bean names so that we don't consider the hierarchy
        String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
        if (beanNames.length == 0) {
            throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "
                    + "ServletWebServerFactory bean.");
        }
        if (beanNames.length > 1) {
            throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "
                    + "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
        }
        return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
    }

2.2 ServletWebServerFactoryAutoConfiguration自动配置类

现在来着重看一下ServletWebServerFactory相关的自动配置类ServletWebServerFactoryAutoConfiguration:

@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
        ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
        ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
        ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {

    @Bean
    public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties,
            ObjectProvider<WebListenerRegistrar> webListenerRegistrars) {
        return new ServletWebServerFactoryCustomizer(serverProperties,
                webListenerRegistrars.orderedStream().collect(Collectors.toList()));
    }

    @Bean
    @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
    public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
            ServerProperties serverProperties) {
        return new TomcatServletWebServerFactoryCustomizer(serverProperties);
    }

    @Bean
    @ConditionalOnMissingFilterBean(ForwardedHeaderFilter.class)
    @ConditionalOnProperty(value = "server.forward-headers-strategy", havingValue = "framework")
    public FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter() {
        ForwardedHeaderFilter filter = new ForwardedHeaderFilter();
        FilterRegistrationBean<ForwardedHeaderFilter> registration = new FilterRegistrationBean<>(filter);
        registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR);
        registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return registration;
    }

    /**
     * Registers a {@link WebServerFactoryCustomizerBeanPostProcessor}. Registered via
     * {@link ImportBeanDefinitionRegistrar} for early registration.
     */
    public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {

        private ConfigurableListableBeanFactory beanFactory;

        @Override
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            if (beanFactory instanceof ConfigurableListableBeanFactory) {
                this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
            }
        }

        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
                BeanDefinitionRegistry registry) {
            if (this.beanFactory == null) {
                return;
            }
            registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
                    WebServerFactoryCustomizerBeanPostProcessor.class,
                    WebServerFactoryCustomizerBeanPostProcessor::new);
            registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",
                    ErrorPageRegistrarBeanPostProcessor.class, ErrorPageRegistrarBeanPostProcessor::new);
        }

        private <T> void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name,
                Class<T> beanClass, Supplier<T> instanceSupplier) {
            if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
                RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass, instanceSupplier);
                beanDefinition.setSynthetic(true);
                registry.registerBeanDefinition(name, beanDefinition);
            }
        }

    }

}

Import引入的EmbeddedTomcat、EmbeddedJetty和EmbeddedUndertow就是创建ServletWebServerFactory的配置类,比如EmbeddedTomcat,只有存在Tomcat依赖,并且Spring容器里面没有ServletWebServerFactory时才会创建TomcatServletWebServerFactory :

@Configuration(proxyBeanMethods = false)
class ServletWebServerFactoryConfiguration {

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
    @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
    static class EmbeddedTomcat {

        @Bean
        TomcatServletWebServerFactory tomcatServletWebServerFactory(
                ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
                ObjectProvider<TomcatContextCustomizer> contextCustomizers,
                ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
            TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
            factory.getTomcatConnectorCustomizers()
                    .addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
            factory.getTomcatContextCustomizers()
                    .addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
            factory.getTomcatProtocolHandlerCustomizers()
                    .addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
            return factory;
        }

    }

2.3 TomcatConnectorCustomizer对connector进行配置

@Bean方法的入参会进行依赖查找,Spring会去容器里面查找并注入,ObjectProvider表示可以为空。TomcatConnectorCustomizer可以对Tomcat的connector进行配置。这个在哪里使用?

详情请看TomcatServletWebServerFactory#getWebServer,其中customizeConnector就会调用之前设置到factory里面的TomcatConnectorCustomizer对connector进行配置:

    public WebServer getWebServer(ServletContextInitializer... initializers) {
        if (this.disableMBeanRegistry) {
            Registry.disableRegistry();
        }
        Tomcat tomcat = new Tomcat();
        File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
        tomcat.setBaseDir(baseDir.getAbsolutePath());
        Connector connector = new Connector(this.protocol);
        connector.setThrowOnFailure(true);
        tomcat.getService().addConnector(connector);
        customizeConnector(connector);
        tomcat.setConnector(connector);
        tomcat.getHost().setAutoDeploy(false);
        configureEngine(tomcat.getEngine());
        for (Connector additionalConnector : this.additionalTomcatConnectors) {
            tomcat.getService().addConnector(additionalConnector);
        }
        prepareContext(tomcat.getHost(), initializers);
        return getTomcatWebServer(tomcat);
    }


    protected void customizeConnector(Connector connector) {
        int port = Math.max(getPort(), 0);
        connector.setPort(port);
        if (StringUtils.hasText(getServerHeader())) {
            connector.setProperty("server", getServerHeader());
        }
        if (connector.getProtocolHandler() instanceof AbstractProtocol) {
            customizeProtocol((AbstractProtocol<?>) connector.getProtocolHandler());
        }
        invokeProtocolHandlerCustomizers(connector.getProtocolHandler());
        if (getUriEncoding() != null) {
            connector.setURIEncoding(getUriEncoding().name());
        }
        // Don't bind to the socket prematurely if ApplicationContext is slow to start
        connector.setProperty("bindOnInit", "false");
        if (getSsl() != null && getSsl().isEnabled()) {
            customizeSsl(connector);
        }
        TomcatConnectorCustomizer compression = new CompressionConnectorCustomizer(getCompression());
        compression.customize(connector);
        for (TomcatConnectorCustomizer customizer : this.tomcatConnectorCustomizers) {
            customizer.customize(connector);
        }
    }

2.4 application.properties中的属性是怎么处理的?

另一种配置属性的方法(比如配置server.port=8084),是在属性配置文件比如application.properties里面进行配置。那这个是怎么获取到配置文件中的属性?

这就涉及到ServletWebServerFactoryAutoConfiguration 自动配置Import导入的另外一个类ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class。BeanPostProcessorsRegistrar是个ImportBeanDefinitionRegistrar,其registerBeanDefinitions()方法注册了WebServerFactoryCustomizerBeanPostProcessor。

WebServerFactoryCustomizerBeanPostProcessor是个BeanPostProcessor,它主要重写了postProcessBeforeInitialization()方法,也就是在初始化Bean之前调用的。

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof WebServerFactory) {
            postProcessBeforeInitialization((WebServerFactory) bean);
        }
        return bean;
    }

    private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
        LambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory)
                .withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
                .invoke((customizer) -> customizer.customize(webServerFactory));
    }

该BeanPostProcessor主要处理的是WebServerFactory的初始化,比如TomcatServletWebServerFactory。

这里主要调用的WebServerFactoryCustomizer对WebServerFactory进行定制,WebServerFactoryCustomizer在哪个地方定义了?就在ServletWebServerFactoryAutoConfiguration自动配置类里面:

    @Bean
    public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties,
            ObjectProvider<WebListenerRegistrar> webListenerRegistrars) {
        return new ServletWebServerFactoryCustomizer(serverProperties,
                webListenerRegistrars.orderedStream().collect(Collectors.toList()));
    }

    @Bean
    @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
    public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
            ServerProperties serverProperties) {
        return new TomcatServletWebServerFactoryCustomizer(serverProperties);
    }

来看一下ServletWebServerFactoryCustomizer#customize,这里面就会从配置文件获取server.port设置到factory对于的port属性里面去。

public class ServletWebServerFactoryCustomizer
        implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {

    private final ServerProperties serverProperties;


    public void customize(ConfigurableServletWebServerFactory factory) {
        PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
        map.from(this.serverProperties::getPort).to(factory::setPort);
        map.from(this.serverProperties::getAddress).to(factory::setAddress);
        map.from(this.serverProperties.getServlet()::getContextPath).to(factory::setContextPath);
        map.from(this.serverProperties.getServlet()::getApplicationDisplayName).to(factory::setDisplayName);
        map.from(this.serverProperties.getServlet()::isRegisterDefaultServlet).to(factory::setRegisterDefaultServlet);
        map.from(this.serverProperties.getServlet()::getSession).to(factory::setSession);
        map.from(this.serverProperties::getSsl).to(factory::setSsl);
        map.from(this.serverProperties.getServlet()::getJsp).to(factory::setJsp);
        map.from(this.serverProperties::getCompression).to(factory::setCompression);
        map.from(this.serverProperties::getHttp2).to(factory::setHttp2);
        map.from(this.serverProperties::getServerHeader).to(factory::setServerHeader);
        map.from(this.serverProperties.getServlet()::getContextParameters).to(factory::setInitParameters);
        map.from(this.serverProperties.getShutdown()).to(factory::setShutdown);
        for (WebListenerRegistrar registrar : this.webListenerRegistrars) {
            registrar.register(factory);
        }
    }
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {

    /**
     * Server HTTP port.
     */
    private Integer port;

3.条件注解

@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
        ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
        ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
        ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {

来看看该自动配置类上面的两个条件注解:

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnWebApplicationCondition.class)
public @interface ConditionalOnWebApplication {

重点看SpringBootCondition#matches()

    public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        String classOrMethodName = getClassOrMethodName(metadata);
        try {
            ConditionOutcome outcome = getMatchOutcome(context, metadata);
            logOutcome(classOrMethodName, outcome);
            recordEvaluation(context, classOrMethodName, outcome);
            return outcome.isMatch();
        }
        catch (NoClassDefFoundError ex) {
            throw new IllegalStateException("Could not evaluate condition on " + classOrMethodName + " due to "
                    + ex.getMessage() + " not found. Make sure your own configuration does not rely on "
                    + "that class. This can also happen if you are "
                    + "@ComponentScanning a springframework package (e.g. if you "
                    + "put a @ComponentScan in the default package by mistake)", ex);
        }
        catch (RuntimeException ex) {
            throw new IllegalStateException("Error processing condition on " + getName(metadata), ex);
        }
    }

该方法执行步骤:

  • 1)获取条件注解写在哪个类上或者哪个方法上
  • 2)获取条件的判断结果getMatchOutcome(),结果放在ConditionOutcome
  • 3)两种日志打印,一种是即时打印,一种是容器启动完成后打印(通过ConditionEvaluationReportLoggingListener监听器监听ContextRefreshedEvent事件进行统一打印)

3.1 ConditionalOnClass、ConditionalOnMissingClass

现在来看子类的实现OnClassCondition#getMatchOutcome:

    public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
        ClassLoader classLoader = context.getClassLoader();
        ConditionMessage matchMessage = ConditionMessage.empty();
        List<String> onClasses = getCandidates(metadata, ConditionalOnClass.class);
        if (onClasses != null) {
            List<String> missing = filter(onClasses, ClassNameFilter.MISSING, classLoader);
            if (!missing.isEmpty()) {
                return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class)
                        .didNotFind("required class", "required classes").items(Style.QUOTE, missing));
            }
            matchMessage = matchMessage.andCondition(ConditionalOnClass.class)
                    .found("required class", "required classes")
                    .items(Style.QUOTE, filter(onClasses, ClassNameFilter.PRESENT, classLoader));
        }
        List<String> onMissingClasses = getCandidates(metadata, ConditionalOnMissingClass.class);
        if (onMissingClasses != null) {
            List<String> present = filter(onMissingClasses, ClassNameFilter.PRESENT, classLoader);
            if (!present.isEmpty()) {
                return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnMissingClass.class)
                        .found("unwanted class", "unwanted classes").items(Style.QUOTE, present));
            }
            matchMessage = matchMessage.andCondition(ConditionalOnMissingClass.class)
                    .didNotFind("unwanted class", "unwanted classes")
                    .items(Style.QUOTE, filter(onMissingClasses, ClassNameFilter.MISSING, classLoader));
        }
        return ConditionOutcome.match(matchMessage);
    }

该方法执行步骤(ConditionalOnClass、ConditionalOnMissingClass都是走@Conditional(OnClassCondition.class),都是在OnClassCondition中进行统一处理):

  • 1)getCandidates()拿到ConditionOnClass注解中的value值,也就是要判断是否存在的类名
  • 2)filter()将onClasses中不存在的类找出来,ClassNameFilter.MISSING就是通过类加载器去加载这些类,看这些类是否存在,如果不存在则记录下这些类
  • 3)这里会同时处理ConditionalOnMissingClass,就是看该自动配置类上面有没有该注解,如果有则拿到该注解的value值,也就是要判断是否存在的类名
  • 4)filter()将onMissingClasses中存在的类找出来,ClassNameFilter.PRESENT就是通过类加载器去加载这些类,看这些类是否存在,如果存在则记录下这些类

具体的filter方法是FilteringSpringBootCondition#filter:

    protected final List<String> filter(Collection<String> classNames, ClassNameFilter classNameFilter,
            ClassLoader classLoader) {
        if (CollectionUtils.isEmpty(classNames)) {
            return Collections.emptyList();
        }
        List<String> matches = new ArrayList<>(classNames.size());
        for (String candidate : classNames) {
            if (classNameFilter.matches(candidate, classLoader)) {
                matches.add(candidate);
            }
        }
        return matches;
    }

传入的classNameFilter是ClassNameFilter.MISSING:

        MISSING {

            @Override
            public boolean matches(String className, ClassLoader classLoader) {
                return !isPresent(className, classLoader);
            }

        };


        static boolean isPresent(String className, ClassLoader classLoader) {
            if (classLoader == null) {
                classLoader = ClassUtils.getDefaultClassLoader();
            }
            try {
                resolve(className, classLoader);
                return true;
            }
            catch (Throwable ex) {
                return false;
            }
        }

3.2 ConditionalOnBean、ConditionalOnSingleCandidate和ConditionalOnMissingBean

这三个条件注解都走的是@Conditional(OnBeanCondition.class),也即OnBeanCondition。核心在OnBeanCondition#getMatchOutcome方法中。

3.3 ConditionalOnJava

@Conditional(OnJavaCondition.class)
public @interface ConditionalOnJava {

OnJavaCondition#getMatchOutcome去判断JDK版本时,是通过下面的特征类的特征方法进行判定的,就是看存不存在,判定时是逆序的(从高版本开始判定)。

public enum JavaVersion {

    /**
     * Java 1.8.
     */
    EIGHT("1.8", Optional.class, "empty"),

    /**
     * Java 9.
     */
    NINE("9", Optional.class, "stream"),

    /**
     * Java 10.
     */
    TEN("10", Optional.class, "orElseThrow"),

    /**
     * Java 11.
     */
    ELEVEN("11", String.class, "strip"),

    /**
     * Java 12.
     */
    TWELVE("12", String.class, "describeConstable"),

    /**
     * Java 13.
     */
    THIRTEEN("13", String.class, "stripIndent"),

    /**
     * Java 14.
     */
    FOURTEEN("14", MethodHandles.Lookup.class, "hasFullPrivilegeAccess"),

    /**
     * Java 15.
     */
    FIFTEEN("15", CharSequence.class, "isEmpty"),

    /**
     * Java 16.
     */
    SIXTEEN("16", Stream.class, "toList");

3.4 ConditionalOnWebApplication和ConditionalOnNotWebApplication

这两个都是基于OnWebApplicationCondition

3.5 ConditionalOnProperty

@Conditional(OnPropertyCondition.class)
public @interface ConditionalOnProperty {

核心就是看Environment对象里面有没有配置该属性。

OnPropertyCondition#getMatchOutcome

    public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
        List<AnnotationAttributes> allAnnotationAttributes = annotationAttributesFromMultiValueMap(
                metadata.getAllAnnotationAttributes(ConditionalOnProperty.class.getName()));
        List<ConditionMessage> noMatch = new ArrayList<>();
        List<ConditionMessage> match = new ArrayList<>();
        for (AnnotationAttributes annotationAttributes : allAnnotationAttributes) {
            ConditionOutcome outcome = determineOutcome(annotationAttributes, context.getEnvironment());
            (outcome.isMatch() ? match : noMatch).add(outcome.getConditionMessage());
        }
        if (!noMatch.isEmpty()) {
            return ConditionOutcome.noMatch(ConditionMessage.of(noMatch));
        }
        return ConditionOutcome.match(ConditionMessage.of(match));
    }

context.getEnvironment()就是获取Environment对象,Environment实现了PropertyResolver接口。

4.@SpringBootApplication

4.1 SpringBoot自动装配原理

  • 1.核心是@SpringBootApplication,包含三个注解:@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan;
  • 2.@EnableAutoConfiguration表明启动类是个配置类,@ComponentScan默认情况下指定启动类所在的包路径为扫描路径;
  • 3.重点是@EnableAutoConfiguration,它导入了一个类@Import(AutoConfigurationImportSelector.class),AutoConfigurationImportSelector是个DeferredImportSelector。
    3-1)ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry()会在每一批次的配置类解析完才处理DeferredImportSelector,这样可以让程序员自定义的Bean优先级更高;
    3-2)AutoConfigurationImportSelector.AutoConfigurationGroup#process会通过Spring SPI机制读取类路径和jar包中META-INF/spring.factories文件中的自动配置类(key为org.springframework.boot.autoconfigure.EnableAutoConfiguration),这里会根据三个Conditional注解进行初步筛选
    3-3)完整的条件注解筛选会在ConfigurationClassParser#processConfigurationClass中的this.conditionEvaluator.shouldSkip()方法里面处理。这里就会根据Starter引入依赖、程序员是否定义某个Bean等各种条件注解判定某个Bean是否需要注册到Spring容器。

4.2 AutoConfigurationImportSelector源码剖析

AutoConfigurationImportSelector.AutoConfigurationGroup#process

        public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
            Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
                    () -> String.format("Only %s implementations are supported, got %s",
                            AutoConfigurationImportSelector.class.getSimpleName(),
                            deferredImportSelector.getClass().getName()));
            AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
                    .getAutoConfigurationEntry(annotationMetadata);
            this.autoConfigurationEntries.add(autoConfigurationEntry);
            for (String importClassName : autoConfigurationEntry.getConfigurations()) {
                this.entries.putIfAbsent(importClassName, annotationMetadata);
            }
        }
    protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        }
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
        configurations = removeDuplicates(configurations);
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        configurations = getConfigurationClassFilter().filter(configurations);
        fireAutoConfigurationImportEvents(configurations, exclusions);
        return new AutoConfigurationEntry(configurations, exclusions);
    }

该方法执行步骤:

  • 1)getAttributes()获取@EnableAutoConfiguration的属性(exclude、excludeName)
  • 2)getCandidateConfigurations()通过Spring SPI机制获取META-INF/spring.factories中所有的AutoConfiguration自动配置类
  • 3)removeDuplicates()去重
  • 4)获取需要排除的自动配置类,@EnableAutoConfiguration的属性exclude、excludeName或者通过配置属性spring.autoconfigure.exclude进行排除
  • 5)filter()对自动配置类进行初步过滤,使用三个条件过滤器OnBeanCondition、OnClassCondition、OnWebApplciationCondition(包含的注解:ConditionOnClass、ConditionOnBean、ConditionOnSingleCandidate、ConditionalOnWebApplication),spring-boot-autoconfigure jar包里面有一个文件spring-autoconfigure-metadata.properties(编译生成的)。这里是直接读取该文件,然后根据该文件里面对各个类的条件注解(特别注意:这里的条件注解是不全的)进行过滤。
    protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
        return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
    }
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

来追踪一下filter()方法:

        List<String> filter(List<String> configurations) {
            long startTime = System.nanoTime();
            String[] candidates = StringUtils.toStringArray(configurations);
            boolean skipped = false;
            for (AutoConfigurationImportFilter filter : this.filters) {
                boolean[] match = filter.match(candidates, this.autoConfigurationMetadata);
                for (int i = 0; i < match.length; i++) {
                    if (!match[i]) {
                        candidates[i] = null;
                        skipped = true;
                    }
                }
            }
            if (!skipped) {
                return configurations;
            }
            List<String> result = new ArrayList<>(candidates.length);
            for (String candidate : candidates) {
                if (candidate != null) {
                    result.add(candidate);
                }
            }
            if (logger.isTraceEnabled()) {
                int numberFiltered = configurations.size() - result.size();
                logger.trace("Filtered " + numberFiltered + " auto configuration class in "
                        + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
            }
            return result;
        }

FilteringSpringBootCondition#match

    public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {
        ConditionEvaluationReport report = ConditionEvaluationReport.find(this.beanFactory);
        ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses, autoConfigurationMetadata);
        boolean[] match = new boolean[outcomes.length];
        for (int i = 0; i < outcomes.length; i++) {
            match[i] = (outcomes[i] == null || outcomes[i].isMatch());
            if (!match[i] && outcomes[i] != null) {
                logOutcome(autoConfigurationClasses[i], outcomes[i]);
                if (report != null) {
                    report.recordConditionEvaluation(autoConfigurationClasses[i], this, outcomes[i]);
                }
            }
        }
        return match;
    }

这里getOutcomes()就只在上面三个filter里面重写了,这里过滤方法就是根据条件注解进行过滤,没有本质区别,只不过这里使用的条件注解只是局限于某几个(ConditionOnClass、ConditionOnBean、ConditionOnSingleCandidate、ConditionalOnWebApplication):


4.3 @AutoConfigurationPackage注解

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

会执行ImportBeanDefinitionRegistrar#registerBeanDefinitions

    static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

        @Override
        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
        }
        PackageImports(AnnotationMetadata metadata) {
            AnnotationAttributes attributes = AnnotationAttributes
                    .fromMap(metadata.getAnnotationAttributes(AutoConfigurationPackage.class.getName(), false));
            List<String> packageNames = new ArrayList<>(Arrays.asList(attributes.getStringArray("basePackages")));
            for (Class<?> basePackageClass : attributes.getClassArray("basePackageClasses")) {
                packageNames.add(basePackageClass.getPackage().getName());
            }
            if (packageNames.isEmpty()) {
                packageNames.add(ClassUtils.getPackageName(metadata.getClassName()));
            }
            this.packageNames = Collections.unmodifiableList(packageNames);
        }

AutoConfigurationPackage该注解单独使用时,可以指定包路径basePackages。在这里是没有值的。

然后就会获取启动类所在的包路径。

最后会将该包路径封装成BasePackagesBeanDefinition,名字为AutoConfigurationPackages.class.getName(),注册到Spring容器。

那这个路径谁用呢?Mybatis可以用,让Mybatis少一些配置,直接使用这个包路径就可以。Mybatis的扫描器就会去获取该包路径的Bean:

    public static List<String> get(BeanFactory beanFactory) {
        try {
            return beanFactory.getBean(BEAN, BasePackages.class).get();
        }
        catch (NoSuchBeanDefinitionException ex) {
            throw new IllegalStateException("Unable to retrieve @EnableAutoConfiguration base packages");
        }
    }

4.4 @ComponentScan里面的两个exclueFilter

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoaderAware {

    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
            throws IOException {
        return isConfiguration(metadataReader) && isAutoConfiguration(metadataReader);
    }

    private boolean isConfiguration(MetadataReader metadataReader) {
        return metadataReader.getAnnotationMetadata().isAnnotated(Configuration.class.getName());
    }

    private boolean isAutoConfiguration(MetadataReader metadataReader) {
        return getAutoConfigurations().contains(metadataReader.getClassMetadata().getClassName());
    }

AutoConfigurationExcludeFilter会过滤掉是自动配置类的配置类,首先判断是否有@Configuration注解,然后判断自动配置类(通过SPI机制加载的key为EnableAutoConfiguration)是否包含该配置类。

为什么要这么做?
因为自动配置类要在每一轮配置类扫描完成后才进行处理,不需要在扫描的时候就处理。

public class TypeExcludeFilter implements TypeFilter, BeanFactoryAware {

    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
            throws IOException {
        if (this.beanFactory instanceof ListableBeanFactory && getClass() == TypeExcludeFilter.class) {
            for (TypeExcludeFilter delegate : getDelegates()) {
                if (delegate.match(metadataReader, metadataReaderFactory)) {
                    return true;
                }
            }
        }
        return false;
    }

    private Collection<TypeExcludeFilter> getDelegates() {
        Collection<TypeExcludeFilter> delegates = this.delegates;
        if (delegates == null) {
            delegates = ((ListableBeanFactory) this.beanFactory).getBeansOfType(TypeExcludeFilter.class).values();
            this.delegates = delegates;
        }
        return delegates;
    }

会从Spring容器中获取所有TypeExcludeFilter,然后进行匹配,匹配成功的则不进行解析。

特别注意:

  • 1)如果这里先定义一个TypeExcludeFilter作为@Bean注册到Spring容器,并过滤某个@Component,是不会生效的
  • 2)为什么,跟顺序有关,因为@Component、@ComponentScan会先进行处理,@Bean后进行处理,@ComponentScan进行扫描时,@Bean注解TypeExcludeFilter在容器里面还不存在,所以不起作用。
  • 3)@Component注解TypeExcludeFilter也不一定生效,也有个顺序问题

那怎么解决这个问题?

  • 关键就是要将TypeExcludeFilter在扫描之前就加入到Spring容器
  • 可以定义ApplicationContextInitializer,并配置到spring.factories文件里面
  • 然后在ApplicationContextInitializer#initialize中applicationContext.getBeanFactory().registerSingleton()向Spring容器注册TypeExcludeFilter
  • 当然也可以用BeanFactoryRegistryPostProcessor,并且是要程序手动向Spring容器添加的,这样就会在ConfigurationClassPostProcessor之前执行,但是这样做不太规范,没法实现。

5.starter包

Starter 其实就是一个 jar 包,在 pom 中引入一个 starter 其实就是引入了一个 jar 包。而且很多时候,这个 starter 对应的 jar 包是个空的,里面并没有任何类和接口。

那这个空的 jar 包有何意义呢?它的意义就在于为了引入有意义的其它 jar 包,因为这些 jar 包都是基于 Maven 的,因此 jar 包的 pom 文件中包含了其它依赖。而且SpringBoot会把常用的依赖及其适合的版本号都通过依赖管理的方式包含在 pom 文件中。

一般SpringBoot已经整合的组件,也就是SpringBoot提供的starter包里面只会有相关依赖(自动配置类已经写好了);而对于第三方组件想要整合SpringBoot,还需要加入自动配置的jar包(比如mybatis-spring-boot-starter里面还包括mybatis-spring-boot-autoconfigure的jar包)。

6.SpringBoot启动过程

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

6.1 构造SpringApplication对象

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");

        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));

        // 1. 推测web应用类型(NONE、REACTIVE、SERVLET)
        this.webApplicationType = WebApplicationType.deduceFromClasspath();

        // 2. 从spring.factories中获取BootstrapRegistryInitializer对象
        this.bootstrapRegistryInitializers = new ArrayList<>(
                getSpringFactoriesInstances(BootstrapRegistryInitializer.class));

        // 3. 从spring.factories中获取ApplicationContextInitializer对象
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

        // 4. 从spring.factories中获取ApplicationListener对象
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

        // 5. 推测出Main类(main()方法所在的类)
        this.mainApplicationClass = deduceMainApplicationClass();
    }

xxx\spring-boot-2.6.6-master\spring-boot-project\spring-boot\src\main\resources\META-INF\spring.factories

# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener

6.2 核心启动流程在run()方法里面

    public ConfigurableApplicationContext run(String... args) {
        long startTime = System.nanoTime();

        // 1、创建引导启动器,类似一个ApplicationContext,可以往里面添加一些对象
        DefaultBootstrapContext bootstrapContext = createBootstrapContext();

        ConfigurableApplicationContext context = null;
        configureHeadlessProperty();

        // 2、从spring.factories中获取SpringApplicationRunListener对象
        // 默认会拿到一个EventPublishingRunListener,它会启动过程的各个阶段发布对应的ApplicationEvent事件
        SpringApplicationRunListeners listeners = getRunListeners(args);

        // 3、发布ApplicationStartingEvent
        listeners.starting(bootstrapContext, this.mainApplicationClass);
        try {

            // 4、将run()的参数封装为DefaultApplicationArguments对象
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

            // 5、准备Environment
            // 包括操作系统,JVM、ServletContext、properties、yaml等等配置
            // 会发布一个ApplicationEnvironmentPreparedEvent
            ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);

            // 默认spring.beaninfo.ignore=true,表示不需要jdk缓存beanInfo信息,Spring自己会缓存
            configureIgnoreBeanInfo(environment);

            Banner printedBanner = printBanner(environment);

            // 6、根据应用类型创建Spring容器
            context = createApplicationContext();
            context.setApplicationStartup(this.applicationStartup);

            // 7、利用ApplicationContextInitializer初始化Spring容器
            // 8、发布ApplicationContextInitializedEvent
            // 9、关闭DefaultBootstrapContext
            // 10、注册primarySources类,就是run方法存入进来的配置类
            // 11、发布ApplicationPreparedEvent事件
            prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);

            // 12、刷新Spring容器,会解析配置类、扫描、启动WebServer
            refreshContext(context);

            // 空方法
            afterRefresh(context, applicationArguments);

            // 启动时间
            Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
            }

            // 13、发布ApplicationStartedEvent事件,表示Spring容器已经启动
            listeners.started(context, timeTakenToStartup);

            // 14、从Spring容器中获取ApplicationRunner和CommandLineRunner,并执行其run()
            callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
            // 15、发布ApplicationFailedEvent事件
            handleRunFailure(context, ex, listeners);
            throw new IllegalStateException(ex);
        }

        try {
            Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);

            // 16、发布ApplicationReadyEvent事件,表示Spring容器已经准备好了
            listeners.ready(context, timeTakenToReady);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }

6.2.1 createBootstrapContext()

    private DefaultBootstrapContext createBootstrapContext() {
        DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
        // 利用bootstrapRegistryInitializers初始化DefaultBootstrapContext
        this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext));
        return bootstrapContext;
    }

6.2.2 SpringApplicationRunListener

SpringApplicationRunListeners#starting

    void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
        doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),
                (step) -> {
                    if (mainApplicationClass != null) {
                        step.tag("mainApplicationClass", mainApplicationClass.getName());
                    }
                });
    }
    private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction,
            Consumer<StartupStep> stepAction) {
        StartupStep step = this.applicationStartup.start(stepName);
        this.listeners.forEach(listenerAction);
        if (stepAction != null) {
            stepAction.accept(step);
        }
        step.end();
    }

其中listeners只有一个,就是EventPublishingRunListener

class SpringApplicationRunListeners {

    private final List<SpringApplicationRunListener> listeners;
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
    public void starting(ConfigurableBootstrapContext bootstrapContext) {
        this.initialMulticaster
                .multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));
    }

EventPublishingRunListener#starting就是发布事件,然后调用ApplicationListener进行处理。这里可以使用SimpleApplicationEventMulticaster的taskExecutor线程池进行异步调用监听器进行处理(如果有的话)。

6.3 prepareEnvironment()

    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
            DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
        // Create and configure the environment
        // 创建ApplicationServletEnvironment,里面添加了四个PropertySource
        // 1. StubPropertySource {name='servletConfigInitParams'}
        // 2. StubPropertySource {name='servletContextInitParams'}
        // 3. PropertiesPropertySource {name='systemProperties'}
        // 4. SystemEnvironmentPropertySource {name='systemEnvironment'}
        ConfigurableEnvironment environment = getOrCreateEnvironment();

        // 添加SimpleCommandLinePropertySource {name='commandLineArgs'},放在首位
        configureEnvironment(environment, applicationArguments.getSourceArgs());

        // 把所有的PropertySources封装为一个ConfigurationPropertySourcesPropertySource
        // 然后添加到environment中,放在首位
        ConfigurationPropertySources.attach(environment);

        // 发布ApplicationEnvironmentPreparedEvent事件,表示环境已经准备好了
        // 默认EnvironmentPostProcessorApplicationListener会处理这个事件,会从spring.factories中拿出EnvironmentPostProcessor进一步处理Environment
        listeners.environmentPrepared(bootstrapContext, environment);

        // 最后,把defaultProperties移到最后
        DefaultPropertiesPropertySource.moveToEnd(environment);
        Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
                "Environment prefix cannot be set via properties.");
        bindToSpringApplication(environment);
        if (!this.isCustomEnvironment) {
            environment = convertEnvironment(environment);
        }
        ConfigurationPropertySources.attach(environment);
        return environment;
    }

6.4 createApplicationContext()

ApplicationContextFactory中的DEFAULT里面进行创建:

    ApplicationContextFactory DEFAULT = (webApplicationType) -> {
        try {
            switch (webApplicationType) {
            case SERVLET:
                return new AnnotationConfigServletWebServerApplicationContext();
            case REACTIVE:
                return new AnnotationConfigReactiveWebServerApplicationContext();
            default:
                return new AnnotationConfigApplicationContext();
            }
        }
        catch (Exception ex) {
            throw new IllegalStateException("Unable create a default ApplicationContext instance, "
                    + "you may need a custom ApplicationContextFactory", ex);
        }
    };

6.5 prepareContext()

主要是做几件事:

  • 1)利用ApplicationContextInitializer初始化Spring容器
  • 2)发布ApplicationContextInitializedEvent
  • 3)关闭DefaultBootstrapContext
  • 4)注册primarySources类,就是run方法存入进来的配置类
  • 5)发布ApplicationPreparedEvent事件
    private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
            ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments, Banner printedBanner) {

        // 将前面生成的Environment设置到Spring容器中
        context.setEnvironment(environment);

        // 将设置在SpringApplication上的beanNameGenerator、resourceLoader设置到Spring容器中
        postProcessApplicationContext(context);

        // 利用ApplicationContextInitializer初始化Spring容器
        applyInitializers(context);

        // 发布ApplicationContextInitializedEvent事件,表示Spring容器初始化完成
        listeners.contextPrepared(context);

        // Spring容器初始化好了,就关闭DefaultBootstrapContext
        bootstrapContext.close(context);

        if (this.logStartupInfo) {
            logStartupInfo(context.getParent() == null);
            logStartupProfileInfo(context);
        }

        // 注册一些单例Bean
        // Add boot specific singleton beans
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }

        // 设置allowCircularReferences和allowBeanDefinitionOverriding给Spring容器
        if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
            ((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);
            if (beanFactory instanceof DefaultListableBeanFactory) {
                ((DefaultListableBeanFactory) beanFactory)
                        .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
            }
        }

        if (this.lazyInitialization) {
            context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
        }

        // Load the sources
        // 拿到启动配置类(run方法传进来的类)
        Set<Object> sources = getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");

        // 将启动配置类解析为BeanDefinition注册到Spring容器中
        load(context, sources.toArray(new Object[0]));

        // 发布ApplicationPreparedEvent事件,表示Spring容器已经准备好
        listeners.contextLoaded(context);
    }

6.6 入参

            // 4、将run()的参数封装为DefaultApplicationArguments对象
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

            // 14、从Spring容器中获取ApplicationRunner和CommandLineRunner,并执行其run()
            callRunners(context, applicationArguments);

callRunners()会去Spring容器查找ApplicationRunner和CommandLineRunner执行,执行时会传入入参applicationArguments。

7.重点分析一下各种配置的解析prepareEnvironment()

    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
            DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
        // Create and configure the environment
        // 创建ApplicationServletEnvironment,里面添加了四个PropertySource
        // 1. StubPropertySource {name='servletConfigInitParams'}
        // 2. StubPropertySource {name='servletContextInitParams'}
        // 3. PropertiesPropertySource {name='systemProperties'}
        // 4. SystemEnvironmentPropertySource {name='systemEnvironment'}
        ConfigurableEnvironment environment = getOrCreateEnvironment();

        // 添加SimpleCommandLinePropertySource {name='commandLineArgs'},放在首位
        configureEnvironment(environment, applicationArguments.getSourceArgs());

        // 把所有的PropertySources封装为一个ConfigurationPropertySourcesPropertySource
        // 然后添加到environment中,放在首位
        ConfigurationPropertySources.attach(environment);

        // 发布ApplicationEnvironmentPreparedEvent事件,表示环境已经准备好了
        // 默认EnvironmentPostProcessorApplicationListener会处理这个事件,会从spring.factories中拿出EnvironmentPostProcessor进一步处理Environment
        listeners.environmentPrepared(bootstrapContext, environment);

        // 最后,把defaultProperties移到最后
        DefaultPropertiesPropertySource.moveToEnd(environment);
        Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
                "Environment prefix cannot be set via properties.");
        bindToSpringApplication(environment);
        if (!this.isCustomEnvironment) {
            environment = convertEnvironment(environment);
        }
        ConfigurationPropertySources.attach(environment);
        return environment;
    }
public class MutablePropertySources implements PropertySources {

    private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<>();

一个PropertySource就代表一个配置文件(解析完后的内容)。

    private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
        ConfigurableEnvironment environment = event.getEnvironment();
        SpringApplication application = event.getSpringApplication();

        // 从spring.factories中拿出EnvironmentPostProcessor进一步处理Environment
        // RandomValuePropertySourceEnvironmentPostProcessor
        // SystemEnvironmentPropertySourceEnvironmentPostProcessor
        // SpringApplicationJsonEnvironmentPostProcessor
        // CloudFoundryVcapEnvironmentPostProcessor
        // ConfigDataEnvironmentPostProcessor
        // IntegrationPropertiesEnvironmentPostProcessor
        // DebugAgentEnvironmentPostProcessor
        for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(),
                event.getBootstrapContext())) {
            postProcessor.postProcessEnvironment(environment, application);
        }
    }

来看看这些EnvironmentPostProcessor的作用

  • RandomValuePropertySourceEnvironmentPostProcessor添加了一个random
  • SystemEnvironmentPropertySourceEnvironmentPostProcessor有前缀的情况下会替换systemEnvironment(操作系统的环境变量)。Environment variables中进行配置。这个前缀可用于区分环境,操作系统级别的环境。比如如果配置了前缀zhouyu,必须配置成zhouyu.k5=v5才能获取到k5的值。
  • SpringApplicationJsonEnvironmentPostProcessor会去获取spring.application.json里面配置的json字符串,然后解析出来后放到PropertySource。只能配置在VM options里面或者Program arguments里面,也就是只能配置在已经解析完成的属性文件里面。此时properties还没解析,配置无用。
  • CloudFoundryVcapEnvironmentPostProcessor
  • ConfigDataEnvironmentPostProcessor在三个参数指定的路径下面去找properties或者yaml文件

整体流程:






SpringBoot完整的配置优先级
https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config

相关文章

网友评论

    本文标题:SpringBoot自动装配及启动

    本文链接:https://www.haomeiwen.com/subject/odvhcdtx.html