spring boot容器启动详解

一、容器启动

spring boot一般是指定容器启动main方法,然后以命令行方式启动Jar包,如下图:

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

这里核心关注2个东西:

1.@SpringBootApplication注解

2.SpringApplication.run()静态方法

 下面我们就分别探究这两块内容。

1.1 @SpringBootApplication注解

源码如下:

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

核心注解:

@SpringBootConfiguration(实际就是个@Configuration):表示这是一个JavaConfig配置类,可以在这个类中自定义bean,依赖关系等。-》这个是spring-boot特有的注解,常用到。
@EnableAutoConfiguration:详细见这篇博客
@ComponentScan:spring的自动扫描注解,可定义扫描范围,加载到IOC容器。-》这个不多说,spring的注解大家肯定眼熟

1.2 SpringApplication.run()静态方法

SpringApplication.run

public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        FailureAnalyzers analyzers = null;
        configureHeadlessProperty();
        SpringApplicationRunListeners listeners = getRunListeners(args);//1.获取***
        listeners.starting();-->启动!
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                    args);
            ConfigurableEnvironment environment = prepareEnvironment(listeners,//2.准备好环境,触发ApplicationEnvironmentPreparedEvent事件
                    applicationArguments);
            Banner printedBanner = printBanner(environment);//打印启动提示字符,默认spring的字符图
            context = createApplicationContext();//实例化一个可配置应用上下文
            analyzers = new FailureAnalyzers(context);
            prepareContext(context, environment, listeners, applicationArguments,//3.准备上下文
                    printedBanner);
            refreshContext(context);//4.刷新上下文
            afterRefresh(context, applicationArguments);//5.刷新上下文后
            listeners.finished(context, null);--关闭!
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass)
                        .logStarted(getApplicationLog(), stopWatch);
            }
            return context;
        }
        catch (Throwable ex) {
            handleRunFailure(context, listeners, analyzers, ex);
            throw new IllegalStateException(ex);
        }
    }

如上图,容器启动流程可以分为5个主要步骤:

1.getRunListeners获取***(SpringApplicationRunListeners )

实际是SpringApplicationRunListener类

private SpringApplicationRunListeners getRunListeners(String[] args) {
    Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
            SpringApplicationRunListener.class, types, this, args));
}

private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {
    return getSpringFactoriesInstances(type, new Class<?>[] {});
}

private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
        Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    // 使用Set确保的字符串的唯一性
    Set<String> names = new LinkedHashSet<String>(
            SpringFactoriesLoader.loadFactoryNames(type, classLoader));// 1.载入工厂名称集合
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes,// 2.创建工厂实例
            classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);// 排序
    return instances;
}

载入工厂名称(loadFactoryNames)

当前类的类加载器从META-INF/spring.factories文件中获取SpringApplicationRunListener类的配置

public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        try {
            Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            List<String> result = new ArrayList<String>();
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
                String factoryClassNames = properties.getProperty(factoryClassName);
                result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
            }
            return result;
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
                    "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
    }

上图,获取到工厂类名后,下面来看看META-INF/spring.factories中定义了啥:

# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader

# Run Listeners  这里呢,看这里!!!!
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

# 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.context.embedded.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.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,\
org.springframework.boot.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.logging.LoggingApplicationListener

# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor

# Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer

# FailureAnalysisReporters
org.springframework.boot.diagnostics.FailureAnalysisReporter=\
org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter

哇,都是些类全名称,且key都是接口,value都是实现类。我们根据key=“org.springframework.boot.SpringApplicationRunListener”查询得到实现类value="org.springframework.boot.context.event.EventPublishingRunListener"事件发布启动***,一猜也知道肯定要用”反射”根据类名获取类实例,下面很快得到验证...

创建spring工厂实例(createSpringFactoriesInstances)

根据第一步得到的Set<String> names(SpringApplicationRunListener的唯一实现类EventPublishingRunListener)生成"事件发布启动***"工厂实例

@SuppressWarnings("unchecked")
    private <T> List<T> createSpringFactoriesInstances(Class<T> type,
            Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
            Set<String> names) {
        List<T> instances = new ArrayList<T>(names.size());
        for (String name : names) {
            try {
                Class<?> instanceClass = ClassUtils.forName(name, classLoader);// 利用反射获取类
                Assert.isAssignable(type, instanceClass);
                Constructor<?> constructor = instanceClass
                        .getDeclaredConstructor(parameterTypes);// 得到构造器
                T instance = (T) BeanUtils.instantiateClass(constructor, args);// 根据构造器和参数构造实例
                instances.add(instance);
            }
            catch (Throwable ex) {
                throw new IllegalArgumentException(
                        "Cannot instantiate " + type + " : " + name, ex);
            }
        }
        return instances;
    }

2.准备好环境

构造一个ConfigurableEnvironment,这里不多说。

3.准备上下文

private void prepareContext(ConfigurableApplicationContext context,
            ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments, Banner printedBanner) {
        context.setEnvironment(environment);
        postProcessApplicationContext(context);//单例一个BeanNameGenerator,把ResourceLoader设置进应用上下文
        applyInitializers(context);//执行初始化器
        listeners.contextPrepared(context);// ***执行上下文"已准备好"方法
        if (this.logStartupInfo) {
            logStartupInfo(context.getParent() == null);
            logStartupProfileInfo(context);
        }

        // 添加spring boot特殊单例bean
        context.getBeanFactory().registerSingleton("springApplicationArguments",
                applicationArguments);
        if (printedBanner != null) {
            context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
        }

        // 载入资源
        Set<Object> sources = getSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        load(context, sources.toArray(new Object[sources.size()]));
        listeners.contextLoaded(context);// ***执行"上下文已加载"方法
    }

4.刷新上下文

private void refreshContext(ConfigurableApplicationContext context) {
    refresh(context);//核心类
    if (this.registerShutdownHook) {
        try {
            context.registerShutdownHook();//注册关闭钩子,容器关闭时执行
        }
        catch (AccessControlException ex) {
            // Not allowed in some environments.
        }
    }
}

protected void refresh(ApplicationContext applicationContext) {
    Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
    ((AbstractApplicationContext) applicationContext).refresh();
}

最终执行的是AbstractApplicationContext抽象类的refresh方法。

public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            //准备刷新的上下文环境,例如对系统属性或者环境变量进行准备及验证。
            prepareRefresh();

            //启动子类的refreshBeanFactory方法.解析xml
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            //为BeanFactory配置容器特性,例如类加载器、事件处理器等.
            prepareBeanFactory(beanFactory);

            try {
                //设置BeanFactory的后置处理. 空方法,留给子类拓展用。 
                postProcessBeanFactory(beanFactory);

                //调用BeanFactory的后处理器, 这些后处理器是在Bean定义中向容器注册的.  
                invokeBeanFactoryPostProcessors(beanFactory);

                //注册Bean的后处理器, 在Bean创建过程中调用.  
                registerBeanPostProcessors(beanFactory);

                //初始化上下文中的消息源,即不同语言的消息体进行国际化处理  
                initMessageSource();

                //初始化ApplicationEventMulticaster bean,应用事件广播器
                initApplicationEventMulticaster();

                //初始化其它特殊的Bean, 空方法,留给子类拓展用。 
                onRefresh();

                //检查并向容器注册***Bean
                registerListeners();

                //实例化所有剩余的(non-lazy-init) 单例Bean.
                finishBeanFactoryInitialization(beanFactory);

                //发布容器事件, 结束refresh过程. 
                finishRefresh();
            }

            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                //销毁已经创建的单例Bean, 以避免资源占用.
                destroyBeans();

                //取消refresh操作, 重置active标志. 
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }

            finally {
                //重置Spring的核心缓存
                resetCommonCaches();
            }
        }
    }

5.刷新完上下文后

spring boot提供的2个供用户自己拓展的接口:ApplicationRunner和CommandLineRunner。可以在容器启动完毕后(上下文刷新后)执行,做一些类似数据初始化的操作。

private void callRunners(ApplicationContext context, ApplicationArguments args) {
        List<Object> runners = new ArrayList<Object>();
        runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());//从上下文中获取ApplicationRunner类型的bean
        runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());//从上下文中获取CommandLineRunner类型的bean
        AnnotationAwareOrderComparator.sort(runners);//排序
        for (Object runner : new LinkedHashSet<Object>(runners)) {
            if (runner instanceof ApplicationRunner) {
                callRunner((ApplicationRunner) runner, args);//执行
            }
            if (runner instanceof CommandLineRunner) {
                callRunner((CommandLineRunner) runner, args);
            }
        }
    }
两个区别在于入参不同,根据实际情况自己选择。
public interface CommandLineRunner {
    void run(String... args) throws Exception;
}

public interface ApplicationRunner {
    void run(ApplicationArguments args) throws Exception;

}

 CommandLineRunner中执行参数是原始的java启动类main方法的String[] args字符串数组参数;ApplicationRunner中的参数经过处理提供一些方法例如:

 List<String> getOptionValues(String name); 

 根据名称获取值list,java 启动命令中 --foo=bar --foo=baz,则根据foo参数名返回list["bar", "baz"]

二、总结

原文地址 

全部评论

相关推荐

像好涩一样好学:这公司我也拿过 基本明确周六加班 工资还凑活 另外下次镜头往上点儿
点赞 评论 收藏
分享
10-06 12:46
门头沟学院 Java
跨考小白:定时任务启动
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务