SpringBoot核心类之SpringApplication类分析

1.SpringApplication初始化

1.1 SpringApplication静态方法run方法

我们找一个SpringBoot应用,然后打开应用启动主类,我们知道主应用的启动会调用SpringApplication的静态方法#run方法。run方法的执行会伴随调用SpringApplication的构造器。我点进去瞧一瞧:

    @SpringBootApplication(exclude={SecurityAutoConfiguration.class})
    public class SpringBootApp{
      public static void main(String[] args){
        SpringApplication.run(SpringBootApp.class, args);
      }
    }
    
    
    public class SpringApplication {
        
        public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
                return run(new Class[]{primarySource}, args);
        }
    	
        //此构造器是SpringApplication初始化的核心方法。
        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.resourceLoader = resourceLoader;
                Assert.notNull(primarySources, "PrimarySources must not be null");
                this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
            	//(1)推断web应用类型
                this.webApplicationType = WebApplicationType.deduceFromClasspath();
                //(2)加载Spring应用上下文初始化器this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
            	//(3)加载Spring应用事件监听器
                this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
            	//(4)推断应用引导类
                this.mainApplicationClass = this.deduceMainApplicationClass();
        }
    }    

在执行构造器过程种依次调用(1)(2)(3)(4)处的方法,下面就逐一分析每个方法的调用过程。

1.1.1 推断web应用类型
    public enum WebApplicationType {
        NONE,
        SERVLET,
        REACTIVE;
        private static final String[] SERVLET_INDICATOR_CLASSES = new String[]{"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext"};
        //检查当前ClassLoader下是否存在否写类作为判断标准
    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;
            }
        }
    }

通过ClassUtils.isPresent方法依次推断DispatcherHandlerDispatcherServletServletContainerConfigurableWebApplicationContext的存在性组合情况,从而判断web应用类型。

1.1.2 加载Spring应用上下文初始化器

首选看到这个步骤分两步走,

 this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));

先看第一步

  1. this.getSpringFactoriesInstances()

我仔细看看这个方法做了什么,

    public class SpringApplication {
        private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
                return this.getSpringFactoriesInstances(type, new Class[0]);
            }
    	
        private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
                ClassLoader classLoader = this.getClassLoader();
                //加载对象,
                Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
            	//创建对象,就是循环names,然后创建对象,并把对象放在list集合中
                List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
            	//将对象进行排序
                AnnotationAwareOrderComparator.sort(instances);
            	//返回的是对象数组
                return instances;
            }
    }

SpringFactoriesLoader#loadFactoryNames()每当看到loader都要引起注意。此处运用了Spring工厂加载机制。看到这里心中有些疑问:从哪里加载的对象?加载那些对象?我们跟进去瞧一瞧:

源码如下,粗略扫一眼有点似曾相识的感觉,就好像见到了前任一样,感觉好像在哪见过。没错,如果看过上一篇文章的小伙伴,想必已经能回忆起来了,这不就是加载META-INF/spring.factories资源配置文件下指定的实现类嘛,看看this.getSpringFactoriesInstances(ApplicationContextInitializer.class)者个方法传的参数,换言之就是要加载ApplicationContextInitializer接口类的实现类对象数组。

    public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
            String factoryClassName = factoryClass.getName();
            return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
        }
        
    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
            MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
            if (result != null) {
                return result;
            } else {
                try {
                    Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                    LinkedMultiValueMap result = new LinkedMultiValueMap();
    
                    while(urls.hasMoreElements()) {
                        URL url = (URL)urls.nextElement();
                        UrlResource resource = new UrlResource(url);
                        Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                        Iterator var6 = properties.entrySet().iterator();
    
                        while(var6.hasNext()) {
                            Entry<?, ?> entry = (Entry)var6.next();
                            String factoryClassName = ((String)entry.getKey()).trim();
                            String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                            int var10 = var9.length;
    
                            for(int var11 = 0; var11 < var10; ++var11) {
                                String factoryName = var9[var11];
                                result.add(factoryClassName, factoryName.trim());
                            }
                        }
                    }
    
                    cache.put(classLoader, result);
                    return result;
                } catch (IOException var13) {
                    throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
                }
            }
        }

不相信我们就去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.web.context.ServerPortInfoApplicationContextInitializer

小伙伴们,这次明白了吧,SpringBoot自动装配就是这个niaoxing。

看完第一步在看第二步做了什么

  1. this.setInitializers()
    //就是把上一步的对象放在initializers中
    private List<ApplicationContextInitializer<?>> initializers;
    public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
            this.initializers = new ArrayList();
            this.initializers.addAll(initializers);
    }
1.1.3 加载Spring应用事件监听器

好吧,这里看完了,看明白前面加载Spring应用上下文初始化器可以pass掉了,因为这一步和上面一样。

 this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
1.1.4 推断应用引导类

找到main方法启动入口类

       private Class<?> deduceMainApplicationClass() {
            try {
                //获取当前线程执行栈数组
                StackTraceElement[] stackTrace = (new RuntimeException()).getStackTrace();
                StackTraceElement[] var2 = stackTrace;
                int var3 = stackTrace.length;
    
                for(int var4 = 0; var4 < var3; ++var4) {
                    StackTraceElement stackTraceElement = var2[var4];
                    //判断那个方法包含main方法。
                    if ("main".equals(stackTraceElement.getMethodName())) {
                        return Class.forName(stackTraceElement.getClassName());
                    }
                }
            } catch (ClassNotFoundException var6) {
                ;//第一次见源码这么写
            }
    
            return null;
        }

至此SpringApplication初始化过程已经分析完了,小伙伴们,是不是感觉不过瘾,那我们继续深入深入,这个过程会很爽的。

2. SpringApplication运行阶段

2.1 SpringApplication核心方法run方法

运行时调用SpringApplication#run方法,现在我们一起来看看它的真容。

    //SpringApplication.class
        public ConfigurableApplicationContext run(String... args) {
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            ConfigurableApplicationContext context = null;
            Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
            this.configureHeadlessProperty();
            //(1)获取SpringApplicationRunListeners应用运行时监听器
            SpringApplicationRunListeners listeners = this.getRunListeners(args);
            listeners.starting();
    		
            Collection exceptionReporters;
            try {
                ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
                ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
                this.configureIgnoreBeanInfo(environment);
                Banner printedBanner = this.printBanner(environment);
                context = this.createApplicationContext();
                exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
                this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
                this.refreshContext(context);
                this.afterRefresh(context, applicationArguments);
                stopWatch.stop();
                if (this.logStartupInfo) {
                    (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
                }
    
                listeners.started(context);
                this.callRunners(context, applicationArguments);
            } catch (Throwable var10) {
                this.handleRunFailure(context, var10, exceptionReporters, listeners);
                throw new IllegalStateException(var10);
            }
    
            try {
                listeners.running(context);
                return context;
            } catch (Throwable var9) {
                this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
                throw new IllegalStateException(var9);
            }
        }

接下来我们还是逐一分析每个方法。

2.1.1 获取SpringApplicationRunListeners应用运行时监听器
    private SpringApplicationRunListeners getRunListeners(String[] args) {
            Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
            return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
        }
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值