当前位置: 首页 > news >正文

建行信用卡网站登录南宁网站建设设计

建行信用卡网站登录,南宁网站建设设计,友情链接平台网站,做微信封面模板下载网站今天,聊聊SpringBoot的源码,本博客聊的版本为v2.0.3.RELEASE。目前SpringBoot的最新版为v3.3.2,可能目前有些公司使用的SpringBoot版本高于我这个版本。但是没关系,因为版本越新,新增的功能越多,反而对Spri…

        今天,聊聊SpringBoot的源码,本博客聊的版本为v2.0.3.RELEASE。目前SpringBoot的最新版为v3.3.2,可能目前有些公司使用的SpringBoot版本高于我这个版本。但是没关系,因为版本越新,新增的功能越多,反而对SpringBoot源码的研究带来更多的困难,我觉得没必要刻意追求最新,只要掌握其核心流程即可,万变不离其宗。另外,前面我花了大量的时间,一共写了六篇博客,也是为了讲SpringBoot框架做铺垫,Spring/SpringMVC的原理,如果没看的话,建议先看这部分的博客(《Spring源码深度解析(上)、《SpringMVC源码深度解析(上)》),不然直接看SpringBoot源码,会有一定难度。因为我理解的SpringBoot框架,是对Spring FrameWork框架的进一步封装。OK,话不多说,进入正题。

        先看看项目的层级目录:

        依赖也很简单,如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-project</artifactId><version>2.0.3.RELEASE</version></parent><modelVersion>4.0.0</modelVersion><artifactId>my-spring-boot</artifactId><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.0.3.RELEASE</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><version>2.0.3.RELEASE</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId><version>2.0.3.RELEASE</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.24</version></dependency></dependencies></project>

        使用过SpringBoot框架的朋友都知道,SpringBoot会有一个启动类,启动类是被@SpringBootApplication注解修饰的。看看App.class的代码:

        那就以SpringApplication这个类最为切入点讲解。先看@SpringBootApplication注解,代码如下:

        可以看出,@SpringBootApplication注解也是可以添加包扫描路径的,最终添加的包扫描路径会设置到@ComponentScan注解中的scanBasePackages或者scanBasePackageClasses属性中去。但是我们一般不会指定,默认扫描的包路径为:App类所在的包及其子包。然后,在@SpringBootApplication注解注解上,还添加了几个注解,分别是:@ComponentScan、@SpringBootConfiguration、@EnableAutoConfiguration等。@ComponentScan自不必说,@SpringBootConfiguration注解实际上又是被@Configuration注解修饰的,如果把@SpringBootConfiguration注解替换成@Configuration注解,也是没有任何问题的。

因此,被@SpringBootApplication注解修饰的类可以直接当配置类使用,也就是可以在类中添加其它类到Spring容器中。重点来了,就是@EnableAutoConfiguration注解,这是SpringBoot实现自动装配的关键,代码如下:

        可以看出,@EnableAutoConfiguration注解可以排除一些类,除此之外,这个注解上面也被其他注解所修饰,分别是:@AutoConfigurationPackage注解和@Import注解。代码中的注释对@AutoConfigurationPackage注解,我说的很清楚:作用就是往Spring容器中注入BasePackages对象,该类存有扫描的包信息,用于其他框架整合SpringBoot的时候,方便获取到这个v包,进行它自己的扫描,需要这样操作的框架还挺多的,如Mybatis、Dubbo、Open Feign等。当然,其他框架用不用这个类是它们的事,但是SpringBoot有提供这样的方式。

        再看看@Import注解,熟悉Spring框架的朋友应该对这个注解很熟悉,它的作用是向Spring容器中注入@Import注解配置的Class。看看AutoConfigurationImportSelector类,代码如下:

        可以看出,AutoConfigurationImportSelector实现了DeferredImportSelector、BeanClassLoaderAware、ResourceLoaderAware、BeanFactoryAware、EnvironmentAware等接口,Spring框架在初始化AutoConfigurationImportSelector的时候,会多次调用回调方法,比如给AutoConfigurationImportSelector设置ConfigurableListableBeanFactory、Environment、ClassLoader、ResourceLoader等对象。其中,DeferredImportSelector接口很重要,根据这个接口的特点:当Spring在解析配置类的时候,当解析完这一轮配置类后,才回调用DeferredImportSelector#selectImports()方法,由于有着一个延迟解析的特点,才能实现这样一个功能:比如Servlet容器有很多种,如Tomcat、Jetty、Undertow等,默认使用Tomcat作为Servlet容器,如果此时开发人员不想用Tomcat,想用Jetty,那应该怎么做呢?很简单,引入Jetty的依赖,排除Tomcat相关依赖即可。这里涉及到ServletWebServerFactoryConfiguration类,代码如下:

        除了我刚刚说的,引入Jetty的依赖,再排除Tomcat相关依赖,可以改成使用Jetty服务器;还有一个方法也可以做到,即不用排除Tomcat的依赖,只需要再引入Jetty的依赖,在自己的配置类中添加JettyServletWebServerFactory即可。因为ServletWebServerFactoryConfiguration这个配置类是Spring在解析完程序员自定义的配置类后再解析的,因此通过@ConditionalOnMissingBean注解进行判断的时候会发现,此时Spring容器中已经有了之前注入的JettyServletWebServerFactory对象了,因此,ServletWebServerFactoryConfiguration中配置三个ServletWebServerFactory对象都不会注入到Spring容器中,最后调用ServletWebServerFactory#getWebServer()方法,得到的只有JettyWebServer,代码如下:

        因此可以知道,AutoConfigurationImportSelector实现DeferredImportSelector接口的作用就是保证程序员的配置大于默认配置!当然,讲到这里,其实还不是SpingBoot的自动装配,自动装配的话,还是要看AutoConfigurationImportSelector#selectImports()方法,代码如下:

        再看看AutoConfigurationImportSelectorget#CandidateConfigurations()方法,看看它是如何获取配置类的,代码如下:

可以看出,上面的逻辑是,通过ClassLoader读取classpath下的META-INF/spring.factories文件,获取文件中的内容,看看spring.factories,如下:

        其中有一个EnableAutoConfiguration类的全限定名,而且确实方法中也传入了EnableAutoConfiguration类的全限定名,因此可以猜到:程序读取spring.factories文件,并通过EnableAutoConfiguration类的全限定名作为Key,获取对用的value,也就是截图中的一大堆类的全限定名,并返回,这就是所有待解析的配置类。其中也不乏我们熟悉的类,如:RabbitAutoConfiguration、AopAutoConfiguration、ElasticsearchDataAutoConfiguration等等。那是不是把这些配置类全部返回,并进行加载解析就行了呢?当然不行,准确的说是没必要,因为这些配置类都是要有相关的依赖,才会起作用,因此需要过滤,当然,如果通过@SpringBootApplication配置,排除配置类或者配置类名,这种也需要过滤。

        以上就是SpringBoot自动装备的原理。如果我们自己要写一个工具,怎么与SpringBoot整合呢?其实很简单,自己写一个项目,在META-INF目录下建一个spring.factories文件,目录为:

在该文件中内容为:org.springframework.boot.autoconfigure.EnableAutoConfiguration=你的配置的全限定名。如果有多个配置类,就用","隔开。这样在pom文件中,引入你写的工具的依赖,SpringBoot就会加载这个配置类,再配合配置上加的条件注解即可。

        回到App类中,看看main方法:

        在该方法中,核心的类是SpringApplication,先看看它的有参构造方法,传入的是App.class,代码如下:

        将传入的App.class存入LinkedHashSet,并赋值给primarySources属性。然后调用SpringApplication#deduceWebApplicationType(),推断应用类型,代码如下:

        再调用SpringApplication#getSpringFactoriesInstances()方法,传入ApplicationContextInitializer.class,加载ApplicationContextInitializer.class接口的实现类,代码如下:

        并将获取到的ApplicationContextInitializer对象的集合,赋值给SpringApplication的initializers属性,代码如下:

        同理,从spring.factories中获取到所有ApplicationListener对象的集合,赋值给SpringApplication的listeners属性中。最后调用SpringApplication#deduceMainApplicationClass()方法,推断主类,代码如下:

         最终获取到的也是App.calss,并赋值给SpringApplication的mainApplicationClass属性。

        以上,就是SpringApplication的有参构造方法。这也这只是完成了SpringApplication初始化工作,但是要让服务跑以来,核心的就是调用SpringApplication#run(String[] args)方法,代码如下:

        其实我的注释写的很详细的,不过我还是带着大家看看。首先是看看SpringApplication#getRunListeners()方法,代码如下:

        还是通过通过spring.factories文件获取SpringApplicationRunListener对象的集合,实际上只有一个实现类,就是 EventPublishingRunListener,在创建这个对象的时候,会调用它的有参构造,传入 SpringApplication对象,有参构造的代码为:

        最终将EventPublishingRunListener对象在设置到SpringApplicationRunListeners对象中,后续在进行时间发布的时候,调用的是SpringApplicationRunListeners的某些方法,代码如下:

        回到SpringApplication#run()方法,接着就是调用SpringApplicationRunListeners#starting()方法,发布ApplicationStartingEvent,调用ApplicationListener#onApplicationEvent()方法,调用之前会先判断,哪些ApplicationListener对象是对ApplicationStartingEvent事件“感兴趣”的。这里没有太多好说的,就不说了,继续往下看,再调用SpringApplication#prepareEnvironment(),这里是处理环境变量,配置就是在这个方法中解析读取的,需要重点看看,代码如下:

        先看看SpringApplication#getOrCreateEnvironment()方法,代码如下:

        看看StandardServletEnvironment的类继承图,如下:

        看看父类的构造,发现AbstractEnvironment父类构造中有做一些初始化的操作,代码如下:

        到这里,可以知道,此时在环境变量中,应该设置了四种属性,顺序(顺序代表着优先级)分别是:StubPropertySource(servletConfigInitParams)、StubPropertySource(servletContextInitParams)、MapPropertySource(systemProperties)、SystemEnvironmentPropertySource(systemEnvironment),只不过前两个,此时还没有任何值,毕竟还没有设置值。打断点看看,我说的是否正确:

        要想获取main方法中的args参数,需要先设置在Idea中设置,设置如下:

        可以知道,最终通过main方法传入的args参数,封装成SimpleCommandLinePropertySource对象,并放入环境变量属性的最前面,此时环境变量有五种属性了。打断点看看:

        再看看SpringApplication#configureProfiles()方法,代码如下:

        重点看看SpringApplicationRunListeners#environmentPrepared()方法,代码如下:

        看这个事件名,可以猜到是处理配置相关的,继续往下看,代码如下:

        这里我可以明确的告诉你,调用的是ConfigFileApplicationListener#onApplicationEvent()方法(我看过SpringBoot v2.6的版本,这个版本中没有使用ConfigFileApplicationListener来解析配置文件了,最低是什么版本就没有再使用ConfigFileApplicationListener类了,这我就不确定了),这个方法会处理配置文件,该方法的代码如下:

        可以知道,会创建RandomValuePropertySource(random)对象,放在SystemEnvironmentPropertySource(systemEnvironment)后面,到目前为止,环境变量一共有六种属性了,打断点看看,代码如下:

        OK,再看看Loader#Loader()方法,代码如下:

        再看看Loader#initializeProfiles()方法:

        可以知道,此时在profiles中有两个对象,也是个空的Set对象,另一个是Profile(default)。回到Loader#Loader()方法继续往下看,接着就是遍历profiles对象,代码如下:

        核心是调用重载方法 Loader#Loader(),代码如下 :

        因此从源码可以知道,环境变量设置:spring.config.location、spring.config.additional-location,可以指定读取文件的路径,如果没有设置的话,默认读取的路径为:file:./config/ 、file:./、classpath:/config/、classpath:/ 等四个路径(顺序即为读取路径的优先级)。并且设置 spring.profiles.active,可以设置文件后缀,如设置为 dev,最后读取的文件为:xx-dev。

然后就是对这四个路径进行遍历,判断那个路径下,有配置文件。除了知道文件路径外,还要知道读取的文件名叫什么,这个也有默认值,当然也可以通过配置去修改默认的文件名,代码如下:

        有了路径和文件名,就可以准备读取了,代码如下:

        看看PropertySourceLoader是如何赋值的,代码如下:

        可以知道,也是从spring.factories文件中读取PropertySourceLoader接口的实现类并实例化,一共有两个,分别是PropertiesPropertySourceLoader 和 YamlPropertySourceLoader。前者用于解析xml和propeties后缀的文件,后者解析yml和yaml后缀的文件,如下:

        继续往下看,代码如下:

        看看Loader#loadDocuments()方法,代码如下:

        到这里就行了,感兴趣的可以自己研究,回到Loader#load()方法,代码如下:

        调用consumer#accept()方法,也就是前面传入的λ表达式,即:

        到目前为止解析的还是application,由于我在application.yml配置了profile,因此还会继续读取:

        回到Loader#load()方法,由于在前面已经读取到application.yml中设置的dev了,并放入Loader的profiles属性中,而且还是在遍历profiles,因此最终会解析application-dev.yml文件,代码如下:

        读取配置的逻辑一样,这里不再赘述,到现在为止,读取的配置还只是存在Loader的loaded属性中,需要放如环境变量中,也就是调用下面的代码,代码如下:

        在读取配置的时候,会多次调用Collections.reverse()方法,改变顺序,其实这就是配置优先级的关键,继续往下看:

        到现在为止,环境变量中已经有八个配置了,其中application-dev.yml的配置在application.yml之前。如果通过环境变量取值的话,就是按照这个顺序来取值的,也就是说,只有前面七个配置中找不到,才会到第八个配置中找!到目前为止,我觉得SpringBoot配置读取这块,应该是讲的很详细了。

        回到SpringApplication#run()方法,继续往下看,代码如下:

        再看看SpringApplication#prepareContext()方法,代码如下:

        看看BeanDefinitionLoader#load()方法,代码如下:

        回到SpringApplication#run()方法,再看看SpringApplication#refreshContext()方法,代码如下:

        看看它的继承关系图:

        AbstractApplicationContext#refresh()方法有多重要,想必就不用我多说了吧,这块的代码在我之前的博客(《Spring源码深度解析(上)》)讲的很详细了,有兴趣的可以看看,其中有两个方法,即onRefresh()方法和finishRefresh()方法,需要我说一下,先看看onRefresh()方法,代码如下:

        其中ServletWebServerApplicationContext#initPropertySources()方法,会将ServletContext属性值设置到环境变量中,代码如下:

        再看ServletWebServerApplicationContext#createWebServer()方法,代码如下:

        再看看finishRefresh()方法,代码如下:

        最后再回到SpringApplication#run()方法看看剩下的代码,如下:

        到这里位置SpringBoot框架的源码算是讲完了,我个人觉得应该是讲的很全面的,如果在讲解的过程中,有漏讲或者讲错的,欢迎指出,感谢~

http://www.yayakq.cn/news/280158/

相关文章:

  • 进行seo网站建设房产加盟
  • 好的门户网站当今做啥网站能致富
  • 大学网站栏目建设通知用xampp来搭建wordpress建站环境
  • 建设公司的网站制作seo基础知识培训视频
  • 安徽太基建设官方网站百度关键词优化服务
  • 珠海网站建设专线WordPress影视采集
  • 在家用电脑做网站抖店怎么推广
  • 网站建设与运营 好考吗汽车租赁网站开发
  • 推广软件排行榜前十名台州百度推广优化
  • ps网站导航条素材山东富泰建设工程有限公司网站
  • 物流好的网站模板下载息烽县住房和城乡建设局网站
  • nodejs做网站能保护源代码吗网站页面的滑动怎么做的
  • flash制作网站top长春网站网络公司
  • 济南网站建设找老兵windows与wordpress
  • 百度推广网络推广微信网站重庆网站建设公司推荐
  • 大学生创业服务网站建设方案中国联通与腾讯设立合作
  • 免费空间asp网站网站建设氺金手指排名15
  • 做淘客网站 知乎有限责任公司优缺点
  • 教人做家具的网站郑州优化公司有哪些
  • python网站开发实例教程厦门创意互动网站建设
  • 电子代加工东莞网站建设wordpress sql优化
  • 如何更改公司网站内容有什么关于网站建设实例的书
  • 网站的优化是什么苏州住房建设建局官方网站
  • 宁波网站推广软件东莞企业网站建设预算大概多少
  • 铁岭 网站建设网站主办者有效证件电子件
  • 大连市营商环境建设局门户网站聊城seo培训
  • django做网站效率高吗网站制作文案
  • 网站建设方案哪家好 推荐html网页跳转代码到子网页
  • 做英文网站哪家好学校网站 源码
  • 张家口网站建设工作室网站建设自查工作总结