springboot启动解析六

此篇文章继续解析SpringApplication的run方法具体代码如下:

public ConfigurableApplicationContext run(String... args) {
        1.StopWatch stopWatch = new StopWatch();
        2.stopWatch.start();
        3.ConfigurableApplicationContext context = null;
        4.Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        5.configureHeadlessProperty();
        6.SpringApplicationRunListeners listeners = getRunListeners(args);
        7.listeners.starting();
        try {
        8.  ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                    args);
        9.  ConfigurableEnvironment environment = prepareEnvironment(listeners,
                    applicationArguments);
        10. configureIgnoreBeanInfo(environment);
        11. Banner printedBanner = printBanner(environment);
        12. context = createApplicationContext();
        13. exceptionReporters = getSpringFactoriesInstances(
                SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
        14.     prepareContext(context, environment, listeners, applicationArguments,
                    printedBanner);
        15. refreshContext(context);
        16. afterRefresh(context, applicationArguments);
        17. stopWatch.stop();
        18. if (this.logStartupInfo) {
        19.     new StartupInfoLogger(this.mainApplicationClass)
                        .logStarted(getApplicationLog(), stopWatch);
            }
        20. listeners.started(context);
        21. callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
        22. handleRunFailure(context, ex, exceptionReporters, listeners);
            throw new IllegalStateException(ex);
        }

        try {
        23. listeners.running(context);
        }
        catch (Throwable ex) {
        24. handleRunFailure(context, ex, exceptionReporters, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }

一共24行代码,本章主要解析15小节,其就是真正的把spring容器初始化的方法,基本上把spring容器的大部分功能都包含在内

    private void refreshContext(ConfigurableApplicationContext context) {
        refresh(context);
        if (this.registerShutdownHook) {
            try {
                context.registerShutdownHook();
            }
            catch (AccessControlException ex) {
                // Not allowed in some environments.
            }
        }
    }
最终注册方法就是这个doClose
protected void doClose() {
        if (this.active.get() && this.closed.compareAndSet(false, true)) {
            if (logger.isInfoEnabled()) {
                logger.info("Closing " + this);
            }
卸载当前的spring容器,包含卸载该spring容器相关的MBeanServer
            LiveBeansView.unregisterApplicationContext(this);

            try {
    发布关闭事件
                publishEvent(new ContextClosedEvent(this));
            }
            catch (Throwable ex) {
                logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
            }

            停止所有实现SmartLifecycle或者Lifecycle的bean,lifecycleProcessor 就是
负责这些bean的声明周期
            if (this.lifecycleProcessor != null) {
                try {
                    this.lifecycleProcessor.onClose();
                }
                catch (Throwable ex) {
                    logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
                }
            }

        摧毁容器中的所有单例bean
            destroyBeans();

        关闭bean工厂
            closeBeanFactory();

        关闭我们内置的tomcat的容器
            onClose();
设置未激活标识
            this.active.set(false);
        }

refresh(context);的详细代码如下:

    @Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
        清除ClassPathBeanDefinitionScanner的缓存
            设置servletContext和servletConfig,如果这个两个属性不为空则替换
environment的source中关于这个两个的属性
校验必须存在的properties是否存在(我们可以设置哪些properties)
设置earlyApplicationEvents,存储早期的ApplicationEvents,当所有的ApplicationContextListener都注册进入容器之后在执行这些事件

            prepareRefresh();

        获取beanFactory,还给其设置了refreshed标识
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

准备beanFactory,在注册bean之前先给beanFactory注册一堆bean,这些bean可以
帮助beanFactory后期去解析,注册其他的bean,比如忽略依赖,比如指定依赖
ignoreDependencyInterface:当我们spring指定自动注入的时候会忽略对这个指定参数的EnvironmentAware的注入
registerResolvableDependency:当我们spring指定自动注入的时候会只注入我们指定的参数属性
            prepareBeanFactory(beanFactory);

            try {
        设置ignoreDependencyInterface(ServletContextAware.class),标识对于ServletContextAware
的实现类中的ServletContextAware属性禁止自动注入
如果存在basePackages和annotatedClasses,则扫描basePackages,并把扫描到的class和annotatedClasses注册到spring容器中
                postProcessBeanFactory(beanFactory);

            执行beanFactory的PostProcessors
按照这个顺序PriorityOrdered,Ordered,nonOrdered 
分别执行BeanDefinitionRegistry的postProcessBeanDefinitionRegistry,
BeanFactoryPostProcessor的postProcessBeanFactory

alreadyCreated:这个集合存储已经初始化好的bean实例,
所以当我们执行完beanFactoryPostProcessor的方法时候 会清除未初始化的bean
也会清除allBeanNamesByType和singletonBeanNamesByType

                invokeBeanFactoryPostProcessors(beanFactory);

注册beanPostProcessor(会确保spring的internalPostProcessors)一定是排在beanPostProcessor的集合最后
                registerBeanPostProcessors(beanFactory);

    给spring的容器和其父容器设置messageSource
                initMessageSource();

        给spring的容器注册一个事件广播器applicationEventMulticaster
                initApplicationEventMulticaster();

        初始化tomcat
                onRefresh();

        注册所有的ApplicationListener,并且执行之前积攒的earlyApplicationEvents
                registerListeners();

            初始化所有非懒加载的单例bean
                finishBeanFactoryInitialization(beanFactory);

清理resource的缓存,初始化LifecycleProcessor用来执行
lifeCycle的bean的onRefresh,
发布ContextRefreshedEvent,将该spring容器注册到
LiveBeansView,启动tomcat发布tomcat启动事件
                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();
            }
        }
    }

每行代码的意思都有英文解释,我们一行行来分析
prepareRefresh();的详细代码如下:

    protected void prepareRefresh() {
        this.scanner.clearCache();
        super.prepareRefresh();
    }

    protected void prepareRefresh() {
        this.startupDate = System.currentTimeMillis();
        this.closed.set(false);
        this.active.set(true);

        if (logger.isInfoEnabled()) {
            logger.info("Refreshing " + this);
        }

        // Initialize any placeholder property sources in the context environment
        initPropertySources();

        // Validate that all properties marked as required are resolvable
        // see ConfigurablePropertyResolver#setRequiredProperties
        getEnvironment().validateRequiredProperties();

        // Allow for the collection of early ApplicationEvents,
        // to be published once the multicaster is available...
        this.earlyApplicationEvents = new LinkedHashSet<>();
    }

第一步主要是父类AbstractApplicationContext的refresh方法(因为其子类AnnotationConfigServletWebServerApplicationContext 没有实现该方法)
refresh:

  • 该方法第一步调用子类的 prepareRefresh()方法
  • 子类的prepareRefresh方法清除CachingMetadataReaderFactory中的缓存然后继续调用父类的prepareRefresh方法
    prepareRefresh:
  • 设置相关属性 校验environment的properties是否正确
  • initPropertySources会调用GenericWebApplicationContext的实现 就是设置容器的环境初始化容器的属性
  • earlyApplicationEvents 是在multicaster 可用的时候进行发布
    obtainFreshBeanFactory()的代码如下:
    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        refreshBeanFactory();
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (logger.isDebugEnabled()) {
            logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
        }
        return beanFactory;
    }

    protected final void refreshBeanFactory() throws IllegalStateException {
        if (!this.refreshed.compareAndSet(false, true)) {
            throw new IllegalStateException(
                    "GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");
        }
        this.beanFactory.setSerializationId(getId());
    }

    public final ConfigurableListableBeanFactory getBeanFactory() {
        return this.beanFactory;
    }
  • 首先ConfigurableListableBeanFactory 是在初始化容器的时候其父类容器也被初始化了才赋值的
  • spring容器是通过ConfigurableListableBeanFactory 这个bean工厂进行真正的bean加载
  • 然后就是首先查看beanFactory是否已经refresh过,如果是 就给容器一个id 同时返回容器
    prepareBeanFactory(beanFactory);的详细代码如下:
    protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        // Tell the internal bean factory to use the context's class loader etc.
        beanFactory.setBeanClassLoader(getClassLoader());
        beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
        beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

        // Configure the bean factory with context callbacks.
        beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
        beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
        beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
        beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
        beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
        beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
        beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

        // BeanFactory interface not registered as resolvable type in a plain factory.
        // MessageSource registered (and found for autowiring) as a bean.
        beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
        beanFactory.registerResolvableDependency(ResourceLoader.class, this);
        beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
        beanFactory.registerResolvableDependency(ApplicationContext.class, this);

        // Register early post-processor for detecting inner beans as ApplicationListeners.
        beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

        // Detect a LoadTimeWeaver and prepare for weaving, if found.
        if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
            beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
            // Set a temporary ClassLoader for type matching.
            beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
        }

        // Register default environment beans.
        if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
            beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
        }
        if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
            beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
        }
        if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
            beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
        }
    }
  • 上述代码的作用如下:
  • 设置beanFactory的classloader,一般都是线程上下文类加载器
  • 设置beanFactory的BeanExpressionResolver,用来根据bean的表达式来解析bean
  • 增加ResourceEditorRegistrar,主要是注册用户或者系统定义的属性编辑器
  • 添加BeanPostProcessor-ApplicationContextAwareProcessor
    1.BeanPostProcessor 提供了2个方法
    postProcessBeforeInitialization(该方法在调用bean的初始化方法(注意是初始化方法此时bean早已经实例化好了))
    postProcessAfterInitialization(该方法是在调用bean的初始化方法后进行调用)
    还需注意的是@service @component 都没有初始化方法 但是@Bean注解有
    还有需要注意的是若是上述2中方法对bean 进行修改那么原始的bean就会被替换
    而ApplicationContextAwareProcessor则是只修改postProcessBeforeInitialization,
    方法内部就是判断 bean是否属于
    EnvironmentAware
    EmbeddedValueResolverAware
    ResourceLoaderAware
    ApplicationEventPublisherAware
    MessageSourceAware
    ApplicationContextAware
    不同的aware调用不同的方法
    代码细节如下:
    private void invokeAwareInterfaces(Object bean) {
        if (bean instanceof Aware) {
            if (bean instanceof EnvironmentAware) {
                ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
            }
            if (bean instanceof EmbeddedValueResolverAware) {
                ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
            }
            if (bean instanceof ResourceLoaderAware) {
                ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
            }
            if (bean instanceof ApplicationEventPublisherAware) {
                ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
            }
            if (bean instanceof MessageSourceAware) {
                ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
            }
            if (bean instanceof ApplicationContextAware) {
                ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
            }
        }
    }

而ignoreDependencyInterface 则是正好对应着上述的
EnvironmentAware
EmbeddedValueResolverAware
ResourceLoaderAware
ApplicationEventPublisherAware
MessageSourceAware
ApplicationContextAware
--ignoreDependencyInterface的真正意思是在自动装配时忽略指定接口的实现类中,对外的依赖。
如果想忽略指定类的自动注入使用ignoreDependencyType

  • autowiring特定指的是通过beans标签default-autowire 即@Bean注解

,发现英语中的autowiring特定指的是通过beans标签default-autowire属性来依赖注入的方式,而不是指使用@Autowired注解进行的依赖注入。区别在于,使用default-autowire会自动给所有的类都会从容器中查找匹配的依赖并注入,而使用@Autowired注解只会给这些注解的对象从容器查找依赖并注入。

  • 所谓的自动装配是指我们配置ben的xml 或者@Bean的时候 会自动帮我们注入属性(比如我们配置A类的B属性=3),那么容器就会给我们生成一个属性为3的A的实例对象
    如果上述几个Aware不使用ignoreDependencyInterface会有什么问题尼,很简单 如果我们写一个实现类,然后给他默认配置ApplicationContext属性,而这个ApplicationContext是我们自己new的,这就导致了我们使用的不是spring自己生成的ApplicationContext,所以spring为了避免我们手误自己注册下这个属性,而帮我们忽略。
    而registerResolvableDependency意思更简单了
    beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    上述代码的意思就是当我们使用自动注入比如@AutoWired注入BeanFactory 类型的那么他会帮我们注入我们目前输入的实例对象,而不会去注入他的其他实现类,这是为了避免我们使用其他的自定义的实例对象
    ApplicationListenerDetector是检测ApplicationListener的实现类是否是单例
    // Detect a LoadTimeWeaver and prepare for weaving, if found.
        if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
            beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
            // Set a temporary ClassLoader for type matching.
            beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
        }

上述检测是否存在代码织入,简介如下抄袭别人博客的定义:

在Java 语言中,从织入切面的方式上来看,存在三种织入方式:编译期织入、类加载期织入和运行期织入。编译期织入是指在Java编译期,采用特殊的编译器,将切面织入到Java类中;而类加载期织入则指通过特殊的类加载器,在类字节码加载到JVM时,织入切面;运行期织入则是采用CGLib工具或JDK动态代理进行切面的织入。

AspectJ采用编译期织入和类加载期织入的方式织入切面,是语言级的AOP实现,提供了完备的AOP支持。它用AspectJ语言定义切面,在编译期或类加载期将切面织入到Java类中。

AspectJ提供了两种切面织入方式,第一种通过特殊编译器,在编译期,将AspectJ语言编写的切面类织入到Java类中,可以通过一个Ant或Maven任务来完成这个操作;第二种方式是类加载期织入,也简称为LTW(Load Time Weaving)

如何使用Load Time Weaving?首先,需要通过JVM的-javaagent参数设置LTW的织入器类包,以代理JVM默认的类加载器;第二,LTW织入器需要一个 aop.xml文件,在该文件中指定切面类和需要进行切面织入的目标类。

本人对上述LTW似懂非懂
下面的代码就没什么好讲的了,就是检测某个属性是否存在,然后注册单例bean

if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
            beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
        }
        if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
            beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
        }
        if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
            beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
        }

refresh中的postProcessBeanFactory 等以下代码留着明天分享

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351

推荐阅读更多精彩内容

  • 本文是我自己在秋招复习时的读书笔记,整理的知识点,也是为了防止忘记,尊重劳动成果,转载注明出处哦!如果你也喜欢,那...
    波波波先森阅读 12,285评论 6 86
  • 1.1 spring IoC容器和beans的简介 Spring 框架的最核心基础的功能是IoC(控制反转)容器,...
    simoscode阅读 6,706评论 2 22
  • 1.1 Spring IoC容器和bean简介 本章介绍了Spring Framework实现的控制反转(IoC)...
    起名真是难阅读 2,578评论 0 8
  • I:要利用时间来养成习惯,这个“时间”的选择就非常的重要,一定要选择那些不被打扰的时间。不被打扰的时间可以选择早上...
    菜255阅读 159评论 0 0
  • 早教和早教机构 很多妈妈会陷入要不要参加早教培训而焦虑其实早教是一种融入生活的理念模式,而早教机构只是一个场所。当...
    陪娃党阅读 202评论 0 0