springboot招聘网站源码分享 springboot 项目源码

大家好,今天来为大家分享springboot招聘网站源码分享的一些知识点,和项目源码的问题解析,大家要是都明白,那么可以忽略,如果不太清楚的话可以看看本篇文章,相信很大概率可以解决您的问题,接下来我们就一起来看看吧!

01源码版本

SpringBoot是基于2.4.0。每个版本有些变化,读者尽量和我保持一致,以防源码有些出入。

02从哪入手?

相信很多人尝试读过SpringBoot的源码,但是始终没有找到合适的方法。那是因为你对SpringBoot的各个组件、机制不是很了解,研究起来就像大海捞针。

至于从哪入手不是很简单的问题吗,当然主启动类了,即是标注着@SpringBootApplication注解并且有着main()方法的类,如下一段代码:

@SpringBootApplication\npublicclassAnnotationDemoApplication{\npublicstaticvoidmain(String[]args){\nSpringApplication.run(AnnotationDemoApplication.class,args);\n}\n}

话不多说,DEBUG伺候,别怕,搞它……..

03源码如何切分?

SpringApplication中的静态run()方法并不是一步完成的,最终执行的源码如下:

//org.springframework.context.ConfigurableApplicationContext\npublicstaticConfigurableApplicationContextrun(Class<?>[]primarySources,String[]args){\nreturnnewSpringApplication(primarySources).run(args);\n}

很显然分为两个步骤,分别是创建SpringApplication和执行run()方法,下面将分为这两个部分介绍。

04如何创建SpringApplication?

创建即是new对象了,DEBUG跟进代码,最终执行的SpringApplication构造方法如下图:

如上图中标注的注释,创建过程中用的其实分为这三个阶段,下面将会一一介绍每个阶段做了什么事。

4.1设置应用类型

这个过程非常重要,直接决定了项目的类型,应用类型分为三种,都在WebApplicationType这个枚举类中,如下:

NONE:顾名思义,什么都没有,正常流程走,不额外的启动web容器,比如TomcatSERVLET:基于servlet的web程序,需要启动内嵌的servletweb容器,比如TomcatREACTIVE:基于reactive的web程序,需要启动内嵌reactiveweb容器,作者不是很了解,不便多说。

判断的依据很简单,就是加载对应的类,比如加载了DispatcherServlet等则会判断是Servlet的web程序。源码如下:

staticWebApplicationTypededuceFromClasspath(){\nif(ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS,null)&&!ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS,null)\n&&!ClassUtils.isPresent(JERSEY_INDICATOR_CLASS,null)){\nreturnWebApplicationType.REACTIVE;\n}\nfor(StringclassName:SERVLET_INDICATOR_CLASSES){\nif(!ClassUtils.isPresent(className,null)){\nreturnWebApplicationType.NONE;\n}\n}\nreturnWebApplicationType.SERVLET;\n}\n

这里我引入了spring-boot-starter-web,肯定是Servlet的web程序。

4.2设置初始化器(Initializer)

初始化器ApplicationContextInitializer是个好东西,用于IOC容器刷新之前初始化一些组件,比如ServletContextApplicationContextInitializer

那么如何获取初始化器呢?跟着上图中的代码进入,在SpringApplication中的如下图中的方法:

相对重要的就是第一步获取初始化器的名称了,这个肯定是全类名了,详细源码肯定在loadFactoryNames()方法中了,跟着源码进入,最终调用的是run()方法中,源码如下:

//从spring.factories中获取监听器\nSpringApplicationRunListenerslisteners=getRunListeners(args);\n

跟进getRunListeners()方法,其实还是调用了loadFactoryNames()方法从spring.factories文件中获取值,如下:

org.springframework.boot.SpringApplicationRunListener=\\\norg.springframework.boot.context.event.EventPublishingRunListener\n

最终注入的是EventPublishingRunListener这个实现类,创建实例过程肯定是通过反射了,因此我们看看它的构造方法,如下图:

这个运行监听器内部有一个事件广播器(SimpleApplicationEventMulticaster),主要用来广播特定的事件(SpringApplicationEvent)来触发特定的监听器ApplicationListener

EventPublishingRunListener中的每个方法用来触发SpringApplicationEvent中的不同子类。

(2)如何启动运行监听器?

SpringApplication#run()方法中,源码如下:

//执行starting()方法\nlisteners.starting(bootstrapContext,this.mainApplicationClass);\n

执行SpringApplicationRunListenersstarting()方法,跟进去其实很简单,遍历执行上面获取的运行监听器,这里只有一个EventPublishingRunListener。因此执行的是它的starting()方法,源码如下图:

上述源码中逻辑很简单,其实只是执行了multicastEvent()方法,广播了ApplicationStartingEvent事件。至于multicastEvent()内部方法感兴趣的可以看看,其实就是遍历ApplicationListener的实现类,找到监听ApplicationStartingEvent这个事件的监听器,执行onApplicationEvent()方法。

(3)小结

这一步其实就是广播了ApplicationStartingEvent事件来触发监听这个事件的ApplicationListener

因此如果自定义了ApplicationListener并且监听了ApplicationStartingEvent(应用程序开始启动)事件,则这个监听器将会被触发。

5.2环境构建

这一步主要用于加载系统配置以及用户的自定义配置(application.properties),源码如下,在run()方法中:

ConfigurableEnvironmentenvironment=prepareEnvironment(listeners,bootstrapContext,applicationArguments);\n

prepareEnvironment方法内部广播了ApplicationEnvironmentPreparedEvent事件,源码如下图:

环境构建这一步加载了系统环境配置、用户自定义配置并且广播了ApplicationEnvironmentPreparedEvent事件,触发监听器。

5.3创建IOC容器

源码在run()方法中,如下:

context=createApplicationContext();\n

跟进代码,真正执行的是ApplicationContextFactory方法,如下图:

根据webApplicationType决定创建的类型,很显然,我这里的是servlet,因此创建的是AnnotationConfigServletWebServerApplicationContext

这一步仅仅是创建了IOC容器,未有其他操作。

5.4IOC容器的前置处理

这一步真是精华了,在刷新容器之前做准备,其中有一个非常关键的操作:将启动类注入容器,为后续的自动化配置奠定基础。源码如下:

prepareContext(context,environment,listeners,applicationArguments,printedBanner);\n

prepareContext()源码解析如下图,内容还是挺多的:

从上图可以看出步骤很多,下面将会详细介绍几个重点的内容。

(1)调用初始化器

SpringApplication构建过程中设置的初始化器,从spring.factories取值的。执行的流程很简单,遍历执行,源码如下图:

将自定义的ApplicationContextInitializer放在META-INF/spring.factories中,在此时也是会被调用。

(2)加载启动类,注入容器

这一步是将主启动类加载到IOC容器中,作为后续自动配置的入口。

SpringApplication构建过程中将主启动类放置在primarySources这个集合中,此时的getAllSources()即是从其中取值,如下图:

这里取出的就是主启动类,当然你的项目中可能不止一个,接下来就是将其加载到IOC容器中了,源码如下:

load(context,sources.toArray(newObject[0]));\n

跟着代码进去,其实主要逻辑都在BeanDefinitionLoader.load()方法,如下图:

将主启动类加载到beanDefinitionMap,后续该启动类将作为开启自动配置化的入口,后续章节详细介绍。

(3)两次广播事件

这一步涉及到了两次事件广播,分别是ApplicationContextInitializedEventApplicationPreparedEvent,对应的源码如下:

listeners.contextPrepared(context);\nload(context,sources.toArray(newObject[0]));\n

5.5刷新容器

刷新容器完全是Spring的功能了,比如初始化资源,初始化上下文广播器等,这个就不再详细介绍,有兴趣可以看看Spring的源码。

protectedvoidrefresh(ApplicationContextapplicationContext){\nAssert.isInstanceOf(AbstractApplicationContext.class,applicationContext);\n//调用创建的容器applicationContext中的refresh()方法\n((AbstractApplicationContext)applicationContext).refresh();\n}\npublicvoidrefresh()throwsBeansException,IllegalStateException{\nsynchronized(this.startupShutdownMonitor){\n/**\n*刷新上下文环境\n*/\nprepareRefresh();\n\n/**\n*初始化BeanFactory,解析XML,相当于之前的XmlBeanFactory的操作,\n*/\nConfigurableListableBeanFactorybeanFactory=obtainFreshBeanFactory();\n\n/**\n*为上下文准备BeanFactory,即对BeanFactory的各种功能进行填充,如常用的注解@Autowired@Qualifier等\n*添加ApplicationContextAwareProcessor处理器\n*在依赖注入忽略实现*Aware的接口,如EnvironmentAware、ApplicationEventPublisherAware等\n*注册依赖,如一个bean的属性中含有ApplicationEventPublisher(beanFactory),则会将beanFactory的实例注入进去\n*/\nprepareBeanFactory(beanFactory);\n\ntry{\n/**\n*提供子类覆盖的额外处理,即子类处理自定义的BeanFactoryPostProcess\n*/\npostProcessBeanFactory(beanFactory);\n\n/**\n*激活各种BeanFactory处理器,包括BeanDefinitionRegistryBeanFactoryPostProcessor和普通的BeanFactoryPostProcessor\n*执行对应的postProcessBeanDefinitionRegistry方法和postProcessBeanFactory方法\n*/\ninvokeBeanFactoryPostProcessors(beanFactory);\n\n/**\n*注册拦截Bean创建的Bean处理器,即注册BeanPostProcessor,不是BeanFactoryPostProcessor,注意两者的区别\n*注意,这里仅仅是注册,并不会执行对应的方法,将在bean的实例化时执行对应的方法\n*/\nregisterBeanPostProcessors(beanFactory);\n\n/**\n*初始化上下文中的资源文件,如国际化文件的处理等\n*/\ninitMessageSource();\n\n/**\n*初始化上下文事件广播器,并放入applicatioEventMulticaster,如ApplicationEventPublisher\n*/\ninitApplicationEventMulticaster();\n\n/**\n*给子类扩展初始化其他Bean\n*/\nonRefresh();\n\n/**\n*在所有bean中查找listenerbean,然后注册到广播器中\n*/\nregisterListeners();\n\n/**\n*设置转换器\n*注册一个默认的属性值解析器\n*冻结所有的bean定义,说明注册的bean定义将不能被修改或进一步的处理\n*初始化剩余的非惰性的bean,即初始化非延迟加载的bean\n*/\nfinishBeanFactoryInitialization(beanFactory);\n\n/**\n*通过spring的事件发布机制发布ContextRefreshedEvent事件,以保证对应的监听器做进一步的处理\n*即对那种在spring启动后需要处理的一些类,这些类实现了ApplicationListener<ContextRefreshedEvent>,\n*这里就是要触发这些类的执行(执行onApplicationEvent方法)\n*另外,spring的内置Event有ContextClosedEvent、ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、RequestHandleEvent\n*完成初始化,通知生命周期处理器lifeCycleProcessor刷新过程,同时发出ContextRefreshEvent通知其他人\n*/\nfinishRefresh();\n}\n\nfinally{\n\nresetCommonCaches();\n}\n}\n}\n

5.6IOC容器的后置处理

一个扩展方法,源码如下:

afterRefresh(context,applicationArguments);\n

默认为空,如果有自定义需求可以重写,比如打印一些启动结束日志等。

5.7发出结束执行的事件

同样是EventPublishingRunListener这个监听器,广播ApplicationStartedEvent事件。

但是这里广播事件和前几次不同,并不是广播给SpringApplication中的监听器(在构建过程中从spring.factories文件获取的监听器)。因此在IOC容器中注入的监听器(使用@Component等方式注入的)也能够生效。前面几个事件只有在spring.factories文件中设置的监听器才会生效。

跟着代码进入,可以看到started()方法源码如下:

这里并没有用事件广播器SimpleApplicationEventMulticaster广播事件,而是使用ConfigurableApplicationContext直接在IOC容器中发布事件。

5.8执行Runners

SpringBoot提供了两种Runner让我们定制一些额外的操作,分别是CommandLineRunnerApplicationRunner,关于这两个的区别,后面文章详细介绍。

调用的源码如下:

callRunners(context,applicationArguments);\n

跟进代码,其实真正调执行的是如下方法:

逻辑很简单,从IOC容器中获取,遍历调用。

5.9小结

SpringBoot启动流程相对简单些,作者将其细分了以上八个步骤,希望能够帮助读者理解,流程图如下:

06总结

SpringBoot启动流程就介绍到这里了,需要重点理解run()方法执行的八个步骤以及事件、初始化器、监听器等组件的执行时间点。

作者:爱撒谎的男孩原文链接:https://www.cnblogs.com/Chenjiabing/p/14005575.html

springboot招聘网站源码分享和项目源码的问题分享结束啦,以上的文章解决了您的问题吗?欢迎您下次再来哦!

Published by

风君子

独自遨游何稽首 揭天掀地慰生平