• springboot web


    接上一篇

    在创建 SpringApplication 之后, 调用了 run() 方法.

    public ConfigurableApplicationContext run(String... args) {
      //定时器, 监控启动时间
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        //java.awt.headless是一种模式用于在缺少显示屏、键盘或者鼠标时的系统配置,很多监控工具如jconsole 需要将该值设置为true,系统变量默认为true
        configureHeadlessProperty();
        //从 spring.factories 配置中获取监听器
        SpringApplicationRunListeners listeners = getRunListeners(args);
       //启动监听器
        listeners.starting();
        try {
         //对 args 进行封装, 此处args 是 {}
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                    args);
            //构造容器环境
            ConfigurableEnvironment environment = prepareEnvironment(listeners,
                    applicationArguments);
            //设置需要忽略的bean
            configureIgnoreBeanInfo(environment);
            //打印banner
            Banner printedBanner = printBanner(environment);
            //创建容器, 此处创建的是 AnnotationConfigServletWebServerApplicationContext
            context = createApplicationContext();
            //实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
            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);
            }
         //遍历调用监听器的started()方法, 发布 ApplicationStartedEvent 事件 listeners.started(context); callRunners(context, applicationArguments); }
    catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try {
         //遍历调用监听器的 running() 方法, 发布 ApplicationReadyEvent 事件 listeners.running(context); }
    catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; }

    configureHeadlessProperty()

    private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";
    private boolean headless = true;
    
    private void configureHeadlessProperty() {
        System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty(
                SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
    }

    这里会默认设置为 true.  

    getRunListeners()

    private SpringApplicationRunListeners getRunListeners(String[] args) {
        Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
        return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
                SpringApplicationRunListener.class, types, this, args));
    }

    这里是获取配置的监听器, 并封装到  SpringApplicationRunListeners 了中的 this.liseners 属性中.

    这里获取到的是: org.springframework.boot.context.event.EventPublishingRunListener

    listeners 这块内容比较多, 放在后面去详细解析

    prepareEnvironment()

    private ConfigurableEnvironment prepareEnvironment(
            SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments) {
        //根据webApplicationType, 创建运行环境, 此处创建的是 StandardServletEnviroment
        ConfigurableEnvironment environment = getOrCreateEnvironment();
       //配置环境
        configureEnvironment(environment, applicationArguments.getSourceArgs());
       //给监听器设置环境,遍历监听器,调用其 enviromentParepared() 方法,发布环境已准备事件
        listeners.environmentPrepared(environment);
        bindToSpringApplication(environment);
        if (this.webApplicationType == WebApplicationType.NONE) {
            environment = new EnvironmentConverter(getClassLoader())
                    .convertToStandardEnvironmentIfNecessary(environment);
        }
        ConfigurationPropertySources.attach(environment);
        return environment;
    }

    configureIgnoreBeanInfo()

    public static final String IGNORE_BEANINFO_PROPERTY_NAME = "spring.beaninfo.ignore";
    
    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 为 true

    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 ,  在 application.yml 中, 加入配置

    spring:
        main:
            banner-mode: "off"
    可以关闭 banner 打印, 当然这里也可以自定义自己的 banner . 可有可无的功能, 不去管它. 打印就打印吧. 
     

      

    createApplicationContext()

    public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot."
                + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
                
    public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
                + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
    
    public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
                + "annotation.AnnotationConfigApplicationContext";
    
    protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                switch (this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName(DEFAULT_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);
    }

    根据环境类型, 创建 ApplicationContext 对象 :  AnnotationConfigServletWebServerApplicationContext

     

    prepareContext()

    private void prepareContext(ConfigurableApplicationContext context,
            ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments, Banner printedBanner) {
        context.setEnvironment(environment);
        //容器的后置处理
        postProcessApplicationContext(context);
        //对this.initializers中存放的类进行初始化操作, 调用其initialize()方法
        applyInitializers(context);
        //遍历监听器, 调用其 contextPrepared() 方法, 进行容器准备好的操作, 此处为空操作, 留给子类重写扩展
        listeners.contextPrepared(context);
        //日志打印
        if (this.logStartupInfo) {
            logStartupInfo(context.getParent() == null);
            logStartupProfileInfo(context);
        }
    
        // Add boot specific singleton beans
        context.getBeanFactory().registerSingleton("springApplicationArguments",
                applicationArguments);
        if (printedBanner != null) {
            context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
        }
    
        //拿this.primarySources + this.sources, 此处拿到的是SbmvcApplication
        Set<Object> sources = getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        //将SbmvcApplication注册到容器里
        load(context, sources.toArray(new Object[0]));
        //遍历监听器, 调用其contextLoaded()方法, 进行容器加载后的操作
        listeners.contextLoaded(context);
    }
     

    refreshContext()

    private void refreshContext(ConfigurableApplicationContext context) {
        refresh(context);
        if (this.registerShutdownHook) {
            try {
                context.registerShutdownHook();
            }
            catch (AccessControlException ex) {
                // Not allowed in some environments.
            }
        }
    }
    
    protected void refresh(ApplicationContext applicationContext) {
        Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
        ((AbstractApplicationContext) applicationContext).refresh();
    }
    
    @Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();
    
            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);
    
            try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);
    
                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);
    
                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);
    
                // Initialize message source for this context.
                initMessageSource();
    
                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();
    
                // Initialize other special beans in specific context subclasses.
                onRefresh();
    
                // Check for listener beans and register them.
                registerListeners();
    
                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);
    
                // Last step: publish corresponding event.
                finishRefresh();
            }
    
            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }
    
                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();
    
                // Reset 'active' flag.
                cancelRefresh(ex);
    
                // Propagate exception to caller.
                throw ex;
            }
    
            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
            }
        }
    }

    这里执行的内容非常多, 不在此解析了.

    afterRefresh()

    protected void afterRefresh(ConfigurableApplicationContext context,
            ApplicationArguments args) {
    }

    这里是一个空方法, 可以留个子类重写, 进行扩展操作

    callRunners()

    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);
        for (Object runner : new LinkedHashSet<>(runners)) {
            if (runner instanceof ApplicationRunner) {
                callRunner((ApplicationRunner) runner, args);
            }
            if (runner instanceof CommandLineRunner) {
                callRunner((CommandLineRunner) runner, args);
            }
        }
    }

    执行到这里是, 容器里面还没有哪两个类, 所以 runners.size() = 0. 没有执行什么逻辑.

    总结:

    run() 方法主要进行了以下几个操作

    1. 获取并启动了监听器, 发布了 容器启动事件(ApplicationStartingEvent )

    2. 构造容器环境

    3. 创建容器

    4. 实例化 SpringBootExceptionReporter , 用来支持报告关于启动的错误

    5. 准备容器

    6. 刷新容器

    7. 执行刷新完容器的操作(暂时为空操作)

    8. 发布了 容器启动完成事件(ApplicationStartedEvent)

    9. 发布了 容器已准备好事件(ApplicationReadyEvent), 如果执行失败, 则会发布容器失败事件(ApplicationFailedEvent)

  • 相关阅读:
    为 DropDownList 选项添加背景或样式
    杂七杂八——Name与x:Name的关系
    原创Godaddy帐户取消信用卡或PayPla绑定,防止无端扣费[图文教程]
    dota中名词解释
    在 JavaScript 中如何创建多行字符串(JavaScript Multiline String)
    HDU 4035 Maze(概率DP)
    获取想要得到的风格设计属性描述————Resources.Theme类函数public TypedArray obtainStyledAttributes()
    Android 异步更新UI —— Handler 【复杂的数据操作另起新线程然后在当前UI线程更新结果】
    超链接中文乱码问题
    java中的final变量
  • 原文地址:https://www.cnblogs.com/elvinle/p/12341853.html
Copyright © 2020-2023  润新知