SpringBoot (九)——启动配置原理分析

文章目录

    • 1. SpringBoot 启动流程
    • 2. 启动原理
    • 3. 几个重要的事件回调机制
      • 3.1 测试

1. SpringBoot 启动流程

SpringBoot (九)——启动配置原理分析_第1张图片

2. 启动原理

public static void main(String[] args) {
		//xxx.class:主配置类,(可以传多个)
        SpringApplication.run(Springboot01Application.class, args);
    }
  1. 从run方法开始,创建SpringApplication,然后再调用run方法
/**
 * ConfigurableApplicationContext(可配置的应用程序上下文)
 */
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
        //调用下面的run方法
        return run(new Class[]{primarySource}, args);
    }

    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        return (new SpringApplication(primarySources)).run(args);
    }
  1. 创建springApplication
//primarySources:主配置类
new SpringApplication(primarySources)
public SpringApplication(Class<?>... primarySources) {
		//调用下面构造方法
        this((ResourceLoader)null, primarySources);
    }

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    	//设置一些默认参数
        this.sources = new LinkedHashSet();
        this.bannerMode = Mode.CONSOLE;
        this.logStartupInfo = true;
        this.addCommandLineProperties = true;
        this.addConversionService = true;
        this.headless = true;
        this.registerShutdownHook = true;
        this.additionalProfiles = new HashSet();
        this.isCustomEnvironment = false;
        this.lazyInitialization = false;
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        
        //保存主配置类到一个Set集合primarySources中
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        
        //获取当前的应用类型,判断是不是web应用,见2.1
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
       
        //从类路径下找到META‐INF/spring.factories配置的所有ApplicationContextInitializer;然后保存起来,见2.2
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
       
        //从类路径下找到META‐INF/spring.ApplicationListener;然后保存起来,原理同上,见2.3图
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        
        //从多个配置类中找到有main方法的主配置类,见2.4图(在调run方法的时候是可以传递多个配置类的)
        this.mainApplicationClass = this.deduceMainApplicationClass();
        //执行完毕,SpringApplication对象就创建出来了,返回到1处,调用SpringApplication对象的run方法,到3
    }

  • 2.1 deduceFromClasspath() 判断是不是Web应用
static WebApplicationType deduceFromClasspath() {
        if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
            return REACTIVE;
        } else {
            String[] var0 = SERVLET_INDICATOR_CLASSES;
            int var1 = var0.length;

            for(int var2 = 0; var2 < var1; ++var2) {
                String className = var0[var2];
                if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
                    return NONE;
                }
            }

            return SERVLET;
        }
    }
  • 2.2 getSpringFactoriesInstances :获取所有的ApplicationContextInitializer类型的实例
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
		//调用下面重载方法getSpringFactoriesInstances
        return this.getSpringFactoriesInstances(type, new Class[0]);
    }
	
    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = this.getClassLoader();
		
		//获取key为ApplicationContextInitializer全类名的所有值,见下2.2.1
        Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));

		 //根据拿到的类名集合,使用反射创建对象放到集合中返回 见 2.2.2
        List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;  //返回2,到set
    }

  • 2.2.1 获取所有的ApplicationContextInitializer的全类名
    SpringBoot (九)——启动配置原理分析_第2张图片
    上图65行中调用重载的方法:
//把类路径下所有META‐INF/spring.factories中的配置都存储起来,并返回,见下图
(List)loadSpringFactories(classLoader)

SpringBoot (九)——启动配置原理分析_第3张图片
然后再调用getOrDefault(factoryTypeName, Collections.emptyList())方法,获取key为

ApplicationContextInitializer类名的value集合

SpringBoot (九)——启动配置原理分析_第4张图片

  • 2.2.2 根据2.2.1中的全类名创建实例对象
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) {
        List<T> instances = new ArrayList(names.size());
        Iterator var7 = names.iterator();

        while(var7.hasNext()) {
            String name = (String)var7.next();

            try {
                Class<?> instanceClass = ClassUtils.forName(name, classLoader);
                Assert.isAssignable(type, instanceClass);
                Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
                T instance = BeanUtils.instantiateClass(constructor, args);
                instances.add(instance);
            } catch (Throwable var12) {
                throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, var12);
            }
        }

        return instances;
    }

SpringBoot (九)——启动配置原理分析_第5张图片
返回到2.2 this.setInitializers(ApplicationContextInitializer类型对象的集合)

  • 2.3,获取MET-INF/spring.factories中所有的listeners
    SpringBoot (九)——启动配置原理分析_第6张图片
  • 2.4
    SpringBoot (九)——启动配置原理分析_第7张图片
  1. 调用SpringApplication对象的run方法
    SpringBoot (九)——启动配置原理分析_第8张图片
public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        //声明IOC容器
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        this.configureHeadlessProperty();

		//从类路径下META‐INF/spring.factories获取SpringApplicationRunListeners,原理同2中
		//获取ApplicationContextInitializer和ApplicationListener
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        
        //遍历上一步获取的所有SpringApplicationRunListener,调用其starting方法
        listeners.starting();

        Collection exceptionReporters;
        try {
			//封装命令行
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            
            //准备环境,把上面获取到的listeners传过去,见3.1
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            
            this.configureIgnoreBeanInfo(environment);

			//打印Banner,就是控制台那个Spring字符画
            Banner printedBanner = this.printBanner(environment);
            
            //根据当前环境利用反射创建IOC容器,决定创建web的IOC还是其他的IOC 见3.2
            context = this.createApplicationContext();

			//从类路径下META‐INF/spring.factories获取SpringBootExceptionReporter,原理同2中获取
		    //ApplicationContextInitializer和ApplicationListener
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            
            //准备IOC容器,见3.3
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
           
            //刷新IOC容器,IOC初始化(如果是web应用还会创建嵌入式的Tomcat),可查看spring容器启动过程,链接在3.4
            //扫描,创建,加载所有组件的地方;(配置类,组件,自动配置) 
            this.refreshContext(context);

			 //这是一个空方法 
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }
			
			//调用所有SpringApplicationRunListener的started方法
            listeners.started(context);

			 //见3.5 ,从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调,ApplicationRunner先回调,	
			 //CommandLineRunner 再回调
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, exceptionReporters, listeners);
            throw new IllegalStateException(var10);
        }

        try {
        	//调用所有SpringApplicationRunListener的running方法
            listeners.running(context);
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }

容器创建完成,返回步骤1处,最后返回到启动类

  • 3.1
 private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
 		//获取或者创建环境,有则获取,无则创建
        ConfigurableEnvironment environment = this.getOrCreateEnvironment();

		 //配置环境
        this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
        ConfigurationPropertySources.attach((Environment)environment);
        
        //创建环境完成后,调用前面获取的所有SpringApplicationRunListener的environmentPrepared方法
        listeners.environmentPrepared((ConfigurableEnvironment)environment);
        this.bindToSpringApplication((ConfigurableEnvironment)environment);
        if (!this.isCustomEnvironment) {
            environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
        }

        ConfigurationPropertySources.attach((Environment)environment);
        return (ConfigurableEnvironment)environment;
    }

回到3,将创建好的environment返回

  • 3.2 根据当前环境创建IOC
protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                switch(this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
                    break;
                case REACTIVE:
                    contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
                    break;
                default:
                    contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
                }
            } catch (ClassNotFoundException var3) {
                throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
            }
        }

        return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
    }

将创建好的容器返回,到3

  • 3.3 准备IOC容器
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
		 //将创建好的环境放到IOC容器中
        context.setEnvironment(environment);

		//注册一些组件
        this.postProcessApplicationContext(context);
         
        //获取所有的ApplicationContextInitializer调用其initialize方法,这些ApplicationContextInitializer
        //就是在2步骤中获取的,见3.3.1
        this.applyInitializers(context);

		//回调所有的SpringApplicationRunListener的contextPrepared方法,
		//这些SpringApplicationRunListeners是在步骤3中获取的
        listeners.contextPrepared(context);

		//打印日志
        if (this.logStartupInfo) {
            this.logStartupInfo(context.getParent() == null);
            this.logStartupProfileInfo(context);
        }

        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }

        if (beanFactory instanceof DefaultListableBeanFactory) {
            ((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }

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

        Set<Object> sources = this.getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        this.load(context, sources.toArray(new Object[0]));
        //回调所有的SpringApplicationRunListener的contextLoaded方法
        listeners.contextLoaded(context);
    }

prepareContext方法运行完毕,返回到步骤3,执行refreshContext方法

  • 3.3.1
 protected void applyInitializers(ConfigurableApplicationContext context) {
        Iterator var2 = this.getInitializers().iterator();

        while(var2.hasNext()) {
            ApplicationContextInitializer initializer = (ApplicationContextInitializer)var2.next();
            Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class);
            Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
            initializer.initialize(context);
        }

    }

  • 3.4
    一篇文章让你理解 Spring IOC容器创建过程
  • 3.5
 private void callRunners(ApplicationContext context, ApplicationArguments args) {
        List<Object> runners = new ArrayList();
        runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
        runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
        AnnotationAwareOrderComparator.sort(runners);
        Iterator var4 = (new LinkedHashSet(runners)).iterator();

        while(var4.hasNext()) {
            Object runner = var4.next();
            if (runner instanceof ApplicationRunner) {
                this.callRunner((ApplicationRunner)runner, args);
            }

            if (runner instanceof CommandLineRunner) {
                this.callRunner((CommandLineRunner)runner, args);
            }
        }
    }

3. 几个重要的事件回调机制

配置在META-INF/spring.factories

  • ApplicationContextInitializer
  • SpringApplicationRunListene

只需要放在ioc容器中

  • ApplicationRunner
  • CommandLineRunner

3.1 测试

  1. 创建ApplicationContextInitializerSpringApplicationRunListener的实现类,并在META-INF/spring.factories文件中配置

ApplicationContextInitializer 子类

public class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
        System.out.println("MyApplicationContextInitializer..initialize");
    }
}

SpringApplicationRunListener 子类

public class MySpringApplicationRunListener implements SpringApplicationRunListener {
    @Override
    public void starting() {
        System.out.println("MySpringApplicationRunListener...starting");
    }

    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {
        System.out.println("MySpringApplicationRunListener...environmentPrepared");
    }

    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        System.out.println("MySpringApplicationRunListener..contextPrepared");
    }

    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
        System.out.println("MySpringApplicationRunListener..contextLoaded");
    }

    @Override
    public void started(ConfigurableApplicationContext context) {
        System.out.println("MySpringApplicationRunListener..started");
    }

    @Override
    public void running(ConfigurableApplicationContext context) {
        System.out.println("MySpringApplicationRunListener..run");
    }

    @Override
    public void failed(ConfigurableApplicationContext context, Throwable exception) {
        System.out.println("MySpringApplicationRunListener..failed");
    }
}


ApplicationRunner 子类

@Component // 加入到IOC容器
public class MyApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("MyApplicationRunner..run");
    }
}

在resource 下创建META-INF/spring.factories

org.springframework.boot.SpringApplicationRunListener=\
com.ewen.springboot07.listener.MySpringApplicationRunListener

org.springframework.context.ApplicationContextInitializer=\
com.ewen.springboot07.listener.MyApplicationContextInitializer  

启动报错java.lang.NoSuchMethodException:说是没有找到带org.springframework.boot.SpringApplication和String数组类型参数的构造器,给MySpringApplicationRunListener添加这样的构造器

public MySpringApplicationRunListener(SpringApplication application, String[] args){

    }

执行结果
SpringBoot (九)——启动配置原理分析_第9张图片

如有不足之处,欢迎指正!谢谢!

你可能感兴趣的:(spring,boot,spring,boot,启动原理,学习笔记)