专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

SpringBoot2.2.6启动run方法之createApplicationContext

前言

此系列是针对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图,如下

77_1.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的类图 77_2.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。

文章永久链接:https://tech.souyunku.com/27196

未经允许不得转载:搜云库技术团队 » SpringBoot2.2.6启动run方法之createApplicationContext

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们