美文网首页
SpringBoot2.2.6启动run方法之createApp

SpringBoot2.2.6启动run方法之createApp

作者: 噼咔丘 | 来源:发表于2020-05-28 11:12 被阅读0次

前言

此系列是针对springboot的启动,旨在于和大家一起来看看springboot启动的过程中到底做了一些什么事。文中有不清楚或错误的地方
欢迎留言指正。

源码解读进度

首先我们的源码阅读进度

public ConfigurableApplicationContext run(String... args) {
    // 用于记录启动时间
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    // 声明Spring上下文
    ConfigurableApplicationContext context = null;
    // 声明启动错误回掉
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    // 设置jdk系统属性java.awt.headless,默认情况为true即开启
    configureHeadlessProperty();
    // 装饰者模式创建启动监听器(EventPublishingRunListener实例)
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 触发ApplicationStartingEvent事件,包括转换器的初始化
    listeners.starting();
    try {
        // 参数封装,也就是在命令行下启动应用带的参数
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        // 准备环境:1、加载外部化配置的资源到environment;2、触发ApplicationEnvironmentPreparedEvent事件
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        // 1. 配置spring.beaninfo.ignore,并添加到名叫systemProperties的PropertySource中;默认为true即开启
        configureIgnoreBeanInfo(environment);
        // 2. 打印banner图
        Banner printedBanner = printBanner(environment);
        // 3. 创建应用上下文,这是本文重点
        context = createApplicationContext();
        exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        refreshContext(context);
        afterRefresh(context, applicationArguments);
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
        }
        listeners.started(context);
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }
    try {
        listeners.running(context);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    return context;
}

1. configureIgnoreBeanInfo

看一下该方法的代码

private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
    if (System.getProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) {
        Boolean ignore = environment.getProperty("spring.beaninfo.ignore", Boolean.class, Boolean.TRUE);
        System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME, ignore.toString());
    }
}

配置spring.beaninfo.ignore,并添加到名叫systemProperties的PropertySource中,默认为true即开启,
如上图所示。至于spring.beaninfo.ignore配置这个有什么用,暂时不清楚。

2. printBanner

private Banner printBanner(ConfigurableEnvironment environment) {
    if (this.bannerMode == Banner.Mode.OFF) {
        return null;
    }
    ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
            : new DefaultResourceLoader(getClassLoader());
    SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
    if (this.bannerMode == Mode.LOG) {
        return bannerPrinter.print(environment, this.mainApplicationClass, logger);
    }
    return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}

打印banner图,如下


20200526182627906_8326.png

并返回Banner对象,后面会用到

3. createApplicationContext本文重点

  • 先看代码
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);
}

通过webApplicationType判断创建什么类型的Context,如果是SERVLET那么实例化
AnnotationConfigServletWebServerApplicationContext,利用反射调用无参构造器进行实例化。

  • 下面看一下AnnotationConfigServletWebServerApplicationContext的类图


    20200526182902487_15759.png

打眼一看继承关系很复杂,所以不要试图直接理清他们的关系。我们从具体的类单个分析,分析完之后会感觉就会感觉清晰了。

  1. EnvironmentCapable 接口表示一个组件包含并且暴露一个Environment引用,ApplicationContext继承了EnvironmentCapable,因此暴露了getEnvironment()方法; 然而,ConfigurableApplicationContext 重新定义了getEnvironment()方法,返回ConfigurableEnvironment既而缩小了方法签名。影响就是Environment对象是只读的直到其从ConfigurableApplicationContext中获得,在此时它可以被配制。
public interface EnvironmentCapable {
   Environment getEnvironment();
}
  1. MessageSource 用于解析消息的策略接口,支持此类消息的参数化和国际化, 提供了三个方法
public interface MessageSource {
    @Nullable
    String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale);
    String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException;
    String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;
}
  1. ApplicationEventPublisher 封装事件发布功能的接口,提供了两个方法
@FunctionalInterface
public interface ApplicationEventPublisher {
   default void publishEvent(ApplicationEvent event) {
       publishEvent((Object) event);
   }
   void publishEvent(Object event);
}
  1. ResourcePatternResolver 解析为Resource对象的策略接口,实现类PathMatchingResourcePatternResolver
    可以解析包括jar包里面的配置,也可以解析包含通配符的路径(*、?、{})
public interface ResourcePatternResolver extends ResourceLoader {
   String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
   Resource[] getResources(String locationPattern) throws IOException;
}
  1. ResourceLoader提供 classpath下单资源文件的载入,实现类DefaultResourceLoader
public interface ResourceLoader {
   String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
   Resource getResource(String location);
   @Nullable
   ClassLoader getClassLoader();
}
  1. BeanFactory 是一个Bean工厂方法接口,里面提供了一些对Bean操作的方法
  2. HierarchicalBeanFactory 声明了一个getParentBeanFactory方法
  3. ListableBeanFactory 里面声明了一些有关BeanDefinition的方法
    再看一下AnnotationConfigServletWebServerApplicationContext的构造方法
public AnnotationConfigServletWebServerApplicationContext(DefaultListableBeanFactory beanFactory) {
   super(beanFactory);
   this.reader = new AnnotatedBeanDefinitionReader(this);
   this.scanner = new ClassPathBeanDefinitionScanner(this);
}
  1. AbstractApplicationContext,抽象应用上下文。初始化属性resourcePatternResolver,也就是资源模式解析器;实际类型是PathMatchingResourcePatternResolver
  2. GenericApplicationContext,通用应用上下文。初始化属性beanFactory,其类型是DefaultListableBeanFactory
  3. GenericWebApplicationContext,通用web应用上下文,在GenericApplicationContext基础上增加web支持
  4. ServletWebServerApplicationContext,servlet web服务应用上下文,能够从自身引导,创建,初始化和运行WebServer
    通过这些接口,可以了解到ApplicationContext的基本功能
  • 看一下AnnotationConfigServletWebServerApplicationContext通过构造方法,
public AnnotationConfigServletWebServerApplicationContext() {
   this.reader = new AnnotatedBeanDefinitionReader(this);
   this.scanner = new ClassPathBeanDefinitionScanner(this);
}

可以看到初始化了AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner用于BeanDefinition注册
父类GenericApplicationContext的构造器中初始化了beanFactory,beanFactory的实现是DefaultListableBeanFactory。

相关文章

网友评论

      本文标题:SpringBoot2.2.6启动run方法之createApp

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