美文网首页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