美文网首页Spring技术Spring源码分析
八、SpringBoot启动配置原理

八、SpringBoot启动配置原理

作者: 木石前盟Caychen | 来源:发表于2019-04-24 11:14 被阅读0次

    重要的事件回调机制:

    • ApplicationContextInitializer

    • SpringApplicationRunListener

    • ApplicationRunner

    • CommandLineRunner

    前两者需要配置在META-INF/spring.factories中

    后两者只需要放在ioc容器中

    启动流程:

    7.1、创建SpringApplication对象

    public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
        return (new SpringApplication(sources)).run(args);
    }
    

    首先根据主类创建SpringApplication对象

    public SpringApplication(Object... sources) {
        //初始化对象
        initialize(sources);
    }
    

    7.2、进行初始化工作

    SpringApplication#initialize

    private void initialize(Object[] sources) {
        if (sources != null && sources.length > 0) {
            //保存主配置类
            this.sources.addAll(Arrays.asList(sources));
        }
        //判断是否是web应用,源码#7.2.1
        this.webEnvironment = deduceWebEnvironment();   
        
        //从类路径下找到META-INF/spring.factories配置的所有ApplicationContextInitializer,然后保存起来,源码#7.2.2
        setInitializers((Collection) getSpringFactoriesInstances(
                    ApplicationContextInitializer.class));
        
        //从类路径下找到META-INF/spring.factories配置的所有ApplicationListener,同上
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        
        //从多个主配置类中找到有main方法的主类,源码#7.2.3
        this.mainApplicationClass = deduceMainApplicationClass();
    }
    

    #7.2.1:SpringApplication#deduceWebEnvironment

    private static final String[] WEB_ENVIRONMENT_CLASSES = new String[]{"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext"};
    
    private boolean deduceWebEnvironment() {
        for (String className : WEB_ENVIRONMENT_CLASSES) {
            if (!ClassUtils.isPresent(className, null)) {
                return false;
            }
        }
        return true;
    }
    

    从WEB_ENVIRONMENT_CLASSES集合中遍历元素判断当前环境中是否有web应用的两个重要的依赖。

    #7.2.2:SpringApplication#getSpringFactoriesInstances

    从类路径下的所有jar包里的META-INF/spring.factories文件中获取所有type类型的类

    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 classLoader = Thread.currentThread().getContextClassLoader();
        
        //使用ClassLoader根据type来获取所有的value值并返回name组成的集合,源码#7.2.2.1
       Set<String> names = new LinkedHashSet<String>(
            SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        
        //根据name值利用反射机制得到name所在的类实例,源码#7.2.2.2
        List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
            classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }
    

    #7.2.2.1:SpringFactoriesLoader#loadFactoryNames

    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
    
    public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
    
        try {
            //从类路径下所有jar包中查找META-INF/spring.factories文件路径
            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 = (URL)urls.nextElement();
                
                //依次遍历所有的文件路径,从文件中获取key为classname对应value,并以逗号分隔加入到集合中
                Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
                String factoryClassNames = properties.getProperty(factoryClassName);
                result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
            }
            
            //返回类型集合
            return result;
        } catch (IOException var8) {
            throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
                        "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
    }
    

    #7.2.2.2:SpringApplication#createSpringFactoriesInstances

    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;
    }
    

    #7.2.3:SpringApplication#deduceMainApplicationClass

    private Class<?> deduceMainApplicationClass() {
        try {
            StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
            for (StackTraceElement stackTraceElement : stackTrace) {
                //获取main方法的主类
                if ("main".equals(stackTraceElement.getMethodName())) {
                    return Class.forName(stackTraceElement.getClassName());
                }
            }
        }
        catch (ClassNotFoundException ex) {
            // Swallow and continue
        }
        return null;
    }
    

    以上代码完成SpringApplication对象创建。

    所有的initializer集合,如图所示:

    所有的initializer.png

    所有的listener集合,如图所示:

    以上两个示意图中的集合数据不一定一模一样,要看具体依赖 ,依赖其他模块,可能就不一样了。

    7.3、运行run方法

    public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        FailureAnalyzers analyzers = null;
        configureHeadlessProperty();
        
        //获取SpringApplicationRunListeners,从类路径下找到META-INF/spring.factories配置的所有SpringApplicationRunListener,源码#7.3.1
        SpringApplicationRunListeners listeners = getRunListeners(args);
        
        //回调所有的SpringApplicationRunListener的starting方法,源码#7.3.2
        listeners.starting();
        
        try {
            //封装命令行参数
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                args);
            
            //准备环境:首先创建环境对象,创建完成后回调每个SpringApplicationRunListener对象的environmentPrepared方法,源码#7.3.3
            ConfigurableEnvironment environment = prepareEnvironment(listeners,
                                                                     applicationArguments);
            
            //打印Banner,源码#7.3.4
            Banner printedBanner = printBanner(environment);
            
            //创建ApplicationContext,决定创建web的ioc容器还是普通的ioc容器,具体原理看原来文章(此处略)
            context = createApplicationContext();
            
            analyzers = new FailureAnalyzers(context);
            
            //准备上下文,将environment保存到ioc容器中,源码#7.3.5
            //prepareContext方法内部:
            //首先applyInitializers(context);用来回调之前保存的所有的ApplicationContextInitializer的initialize方法
            //然后listeners.contextPrepared(context);用来回调所有的SpringApplicationRunListener的contextPrepared方法
            //最后listeners.contextLoaded(context);再回调所有的SpringApplicationRunListener的contextLoaded方法
            prepareContext(context, environment, listeners, applicationArguments,
                           printedBanner);
            
            //刷新ioc容器:首先创建ioc容器并初始化,如果是web应用还会创建嵌入式的Servlet容器,具体原理看原来文章(此处略)
            refreshContext(context);
            
            //从ioc容器中获取所有的ApplicationRunner和CommandLineRunner类,源码#7.3.6
            //并先回调ApplicationRunner的run方法,再回调CommandLineRunner的run方法
            afterRefresh(context, applicationArguments);
            
            //执行所有的SpringApplicationRunListener的finished方法,源码#7.3.7
            listeners.finished(context, null);
            
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass)
                    .logStarted(getApplicationLog(), stopWatch);
            }
            
            //整个SpringBoot应用启动完成后返回ioc容器
            return context;
        }
        catch (Throwable ex) {
            handleRunFailure(context, listeners, analyzers, ex);
            throw new IllegalStateException(ex);
        }
    }
    

    #7.3.1:SpringApplication#getRunListeners

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

    从类路径下找到所有META-INF/spring.factories文件中配置的所有SpringApplicationRunListener类,并保存到SpringApplicationRunListeners对象的listeners对象集合中

    #7.3.2:SpringApplicationRunListeners#starting

    public void starting() {
        for (SpringApplicationRunListener listener : this.listeners) {
            listener.starting();
        }
    }
    

    调用所有SpringApplicationRunListener对象的starting方法。

    #7.3.3:SpringApplication#prepareEnvironment

    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
        //获取或者创建环境,源码#7.3.3.1
        ConfigurableEnvironment environment = getOrCreateEnvironment();
        
        //配置环境对象,比如当前的profiles,源码#7.3.3.2
        configureEnvironment(environment, applicationArguments.getSourceArgs());
        
        //调用每个SpringApplicationRunListener对象的environmentPrepared方法,源码#7.3.3.3
        listeners.environmentPrepared(environment);
        if (!this.webEnvironment) {
            environment = new EnvironmentConverter(getClassLoader())
                .convertToStandardEnvironmentIfNecessary(environment);
        }
    
        //返回环境对象
        return environment;
    }
    

    #7.3.3.1:SpringApplication#getOrCreateEnvironment

    private ConfigurableEnvironment getOrCreateEnvironment() {
       if (this.environment != null) {
           return this.environment;
       }
        if (this.webEnvironment) {
            return new StandardServletEnvironment();
        }
        return new StandardEnvironment();
    }
    

    在上面7.2章节中deduceWebEnvironment()方法返回是否是web环境标识,如果是web环境,webEnvironment则为true,否则为false。在此getOrCreateEnvironment()方法中根据webEnvironment的值创建环境对象,如果webEnvironment为true,则创建StandardServletEnvironment类型的环境对象,否则创建StandardEnvironment类型的环境对象。

    #7.3.3.2:SpringApplication#configureEnvironment

    protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
        this.configurePropertySources(environment, args);
        
        //配置当前环境的profiles,默认为default
        this.configureProfiles(environment, args);
    }
    

    #7.3.3.3:SpringApplicationRunListeners#environmentPrepared

    public void environmentPrepared(ConfigurableEnvironment environment) {
        for (SpringApplicationRunListener listener : this.listeners) {
            listener.environmentPrepared(environment);
        }
    }
    

    调用所有SpringApplicationRunListener对象的environmentPrepared方法。

    #7.3.4:SpringApplication#printBanner

    private Banner printBanner(ConfigurableEnvironment environment) {
        //判断当前bannerMode是否为OFF
        if (this.bannerMode == Banner.Mode.OFF) {
            return null;
        } else {
            ResourceLoader resourceLoader = (this.resourceLoader != null ? this.resourceLoader
                    : new DefaultResourceLoader(getClassLoader()));
            
            //获取BannerPrinter打印对象
            SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(
                    resourceLoader, this.banner);
            
            //根据BannerMode的值,来打印Banner,源码#7.3.4.1
            if (this.bannerMode == Mode.LOG) {
                return bannerPrinter.print(environment, this.mainApplicationClass, logger);
            }
            return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
        }
    }
    

    #7.3.4.1:SpringApplicationBannerPrinter#print

    public Banner print(Environment environment, Class<?> sourceClass, PrintStream out) {
        //获取Banner对象
        Banner banner = getBanner(environment, this.fallbackBanner);
        
        //依次调用Banners集合中的Banner对象的printBanner来开始打印Banner,源码#7.3.4.1.1
        banner.printBanner(environment, sourceClass, out);
        return new PrintedBanner(banner, sourceClass);
    }
    
    private Banner getBanner(Environment environment, Banner definedBanner) {
        Banners banners = new Banners();
        
        //从image图片格式中获取banner对象,如果有则保存到banners集合中
        banners.addIfNotNull(getImageBanner(environment));
        
        //从text文本格式中获取banner对象,如果有则保存到banners集合中
        banners.addIfNotNull(getTextBanner(environment));
        
        //banners集合个数只有0,1,2三种情况,然后判断banners集合是否为空,如果不为空,则返回之前获取到的Banners集合对象
        if (banners.hasAtLeastOneBanner()) {
            return banners;
        }
        if (this.fallbackBanner != null) {
            return this.fallbackBanner;
        }
        return DEFAULT_BANNER;
    }
    
    static final String BANNER_LOCATION_PROPERTY = "banner.location";
    static final String DEFAULT_BANNER_LOCATION = "banner.txt";
    
    private Banner getTextBanner(Environment environment) {
        //首先从环境对象中获取banner.location的值,如果存在直接返回该值,否则使用默认的banner.txt文件作为location的值
        String location = environment.getProperty(BANNER_LOCATION_PROPERTY,
                    DEFAULT_BANNER_LOCATION);
        
        //根据location的值获取Resource对象
        Resource resource = this.resourceLoader.getResource(location);
        
        //判断Resource是否存在,如果存在返回ResourceBanner,否则返回null
        if (resource.exists()) {
            return new ResourceBanner(resource);
        }
        return null;
    }
    
    static final String[] IMAGE_EXTENSION = new String[]{"gif", "jpg", "png"};
    static final String BANNER_IMAGE_LOCATION_PROPERTY = "banner.image.location";
    
    private Banner getImageBanner(Environment environment) {
        //首先从环境对象中获取banner.image.location的值,如果存在直接返回该值
        String location = environment.getProperty(BANNER_IMAGE_LOCATION_PROPERTY);
        
        //判断是否有值
        if (StringUtils.hasLength(location)) {
            //如果banner.image.location有值,则根据该值返回Resource对象,并判断是否存在,如果存在则返回ImageBanner,否则返回null
            Resource resource = this.resourceLoader.getResource(location);
            return (resource.exists() ? new ImageBanner(resource) : null);
        }
        //如果banner.image.location没有值或者未设置,则依次判断banner.gif,banner.jpg,banner.png三种文件是否存在,如果某个文件存在,则直接返回该文件的ImageBanner对象,否则返回null
        for (String ext : IMAGE_EXTENSION) {
            Resource resource = this.resourceLoader.getResource("banner." + ext);
            if (resource.exists()) {
                return new ImageBanner(resource);
            }
        }
        return null;
    }
    

    #7.3.4.1.1:SpringApplicationBannerPrinter#printBanner

    @Override
    public void printBanner(Environment environment, Class<?> sourceClass,
                            PrintStream out) {
        for (Banner banner : this.banners) {
            //依次调用每个banner对象的pringBanner方法
            banner.printBanner(environment, sourceClass, out);
        }
    }
    

    ResourceBanner#printBanner

    @Override
    public void printBanner(Environment environment, Class<?> sourceClass,
                            PrintStream out) {
        try {
            String banner = StreamUtils.copyToString(this.resource.getInputStream(),
                                                     environment.getProperty("banner.charset", Charset.class,
                                                                             Charset.forName("UTF-8")));
    
            for (PropertyResolver resolver : getPropertyResolvers(environment,
                                                                  sourceClass)) {
                banner = resolver.resolvePlaceholders(banner);
            }
            out.println(banner);
        }
        catch (Exception ex) {
            //other code...
        }
    }
    

    ImageBanner#printBanner

    @Override
    public void printBanner(Environment environment, Class<?> sourceClass,
                            PrintStream out) {
        String headless = System.getProperty("java.awt.headless");
        try {
            System.setProperty("java.awt.headless", "true");
            printBanner(environment, out);
        }
        //catch...finally...
    }
    

    #7.3.5:SpringApplication#prepareContext

    private void prepareContext(ConfigurableApplicationContext context,
                ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
                ApplicationArguments applicationArguments, Banner printedBanner) {
        context.setEnvironment(environment);
        postProcessApplicationContext(context);
        
        //回调所有的initializer对象的initialize方法,源码#7.3.5.1
        applyInitializers(context);
        
        //回调所有的listener对象的contextPrepared方法,源码#7.3.5.2
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
            logStartupInfo(context.getParent() == null);
            logStartupProfileInfo(context);
        }
    
        //other code...
        
        //回调所有的listener对象的contextLoaded方法,源码#7.3.5.3
        listeners.contextLoaded(context);
    }
    

    #7.3.5.1:SpringApplication#applyInitializers

    protected void applyInitializers(ConfigurableApplicationContext context) {
        //依次遍历所有的ApplicationContextInitializer对象,调用其initialize方法
        for (ApplicationContextInitializer initializer : getInitializers()) {
            Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
                initializer.getClass(), ApplicationContextInitializer.class);
            Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
            initializer.initialize(context);
        }
    }
    

    #7.3.5.2:SpringApplicationRunListeners#contextPrepared

    public void contextPrepared(ConfigurableApplicationContext context) {
        //依次遍历所有的SpringApplicationRunListener对象,调用其contextPrepared方法
        for (SpringApplicationRunListener listener : this.listeners) {
            listener.contextPrepared(context);
        }
    }
    

    #7.3.5.3 : SpringApplicationRunListeners#contextLoaded

    public void contextPrepared(ConfigurableApplicationContext context) {
        //依次遍历所有的SpringApplicationRunListener对象,调用其contextPrepared方法
        for (SpringApplicationRunListener listener : this.listeners) {
            listener.contextPrepared(context);
        }
    }
    

    #7.3.6:SpringApplication#afterRefresh

    protected void afterRefresh(ConfigurableApplicationContext context,
                ApplicationArguments args) {
        //调用所有的xxxRunner的run方法
        callRunners(context, args);
    }
    
    private void callRunners(ApplicationContext context, ApplicationArguments args) {
        List<Object> runners = new ArrayList<Object>();
        //从ioc容器中获取所有的ApplicationRunner和CommandLineRunner类
        runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
        runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
        AnnotationAwareOrderComparator.sort(runners);
        
        //先回调所有ApplicationRunner对象的run方法,在回调所有CommandLineRunner对象的run方法
        for (Object runner : new LinkedHashSet<Object>(runners)) {
            if (runner instanceof ApplicationRunner) {
                callRunner((ApplicationRunner) runner, args);
            }
            if (runner instanceof CommandLineRunner) {
                callRunner((CommandLineRunner) runner, args);
            }
        }
    }
    

    #7.3.7:SpringApplicationRunListeners#finished

    public void finished(ConfigurableApplicationContext context, Throwable exception) {
        //依次遍历所有的SpringApplicationRunListener对象,调用其finished方法
        for (SpringApplicationRunListener listener : this.listeners) {
            callFinishedListener(listener, context, exception);
        }
    }
    
    private void callFinishedListener(SpringApplicationRunListener listener,
                ConfigurableApplicationContext context, Throwable exception) {
        try {
            listener.finished(context, exception);
        }
        catch (Throwable ex) {
            //other code...
        }
    }
    

    这样整个SpringBoot应用启动就完成并返回ioc容器。

    7.4、测试原理用例

    分别编写四个事件回调类的子类:

    ApplicationContextInitializer子类:HelloApplicationContextInitializer

    public class HelloApplicationContextInitializer implements ApplicationContextInitializer {
    
        @Override
        public void initialize(ConfigurableApplicationContext applicationContext) {
            System.out.println("HelloApplicationContextInitializer...initialize...");
        }
    }
    

    SpringApplicationRunListener子类:HelloSpringApplicationRunListener

    public class HelloSpringApplicationRunListener implements SpringApplicationRunListener {
    
        //SpringApplicationRunListener子类必须有一个有参构造函数,参数分别是SpringApplication和String[]
        public HelloSpringApplicationRunListener(SpringApplication springApplication, String[] args){
            System.out.println("HelloSpringApplicationRunListener...constructor...");
        }
        
        @Override
        public void starting() {
            System.out.println("HelloSpringApplicationRunListener...starting...");
        }
    
        @Override
        public void environmentPrepared(ConfigurableEnvironment environment) {
            System.out.println("HelloSpringApplicationRunListener...environmentPrepared...");
        }
    
        @Override
        public void contextPrepared(ConfigurableApplicationContext context) {
            System.out.println("HelloSpringApplicationRunListener...contextPrepared...");
        }
    
        @Override
        public void contextLoaded(ConfigurableApplicationContext context) {
            System.out.println("HelloSpringApplicationRunListener...contextLoaded...");
        }
    
        @Override
        public void finished(ConfigurableApplicationContext context, Throwable exception) {
            System.out.println("HelloSpringApplicationRunListener...finished...");
        }
    }
    

    ApplicationRunner子类:HelloApplicationRunner

    @Component
    public class HelloApplicationRunner implements ApplicationRunner {
    
        @Override
        public void run(ApplicationArguments args) throws Exception {
            System.out.println("HelloApplicationRunner...run...");
        }
    }
    

    CommandLineRunner子类:HelloCommandLineRunner

    @Component
    public class HelloCommandLineRunner implements CommandLineRunner {
    
        @Override
        public void run(String... args) throws Exception {
            System.out.println("HelloCommandLineRunner...run...");
        }
    }
    

    启动服务器之后,查看控制台输出的信息(忽略Spring Boot打印信息):

    HelloSpringApplicationRunListener...constructor...
    HelloSpringApplicationRunListener...starting...
    HelloSpringApplicationRunListener...environmentPrepared...
    HelloApplicationContextInitializer...initialize...
    HelloSpringApplicationRunListener...contextPrepared...
    HelloSpringApplicationRunListener...contextLoaded...
    HelloApplicationRunner...run...
    HelloCommandLineRunner...run...
    HelloSpringApplicationRunListener...finished...
    

    其结果跟源码分析过程一致。

    相关文章

      网友评论

        本文标题:八、SpringBoot启动配置原理

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