浅析SpringBoot起步依赖和自动配置原理

前言:

在Spring中,我们感受到了它高效率的开发模式,同时也应该体验到了它项目前期繁琐的配置,对于这一点,Spring也作出了改变,从Spring3.0开始,支持了纯注解配置,可以完全告别xml,相对而言简单了一些,但是对于复杂配置还是太繁琐,特别是涉及到与其他框架整合的部分。总体来说,Spring的优点是不言而喻的,用过的人都知道,但是存在的这些问题也是真实的,所以总结起来就是一句话:虽然Spring的组件代码是轻量级的,但它的配置却是重量级的。Spring的发展也不是一年两年了,存在的这些问题自然会有人站出来解决。

Pivotal团队在2014年4月发布了全新开源的轻量级框架SpringBoot,它基于Spring4.0设计,不仅继承了Spring框架原有的优秀特性,而且还通过简化配置来进一步简化了Spring应用的整个搭建和开发过程(来自百度百科SpringBoot)。它秉承着约定优于配置的思想,可以让开发人员不必在配置上花太多心思,全身心的投入到逻辑业务的代码编写中,从而大大提高了开发的效率,一定程度上缩短了项目周期。另一方面,在传统的Spring项目开发中,不可避免的需要我们手动导入一些依赖的jar包(包括Maven项目导入坐标),这些依赖通过脑子去记是很难记住的,而且一不小心弄错还会影响整个项目,特别是出现的bug还很难定位,谁能想到错误出现在一开始的导包呢,这一点可以参考我前面的一篇博客,由于aspectjweaver包导错而影响一半的AOP(跳转链接)。而在SpringBoot中,提供了很好的依赖管理方式,不仅可以简化繁琐的导包过程,还可以有效避免复杂项目中交叉依赖带来的版本冲突问题,详细内容可以自行了解,这里不做过多赘述。

但是需要强调一点的是,虽然SpringBoot给初学者提供了非常良好的Spring入门体验,但是还是建议大家先把Spring以及ssm框架学得差不多了再来玩SpringBoot,因为前面提到的“约定大于配置”,首先你得了解这些配置,才能明白这些约定,从而去使用,不然一旦碰到问题是很难自己解决的。(总之,我学完SpringBoot入门之后最大的感受就是:原来Spring还能这么玩。有时确实在整合其他框架时被繁琐的配置搞到心态炸裂,但是当几个框架成功整合到一起时,又有了做前端的时候的那种所见即所得的成就感,还是很庆幸我是在ssm之后才学习的SpringBoot,这样可以让我的基础更加牢固,这也是我给初学者的一点小建议)

下面切入正题,上面提到的SpringBoot的一些优点,被后来的人归纳为SpringBoot的两大核心,也就是起步依赖和自动配置。今天笔者就带领大家从源码的角度粗略的分析一下这两大核心底层的实现原理,由于笔者也是初学者,专业知识比较欠缺,没有能力去做特别细节上的讲解,这里只针对源码中一些常用的大家都认识的部分进行简单的分析,重要的是这两大核心实现的过程。

自动配置原理:

在SpringBoot中集成了一些常见的容器,或者说是组件,例如我们最常用的Tomcat服务器,所以在做web项目时,不需要再另外部署到Tomcat上,可以直接运行,这就需要整个项目有一个入口,类似于普通java程序中的main方法(其实就是main方法),这个入口我们一般用一个单独的类来表示,其中包含一个main方法,这个类通常称之为SpringBoot的引导类,SpringBoot入门程序的引导类代码如下:

package com.dzp;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class mySpringBootApplication {
    public static void main(String[] args) {
        SpringApplication.run(mySpringBootApplication.class,args);
    }
}

这个类上面有一个@SpringBootApplication注解,这个注解是整个SpringBoot程序的核心,他有两个最重要的作用,第一个作用是标识了这个类是SpringBoot的引导类,第二个作用是开启了SpringBoot的自动配置。下面我们进入这个注解的内部看源码(出于对源码的尊重,这里就不copy只截图了)

可以看到在定义这个注解的时候上面又加了很多注解(前面四个可以忽略不看,因为这些是限制注解的使用范围以及生成文档用的),主要看后面的三个注解,分别是@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan,首先@SpringBootConfiguration我们再进一步进入源码可以看到它其实就是熟悉的@Configuration,在Spring纯注解配置中用来标识Spring配置引导类的,这个就不过多分析了。然后看@ComponentScan,其实它也是对应Spring纯注解配置当中的@ComponentScan,用来指定开启注解扫描的位置,只不过在SpringBoot中这个位置也可以不用指定,因为它默认约定扫描的是被@SpringBootApplication修饰的引导类所在的包及其子包,也就是说我们如果按照下面这种项目结构来做的话,就完全不用配置注解扫描的位置:

接下来就是最重要的一个注解就是@EnableAutoConfiguration,它是SpringBoot特有的,从字面意思就可以看出来是开启自动配置的意思,它的实际功能也确实如此,只有当这个注解存在时SpringBoot才会帮你完成自动配置,要想了解它是怎么实现的,我们进一步挖源码:

 

可以看到这个注解上边又有两个注解,@AutoConfigurationPackage和@Import,@AutoConfigurationPackage是用来管理自动配置类的,可以先不看,先看@Import,这个注解在Spring纯注解配置当中也使用过,它的作用是导入另外一个配置类,这里导入了一个叫做AutoConfigurationImportSelector的配置类,翻译过来就是自动配置的导入选择器,进一步挖它的源代码:

由于这个类内容较多,我们只关心它实现自动配置的步骤,所以我们根据内部调用关系一步一步找:

内容太多看不懂不要紧,我们只看关键部分,这是我们找到的关键方法,第一个方法字面意思就是“选择导入”,它的内部又调用了第二个关键方法,意为“获取自动配置入口”,在这个getAutoConfigurationEntry()方法中可以明显看到configurations这样的关键字眼(通过下面的分析可以确定里面装的是一些配置类的全限定类名),而且它是一个字符串类型的集合,它来自于getCandidateConfigurations()方法,所以我们在这个类中继续往下找这个方法:

挖到这里,可以看到它还在继续调用其他类中的方法,这个时候要还想继续往下挖可能就有一定难度了,但是在这个时候我们可以从下方的message中发现一点猫腻,里面的META-INF/spring.factories好像看起来是某个路径下的,那么是那个路径下呢,我们可以盲猜就是当前这个类相关的某个路径,所以我们来到这个类的最上方看它的全限定包名:

我们可以根据这个包名在我们导入的Libraies里面去找这个包,然后真的找到了这么一个配置文件:

这个配置文件里面是一些全限定类名,而且每个类名都是以AutoConfiguration结尾的,这就基本可以确定上面那个configurations里面对应的就是这些内容,然后继续在里面找我们认识且熟悉的:

前面提到的内置Tomcat就是在这个里面进行配置的,我们通过这个全限定类名继续找到这个类:

我们通过关键注解@EnableConfigurationProperties(ServerProperties.class)继续往下挖,找到ServerProperties这个类:

可以看到这个类中定义了很多跟tomcat服务器有关的属性,如port端口、address地址等,上面有一个@ConfigurationProperties注解的属性中有一个prefix = "server",这个其实就是对应配置文件中的前缀,那么配置文件在哪呢,我们继续回到spring.factories同级的目录下,会发现如下的一个json文件:

 在这个.json文件中,可以找到跟刚才相关的配置,也就是说,这个json文件里面的值,就是SpringBoot自动配置时帮我们配置的值,如果我们需要更改这些配置,只需要在我们的配置文件中配置一下就行了,就会覆盖这里的默认配置,而使用我们的自定义配置。

SpringBoot自动配置原理的分析,到这里就基本结束了,虽然分析得比较浅薄,但是对于初学者想初步了解SpringBoot自动配置过程来说,这种深度恰到好处。等进一步熟练它的使用后,可以再进一步往底层挖掘,虽然有些太细节的东西,在项目开发中基本用不到,但是自己有一定了解的话,在使用过程中碰到问题可以有更清晰的思路去定位并解决。

起步依赖原理:

使用了springBoot后,我们不用再向以前那样在项目前期花大量的时间准备项目依赖了,可以将更多的精力放在项目的业务逻辑上,这一切都归功于它的第二个核心--起步依赖,也就是starter。简单地说,起步依赖就是将具有某种功能的依赖坐标打包到一起,提供一些默认的功能,当我们需要新的功能时,只需要导入对应的starter就可以了。在SpringBoot中,常见的依赖是以功能为单位进行导入的,比如我们要利用springMVC写web项目,只需要导入一个spring-boot-starter-web就可以了,与web功能相关的一些依赖jar包会被自动导入。这种依赖管理的方式,除了简化开发,另外还有一个重要的好处就是可以有效避免交叉依赖中的版本冲突问题,因为SpringBoot内置了大量的版本控制,我们在导入依赖时,不用自己考虑版本问题,所以自动规避掉了很多不必要的麻烦。

在每一个SpringBoot项目的pom文件中,都少不了如下这么一段配置:

 很简单的配置了此项目所依赖的父工程,但是这却是整个起步依赖的核心,这也是官方要求每一个springBoot工程必须继承的。和上面分析自动配置一样,要想了解它内部的工作原理,我们继续按住Ctrl进入这个spring-boot-starter-parent,对它里面的关键内容进行分析:

 

我们可以找到以上两个关键内容,第一个,可以看到这个功能又继承了另外一个工程spring-boot-dependencies,也就是当前项目的爷爷,这个我们等会挖,先看第二个,第二个是约定了SpringBoot配置文件的默认路径,也就是只要在这个路径下面去创建配置文件,springBoot启动时会帮我们自动加载,就不用我们手动加载或者指定路径了,实际开发中基本也是按照这个约定来做的,同时可以注意到SpringBoot支持两种类型的配置文件(前面两个属于同一种),分别是yml和properties,而且properties在后面,所以优先级更高(后面的配置会覆盖掉前面的)。下面进入到spring-boot-dependencies这个工程的pom文件中:

这个工程里面没有再继续继承其它工程。可以看到里面有相当多的版本控制,这里每个部分的版本控制不是一成不变的,它是根据当前springBoot的版本将可能会用到的一些其他技术的版本进行一个约定,不同SpringBoot的版本中以上内容可能大同小异。接着往下面看,可以看到里面进行了大量的依赖导入:

这些依赖导入被一个<dependencyManagement>标签包裹,意为依赖管理,这里为我们提供了项目中常用的一些默认功能以及版本控制,我们可以不用进行配置,就能完成一部分功能。综合以上,大致可以分析出,每个SpringBoot必须继承的这一个父工程,它的作用大概可以分为两个部分,第一个就是提供一些默认功能,第二个就是前面提到的版本控制,这里的版本控制不仅仅只包含这些默认功能,还包含其它的starter。

为了进一步弄清楚以功能为单位导入依赖这一概念,我们以web功能为例,继续进行起步依赖分析。

当我们需要做web相关开发时,我们只需要导入一个web相关的起步依赖就可以了,即spring-boot-starter-web,它就会根据依赖传递的思想,自动的将实现web功能所需的依赖全部导入,我们继续进入这个spring-boot-starter-web里面一探究竟:

又看到了以前熟悉的内容,以前这些需要我们手动导入的依赖都被SpringBoot帮我们打包好了,里面包含一些其他的jar包还有一些其他的starter,这些其他的starter又会继续帮我们导入打包好的依赖,这些打包好的starter中可能又包含了另外的一些starter......这就是依赖传递,到最终所有的依赖都以jar包的形式导入完成之后,就实现了web功能全部依赖的导入(这么说可能不够准确,如果需要用到一些特殊组件的话还是需要自己导入,这里只针对大部分情况)。

到这里又基本上分析完了,起步依赖相对于上面的自动配置分析起来可能更加直观,因为没有涉及到生疏的东西,都是看了就大概能懂的,而自动配置中复杂的调用关系确实显得很繁琐,但是重要的是了解它的工作过程,希望认真看完这篇文章的小伙伴能有所收获,虽然分析得不深,但是学习本就是一个逐渐递进的过程。引用新晋作者榜第一名小伙伴的一句话,希望大家能够掌握这个21世纪的魔法,以一台小小的笔记本,撬动人生的杠杆。

漫漫编程路,放弃不难,但坚持一定很酷!晚安

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值