上一节咱们提到过,意识一个新技术的时候,通常是从一个入门的HelloWorld开始,之后浏览它的一些入门文档和书籍、视频,从而把握它的根本应用。
这一节我就来带大家从HelloWorld开始,先摸清楚SpringBoot的外围脉络,之后再来逐渐剖析透彻SpringBoot,从而精通它。
从搭建HelloWorld入口开始剖析SpringBoot
首先咱们从官网的文档中搭建出一个2.2.2 版本的SpringBoot,减少了两个starter,mybatis-plus-boot-starter、spring-boot-starter-web,应用Maven进行我的项目和依赖治理,配置一个本地的mysql。置信这个对你们来说,都比较简单,我就不一一进行赘述了。
通过下面的根本搭建,你就会有相似一个上面的一个SpringBoot HelloWorld级别 的入口。
package org.mfm.learn.springboot;import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@MapperScan("org.mfm.learn.springboot.mapper")@SpringBootApplicationpublic class LearnSpringBootApplication { public static void main(String[] args) { SpringApplication.run(LearnSpringBootApplication.class, args); }}
通过上一节你晓得SpringBoot定义了一个SpringApplication的web利用启动流程,入口通过一个java -jar的命令,执行main函数启动一个JVM过程,运行外部的tomcat监听一个默认8080的端口,提供web服务。
整个过程中第一个最要害的就是SpringBoot定义的SpringApplication,咱们一起先来看下它是怎么创立new的。
SpringApplication的创立时外围组件图
SpringApplication的创立时的代码剖析
在下面的示例代码中,main办法执行了 SpringApplication的run办法,如下:
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class<?>[] { primarySource }, args);}public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args);}
run办法外围入参就是main函数所在类+main函数args的参数,之后就间接创立了一个SpringApplication对象。让咱们一起来看看这个SpringBoot定义的概念怎么创立的,创立时的外围组件又有哪些呢?
public SpringApplication(Class<?>... primarySources) { this(null, primarySources);}public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); this.webApplicationType = WebApplicationType.deduceFromClasspath(); setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass();}
SpringApplication的创立时外围脉络
SpringApplication的创立外围脉络比较简单:
1)ResourceLoader 指明资源加载器,这个临时不分明是啥货色,默认是个null。
2)webApplicationType 推断以后web利用类型,通过一个deduceFromClasspath办法推断出的。
3)之后设置了setInitializers、setListeners两个列表,别离是一堆Initializer和Listener,都是通过getSpringFactoriesInstances办法获取的。
4)通过primarySources、mainApplicationClass记录了启动次要资源类,也就是之前HelloWorld中的LearnSpringBootApplication.class。
下面是我第一次看这个类的一个脉络后,脑中失去的后果。
你第一次看这里,必定什么都不分明,不晓得每个变量有什么用,是干嘛的,没关系的,第一次,你只有相熟它的脉络就能够。晓得这里设置了两个汇合变量Initializer和Listener,能够设置esourceLoader ,标记了一些类型和类,这就够了。
之后你有工夫,再挨个去理解每个变量或者组件的作用就能够了,这个不还是先脉络后细节的思维,是吧?
SpringApplication的创立时的细节剖析
你能够缓缓拆解下面的每一步,独自看看每一个组件大体是作什么的,这个就是细节的钻研,能够一步一步来。
你能够钻研下ResourceLoader 是个啥? 你能够看它的类注解后能够发现,这个ResourceLoader 类负责应用ClassLoader加载ClassPath下的class和各种配置文件的。(如果你不晓得JVM的ClassLoader机制,次要加载什么,能够本人去baidu、google理解下)。这里你能够进一步思考下,它设计成了一个接口,能够实现不同的类加载器来加载资源。
webApplicationType 如何被推断的?就是依据几个动态变量定义的类全限定名称,依据classPath下是否存在对应的类,来推断出类型,应用了web-starter。默认推断出为Servlet类型的利用。
至于primarySources、mainApplicationClass这个两个变量记录了LearnSpringBootApplication.class, 大体是为了之后扫描主动配置等思考的,示意从什么包名的哪一个类下启动的。
最初两个汇合变量Initializer和Listener如何设置的,这块比拟值得钻研下。
基本原理是通过ClassLoader扫描了classPath下所有META-INF/spring.factories这个目录中的文件,通过指定的factoryType,也就是接口名称,获取对应的所有实现类,并且实例化成对象,返回成一个list列表。
比方factoryType=ApplicationContextInitializer 就返回这个接口在META-INF/spring.factories定义的所有的实现类,并实例化为一个列表List ApplicationContextInitializer 。
ApplicationListener同理,获取到了List ApplicationListener一个汇合。
这外面其实有很多细节,应用了类加载器、缓存机制,反射机制等,有趣味的同学能够认真钻研下。
这里以咱们抓大放小思维,概括成一句话:通过工具办法通过classLoader获取classPath指定地位某个接口所有实现类的实例对象列表。
这里获取的是ApplicationContextInitializer、ApplicationListener这两个接口的实例对象列表。
细节中能够学到常识,脉络中一样能够学到常识,这个思维你肯定要缓缓有。抓大放小的意思,更多的是让你晓得重点和关键点,而不是让你抛弃细节,这两者并不抵触,这个肯定要留神。
最初这里细节剖析,画一个简略组件图小结下:
SpringApplication Run办法的脉络剖析
相熟了SpringApplication 的创立,接着咱们该剖析它的run办法了。
其实之前一节,咱们介绍过SpringApplication 的启动流程。就是高度概括了run办法的外围脉络,run办法的外围其实外围就是下图蓝色的局部:
run办法脉络能够次要概括为:
1)主动拆卸配置
2)Spring容器的创立
3)web容器启动(Tomcat的启动)
然而在run办法的执行过程,必定不会这么简略,过程中还掺杂了很多杂七杂八的逻辑,其中有意思的扩大点,也有值得吐槽的坑。这是每个框架都会有的劣势劣势吧。咱们先大体摸一下run办法的脉络,给大家介绍几个术语,不然之后可能会看不懂代码细节。
SpringApplication Run办法的脉络进一步剖析
要想进一步剖析run办法的脉络,首先须要相熟几个术语,就有点像DDD的通用语言似的,懂了这些语言,了解SpringBoot和Spring才会更得心应手。
术语遍及Context/BeanFactory/Environment
ConfigurableApplicationContext,容器通常称为ApplicationContext或者BeanFactory,context也简称为容器。ApplicationContext包装了BeanFactory,封装更高级的API而已。
ConfigurableEnvironment ,是配置文件的形象,无关什么properties或者yml等配置文件的key-value值,都会封装成这个类的某个实现类。
相熟了这些术语后,咱们看一起看下SpringApplication 的run办法代码。
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); 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; }
下面的代码,次要就是执行了一堆办法,能够从办法名字看出,都是围绕Context、Environment这些术语。也就是围绕容器和配置文件组织的逻辑。
在整个Spring容器的创立,刷新,刷新之后交叉了很多逻辑。
另外,SpringBoot整个run办法中有几个很要害扩大点,设计SpringApplicationRunListeners、Runners等扩大入口。容器创立、刷新等也要各自的扩大点,对容器的加强扩大,如beanFactoryPostProcessor,对Bean的减少扩大,如beanPostProcessor。然而这些都是后话了。
我间接用一张图给大家概括了,下面run办法脉络:
(*彩色是直观的看进去的扩大逻辑,红色是run办法每个办法的字面了解,只是每一步有很多扩大点和做的事件比拟多,让你感觉会有点云里雾里的。蓝色局部,概括了外围逻辑。也就是SpringBoot启动,说白了咱们外围就是要找到这三点:主动拆卸配置、Spring容器的创立、web容器启动。)
这时候你肯定要学会抓大放小的思维,之后带着这3个关键步骤,去了解SpringBoot,其余的实现能够独自来钻研剖析它的设计思路,比方各个扩大点的设计是如何思考的,咱们能够参考借鉴哪一些。这才是学习SpringBoot最最该学习的。
概括下就是,当一个技术看着比较复杂时,你应该顺着外围脉络了解原理,学习各个细节的亮点设计思维。不要陷入某一个细节,多思考才最重要。大家肯定要记住这一点,在后续的成长记中,我会逐渐带大家体验这一点的。
小结
好了,简略小结下。
次要思维学习了:
1)先脉络后细节的思维,抓大放小的思维,排除不重要的,剖析最次要的。
2)细节中能够学到常识,脉络中一样能够学到常识,这个思维你肯定要缓缓有。抓大放小的意思,更多的是让你晓得重点和关键点,而不是让你抛弃细节,这两者并不抵触,这个肯定要留神。
3)多思考才最重要。顺着外围脉络了解原理,学习各个细节的亮点设计思维,千万不能陷入常识自身。
次要常识学习了:
明天咱们次要看了下SpringApplication的创立,它的外围组件有哪些,创立后执行的run办法,到底做了些什么,脉络是怎么样的。
相熟了这些脉络,剩下的就简略了,逐渐剖析每个细节,看看每个细节有些值得咱们学习的点,又有哪一些不太适宜的点。
咱们下期再见!
本文由博客一文多发平台 OpenWrite 公布!