springboot学习笔记8-1启动原理(springboot2.3.1版本)

    技术2022-07-11  122

    通过查看springboot主配置类的main方法中,调用的是springApplication中的静态的run方法

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

    进入到run方法中发现,springboot启动分为俩步,一个是实例化SpringApplication,将主配置类存到class数组中作为构造方法参数传进去

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

    第一步:创建SpringApplication对象

    一路点进来,我们发现最后调用的这个构造方法

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; //这里判断是否有主配置类,如果没有,则抛一个异常,这个异常如果被处理后可以继续向下执行,如果没被处理,程序不会向下执行 Assert.notNull(primarySources, "PrimarySources must not be null"); //将主配置类存到一个HashSet集合中,赋值 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); //同样也是给属性赋值,这个是web程序的属性,在WebApplicationType中会有三个属性NONE,SERVLET,REACTIVE this.webApplicationType = WebApplicationType.deduceFromClasspath(); //给initializers属性赋值,在getSpringFactoriesInstances里面我们可以找到调用了loadSpringFactories方法,最后获取的jar包中META-INF包下的Apring.factories这个配置文件中的ApplicationContextInitializer对应的属性 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); //给listeners赋值,一样的道理,获取的是ApplicationListener对应的属性 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }

    这个deduceFromClasspath()方法中最重要的功能 是加载class功能,而class加载是依赖jar包是否引起而判断的,所以如果引入了javax.servlet.Servlet的jar,则会启动Servlet模式,如果引入的jar是spring-boot-starter-webflux,而且没引入servlet相关的jar,则会启动Reactive模式。

    //判断当前的应用服务属于什么类型 static WebApplicationType deduceFromClasspath() { if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null) && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) { return WebApplicationType.REACTIVE; } for (String className : SERVLET_INDICATOR_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return WebApplicationType.NONE; } } return WebApplicationType.SERVLET; } private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" }; private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet"; private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";

    loadSpringFactories里面可以看到获取了FACTORIES_RESOURCE_LOCATION属性,这个FACTORIES_RESOURCE_LOCATION属性就是"META-INF/spring.factories"也就是说获取META-INF包下的spring.factories文件里面ApplicationContextInitializer属性对应的值以集合的形式返回赋值给initializers属性

    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = cache.get(classLoader); if (result != null) { return result; } try { Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); result = new LinkedMultiValueMap<>(); while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<?, ?> entry : properties.entrySet()) { String factoryTypeName = ((String) entry.getKey()).trim(); for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { result.add(factoryTypeName, factoryImplementationName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } }

    最后调用的deduceMainApplicationClass方法,作用是获取主配置类的main方法,将这个类信息返回

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

    第二步:运行程序

    public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); //这里是从META-INF下spring.factories里面获取SpringApplicationRunListener对应的值 SpringApplicationRunListeners listeners = getRunListeners(args); //这里遍历所有的SpringApplicationRunListeners,并且执行里面的starting方法 listeners.starting(); try { //封装命令行参数 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); //准备环境(先创建环境,创建完成后,回调SpringApplicationRunListeners里面的environmentPrepared方法,表示环境准备完成) ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); // 创建ioc容器,根据webApplicationType里面的属性来判断是创建web环境还是其他的 context = createApplicationContext(); //做异常分析 exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); //准备上下文环境,给ioc容器中的属性赋值 prepareContext(context, environment, listeners, applicationArguments, printedBanner); //刷新容器,ioc容器初始化(如果是web容器还会创建嵌入式的tomcat) //扫描,创建,加载,所有组件的地方(配置类,组件,自动配置) refreshContext(context); //这个在牢版本里面调用了callRunners方法,新版本里面为空方法,callRunners在下面有调用 afterRefresh(context, applicationArguments); //在这里,1.0版本有调用SpringApplicationRunListeners里面的finish方法,新版本的SpringApplicationRunListeners里面已经没有了finish方法 //保存当前应用状态 stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } //在ioc容器创建完成,保存状态之后执行该方法 //执行SpringApplicationRunListeners里面的started方法(在1.0版本没有这个步骤) listeners.started(context); //从ioc里面获取所有的ApplicationRunner和CommandLineRunner,遍历回调,先将所有的ApplicationRunner回调完,再回调CommandLineRunner callRunners(context, applicationArguments); } catch (Throwable ex) { //在这里面会执行SpringApplicationRunListeners的failed方法(1.0版本没有该方法) handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { //在callRunners方法执行完,ApplicationRunner,CommandLineRunner加载完之后执行,类似于老版本的finish方法 // 执行SpringApplicationRunListeners里面的running方法(在1.0版本没有这个步骤) listeners.running(context); } catch (Throwable ex) { //在这里面会执行SpringApplicationRunListeners的failed方法(1.0版本没有该方法) handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } //整个spring应用完成后,返回spring的ioc容器 return context; }

    准备环境

    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // Create and configure the environment ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); ConfigurationPropertySources.attach(environment); listeners.environmentPrepared(environment); bindToSpringApplication(environment); if (!this.isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment; }

    创建ioc容器,根据webApplicationType里面的属性来判断(SERVLET,REACTIVE),最后使用BeanUtils反射创建ioc容器

    protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { try { switch (this.webApplicationType) { case SERVLET: contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); break; case REACTIVE: contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); break; default: contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); } } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex); } } return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); }

    配置上下文环境

    private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { //给ioc容器中的environment赋值(配置环境) context.setEnvironment(environment); //ioc的后置组件 postProcessApplicationContext(context); //之前new SpringApplication的时候,已经从各个jar包的META-INF下的spring.favtories里面获取到的ApplicationContextInitializer,调用里面的initialize方法 applyInitializers(context); //listeners还要回调contextPrepared这个方法 listeners.contextPrepared(context); //在listeners准备执行完成之后,还要调用ApplicationContextInitializer里面的contextLoaded方法 if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // Add boot specific singleton beans 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()); } // Load the sources Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); load(context, sources.toArray(new Object[0])); listeners.contextLoaded(context); } protected void applyInitializers(ConfigurableApplicationContext context) { for (ApplicationContextInitializer initializer : getInitializers()) { Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class); Assert.isInstanceOf(requiredType, context, "Unable to call initializer."); initializer.initialize(context); } }
    Processed: 0.012, SQL: 9