努力学习,成为一个吃喝不愁的人。

1 Spring 优缺点分析

优点:AOP + IOC

缺点:Spring 的代码是轻量的,基本版本大于 2 MB,但 Spring 的配置繁多,依赖管理耗时耗力,一旦选错依赖版本,不兼容问题就会找上门。

2 Spring Boot 横空出世

Spring Boot 是 Spring 开源组织下的子项目,它对 Spring 的缺点进行了改善与优化,基于约定优于配置的思想,简化了配置,让开发人员专注于业务逻辑的代码编写。

2.1 约定优于配置

约定优于配置简单来理解,就是遵循约定。比如说项目有一个名为 User的类,那么数据库中对应的表就会默认命名user

具体体现

(1)默认将主程序类所在包及所有子包下的组件扫描到 spring容器中;

(2)使用 Spring Initializr方式构建Spring Boot项目时,会在resource目录下自动生成一个空的 application.properties 文件,Spring Boot项目启动时会自动加载application.properties文件;

(3)@ComponentScan注解默认扫描Spring Boot项目主程序启动类所在的包路径;

(4)spring-boot-starter-web 中默认包含 spring-mvc 相关依赖以及内置的 tomcat 容器等,使得在开发阶段可以直接通过 main 方法或是 jar 包独立运行一个 web 项目。

2.2 起步依赖

将具备某些功能的坐标打包到一起,并提供一些默认的功能,如下图的 spring-boot-starter-web

2.3 自动配置

Spring Boot的自动配置,指的是 Spring Boot会自动将一些配置类的 Bean注册进 IoC容器,我们可以需要的地方使用 @autowired或者@resource等注解来使用它。 “自动的表现形式就是我们只需要引我们想用功能的包,相关的配置可以不管,直接使用即可。

3 Spring Boot 源码剖析

带着问题去看源码,效果更好哦。

3.1 依赖管理

问题 1:为什么导入 dependency 时不需要指定版本?

在Spring Boot入门程序中,项目pom.xml文件有两个核心依赖,分别是spring-boot-starter-parent和spring-boot-starter-web,具体介绍如下:

(1) spring-boot-starter-parent 依赖

<!-- Spring Boot 父项目依赖管理 -->
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent<11./artifactId>             <version>2.2.2.RELEASE</version> <relativePath/>
</parent>
----------------------------------------------------------<!-- 文件加载顺序 -->
<resource>
    <filtering>true</filtering>
    <directory>${basedir}/src/main/resources</directory>
    <includes>
       <include>**/application*.yml</include>
                        <include>**/application*.yaml</include>
        <include>**/application*.properties</include>
    </includes></resource>

spring-boot-starter-parent依赖作为Spring Boot项目的统一父项目依赖管理,并将项目版本号统一为 2.2.2.RELEASE,该版本号可以修改 。使用“Ctrl+鼠标左键进入并查看spring-boot-starter-parent底层源文件,发现 spring-boot-starter-parent 有一个父依赖 spring-boot-dependencies

(2)spring-boot-dependencies

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.0.0.RELEASE</version>
    <relativePath>../../spring-boot-dependencies</relativePath>

</parent>------------------------------------------------------------------
<!-- 版本号统一管理 -->
<properties> <activemq.version>5.15.11</activemq.version> ...<solr.version>8.2.0</solr.version> <mysql.version>8.0.18</mysql.version> <kafka.version>2.3.1</kafka.version> <spring-amqp.version>2.2.2.RELEASE</spring-amqp.version><spring-restdocs.version>2.0.4.RELEASE</spring-restdocs.version> <spring-retry.version>1.2.4.RELEASE</spring-retry.version> <spring-security.version>5.2.1.RELEASE</spring-security.version><spring-session-bom.version>Corn-RELEASE</spring-session-bom.version> <tomcat.version>9.0.29</tomcat.version> <thymeleaf.version>3.0.11.RELEASE</thymeleaf.version> ...
</properties>

该文件通过标签对一些常用技术框架的依赖文件进行了版本号统一管理,例如 activemqspringtomcat等,它们的版本都与Spring Boot 的版本相匹配,所以 pom.xml引入依赖文件不需要标注版本号。 如果 pom.xml引入的依赖文件不是 spring-boot-starter-parent管理的,那么在 pom.xml 引入依赖文件时,需要使用标签指定依赖文件的版本号。

问题 2:spring-boot-starter-parent 父依赖启动器的主要作用是进行版本统一管理,那么项目运行依赖的JAR包是从何而来的?

通过依赖其他的启动器

(3) spring-boot-starter-web依赖

<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.2.2.RELEASE</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-json</artifactId>         <version>2.2.2.RELEASE</version><scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId>     <version>2.2.2.RELEASE</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> <version>2.2.2.RELEASE</version> <scope>compile</scope> <exclusions> <exclusion> <artifactId>tomcat-embed-el</artifactId>     <groupId>org.apache.tomcat.embed</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.2.2.RELEASE</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.2.RELEASE</version> <scope>compile</scope> </dependency>
</dependencies>

spring-boot-starter-web 依赖启动器的主要作用是提供Web开发场景所需的底层所有依赖。因此在 pom.xml 中引入spring-boot-starter-web依赖启动器时,就可以实现Web场景开发,而不需要额外导入 Tomcat服务器以及其他Web依赖文件。当然,这些引入的依赖文件的版本号还是由 spring-boot-starter-parent 父依赖进行统一管理。

Spring Boot除了提供有上述介绍的Web依赖启动器,还提供了其他许多开发场景的相关依赖, 打开Spring Boot官方文档,搜索“Starters” 即可。

3.2 自动配置

概念:在我们添加 jar包依赖时,自动为我们配置一些组件的相关配置,我们无需配置或者只需要少量配置就能运行编写的项目

问题:Spring Boot到底是如何进行自动配置的,都把哪些组件进行了自动配置?

@SpringBootApplication

@Target({ElementType.TYPE}) //注解的适用范围,Type表示注解可以描述在类、接口、注解或枚举中
@Retention(RetentionPolicy.RUNTIME) //表示注解的生命周期,Runtime运行时
@Documented //表示注解可以记录在javadoc中
@Inherited //表示可以被子类继承该注解@SpringBootConfiguration // 标明该类为配置类
@EnableAutoConfiguration // 启动自动配置功能
@ComponentScan // 包扫描器 <context:component-scan base-package="com.xxx.xxx"/>
public @interface SpringBootApplication {......
}

@SpringBootApplication注解是一个组合注解,前面 4 个是注解的元数据信息, 主要看后面 3 个注解:@SpringBootConfiguration@EnableAutoConfiguration、 @ComponentScan

(1)@SpringBootConfiguration

@SpringBootConfiguration 表示当前类为一个配置类,并可以被组件扫描器扫描。由此可见,@SpringBootConfiguration注解的作用与@Configuration注解相同,只不过 @SpringBootConfiguration是被Spring Boot进行了重新封装命名而已。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented @Configuration //配置IOC容器
public @interface SpringBootConfiguration {}

(2)@EnableAutoConfiguration

@EnableAutoConfiguration 注解表示开启自动配置功能,是Spring Boot框架最重要的注解。

@AutoConfigurationPackage //自动配置包 : 会把@springbootApplication注解标注的类所在包名拿到,并且对该包及其子包进行扫描,将组件添加到容器中
@Import(AutoConfigurationImportSelector.class)  //可以帮助springboot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器(ApplicationContext)中
public @interface EnableAutoConfiguration {}

可以发现它是一个组合注解,Spring 中有很多以Enable开头的注解,其作用就是借助@Import来收集并注册特定场景相关的bean,并加载到IoC容器。@EnableAutoConfiguration就是借助@Import 来收集所有符合自动配置条件的 bean定义,并加载到IoC容器。

@AutoConfigurationPackage

主要作用就是将主程序类所在包及所有子包下的组件扫描到 spring容器中。 因此在定义项目包结构时,要求定义的包结构非常规范,项目主程序启动类要定义在最外层的根目录位置。 

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited//spring框架的底层注解,它的作用就是给容器中导入某个组件类,
//例如@Import(AutoConfigurationPackages.Registrar.class),它就是将Registrar这个组件类导入到容器中
@Import(AutoConfigurationPackages.Registrar.class)  //  默认将主配置类(@SpringBootApplication)所在的包及其子包里面的所有组件扫描到Spring容器中
public @interface AutoConfigurationPackage {}

Registrar

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {// 获取的是项目主程序启动类所在的目录// 程序启动会调用该方法//metadata:注解标注的元数据信息@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {// 将会得到启动类的包路径,如 com.wyd   register(registry, new PackageImport(metadata).getPackageName());}}

默认将 @SpringBootApplication 标注的主配置类所在的包及其子包下的所有组件扫描到 IoC 容器中。

等等!!!这不是 @ComponentScan 的功能吗?去官方文档找找答案。

it will be used when scanning for code @Entity classes. It is generally recommended that you place EnableAutoConfiguration (if you're not using @SpringBootApplication) in a root package so that all sub-packages and classes can be searched.

比如说,你用了Spring Data JPA,可能会在实体类上写@Entity注解。这个 @Entity 注解由 @AutoConfigurationPackage扫描并加载,而我们平时开发用的 @Controller/@Service/@Component/@Repository 这些注解是由ComponentScan来扫描并加载的。

  • 简单理解:这二者扫描的对象是不一样的。

验证一下:待补充

AutoConfigurationImportSelector

// 这个方法告诉springboot都需要导入那些组件@Overridepublic String[] selectImports(AnnotationMetadata annotationMetadata) {//判断 enableautoconfiguration注解有没有开启,默认开启(是否进行自动装配)if (!isEnabled(annotationMetadata)) {return NO_IMPORTS;}//1. 加载配置文件META-INF/spring-autoconfigure-metadata.properties,从中获取所有支持自动配置类的条件//作用:SpringBoot使用一个Annotation的处理器来收集一些自动装配的条件,那么这些条件可以在META-INF/spring-autoconfigure-metadata.properties进行配置。// SpringBoot会将收集好的@Configuration进行一次过滤进而剔除不满足条件的配置类// 自动配置的类全名.条件=值AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}

AutoConfigurationMetadataLoader

final class AutoConfigurationMetadataLoader {protected static final String PATH = "META-INF/" + "spring-autoconfigure-metadata.properties";//文件中为需要加载的配置类的类路径static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {try {//1.读取spring-boot-autoconfigure.jar包中spring-autoconfigure-metadata.properties的信息生成urls枚举对象// 获得 PATH 对应的 URL 们Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path) : ClassLoader.getSystemResources(path);// 遍历 URL 数组,读取到 properties 中Properties properties = new Properties();//2.解析urls枚举对象中的信息封装成properties对象并加载while (urls.hasMoreElements()) {properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement())));}// 将 properties 转换成 PropertiesAutoConfigurationMetadata 对象//根据封装好的properties对象生成AutoConfigurationMetadata对象返回return loadMetadata(properties);} catch (IOException ex) {throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex);}}

spring-autoconfigure-metadata.properties

# 类全名
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration
# 条件
.AutoConfigureAfter
=
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration......

Spring.factories

@EnableAutoConfiguration 就是从classpath中搜寻META-INF/spring.factories配置文件,并将其中 org.springframework.boot.autoconfigure.EnableutoConfiguration 对应的配置项通过反射实例化为对应的标注了 @ConfigurationJavaConfig形式的配置类,并加载到IoC容器中。

org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntryprotected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {// 1. 判断是否开启注解。如未开启,返回空串if (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY;}// 2. 获得注解的属性AnnotationAttributes attributes = getAttributes(annotationMetadata);// 3. getCandidateConfigurations()用来获取默认支持的自动配置类名列表// spring Boot在启动的时候,使用内部工具类SpringFactoriesLoader,查找classpath上所有jar包中的META-INF/spring.factories,// 找出其中key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的属性定义的工厂类名称,// 将这些值作为自动配置类导入到容器中,自动配置类就生效了List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);// 3.1 //去除重复的配置类,若我们自己写的starter 可能存在重复的configurations = removeDuplicates(configurations);// 4. 如果项目中某些自动配置类,我们不希望其自动配置,我们可以通过EnableAutoConfiguration的exclude或excludeName属性进行配置,// 或者也可以在配置文件里通过配置项“spring.autoconfigure.exclude”进行配置。//找到不希望自动配置的配置类(根据EnableAutoConfiguration注解的一个exclusions属性)Set<String> exclusions = getExclusions(annotationMetadata, attributes);// 4.1 校验排除类(exclusions指定的类必须是自动配置类,否则抛出异常)checkExcludedClasses(configurations, exclusions);// 4.2 从 configurations 中,移除所有不希望自动配置的配置类configurations.removeAll(exclusions);// 5. 对所有候选的自动配置类进行筛选,根据项目pom.xml文件中加入的依赖文件筛选出最终符合当前项目运行环境对应的自动配置类//@ConditionalOnClass : 某个class位于类路径上,才会实例化这个Bean。//@ConditionalOnMissingClass : classpath中不存在该类时起效//@ConditionalOnBean : DI容器中存在该类型Bean时起效//@ConditionalOnMissingBean : DI容器中不存在该类型Bean时起效//@ConditionalOnSingleCandidate : DI容器中该类型Bean只有一个或@Primary的只有一个时起效//@ConditionalOnExpression : SpEL表达式结果为true时//@ConditionalOnProperty : 参数设置或者值一致时起效//@ConditionalOnResource : 指定的文件存在时起效//@ConditionalOnJndi : 指定的JNDI存在时起效//@ConditionalOnJava : 指定的Java版本存在时起效//@ConditionalOnWebApplication : Web应用环境下起效//@ConditionalOnNotWebApplication : 非Web应用环境下起效//总结一下判断是否要加载某个类的两种方式://根据spring-autoconfigure-metadata.properties进行判断。//要判断@Conditional是否满足// 如@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })表示需要在类路径中存在SqlSessionFactory.class、SqlSessionFactoryBean.class这两个类才能完成自动注册。configurations = filter(configurations, autoConfigurationMetadata);// 6. 将自动配置导入事件通知监听器//当AutoConfigurationImportSelector过滤完成后会自动加载类路径下Jar包中META-INF/spring.factories文件中 AutoConfigurationImportListener的实现类,// 并触发fireAutoConfigurationImportEvents事件。fireAutoConfigurationImportEvents(configurations, exclusions);// 7. 创建 AutoConfigurationEntry 对象return new AutoConfigurationEntry(configurations, exclusions);}

(3) @ComponentScan

@ComponentScan注解具体扫描的包的根路径由Spring Boot项目主程序启动类所在包位置决定,在扫描过程中由前面介绍的@AutoConfifigurationPackage注解进行解析,从而得到Spring Boot项目主程序启动类所在包的具体位置

3.3 总结

学到长痘之 - Spring Boot相关推荐

  1. spring boot设置session超时时长(自定义spring boot session超时时长)

    针对spring boot 2.0 以下版本(亲测1.5.10)设置session超时的方法如下: 1.添加依赖,代码如下: <dependency><groupId>org. ...

  2. 视频教程-Spring Boot实战入门视频课程-Java

    Spring Boot实战入门视频课程 国内上市大型医疗软件公司产品研发部总经理,技术培训总监.6年以上大型项目一线开发.架构.管理经验,曾主导医疗大数据+移动BI产品设计与研发.技术狂热爱好者,擅长 ...

  3. 2023最新首发,全网最全 Spring Boot 学习宝典(附思维导图)

    作者:bug菌 博客:CSDN.掘金.infoQ.51CTO等 简介:CSDN/阿里云/华为云/51CTO博客专家,博客之星Top30,掘金年度人气作者Top40,51CTO年度博主Top12,掘金/ ...

  4. spring boot框架学习学前掌握之重要注解(2)-通过java的配置方式进行配置spring

    本节主要内容: 1:通过代码演示实现零XML配置spring 2:使用重点注解理解 声明: 本文是<凯哥陪你学系列-框架学习之spring boot框架学习>中spring boot框架学 ...

  5. spring boot框架学习2-spring boot核心(1)

    本节主要: 1:解析spring boot入口和@SpringBootApplication源码详解 SpringBootApplication包含: @SpringBootConfiguration ...

  6. 用小说的形式讲解Spring(4) —— 使用Spring Boot创建NoXml的Web应用

    本文发布于专栏Effective Java,如果您觉得看完之后对你有所帮助,欢迎订阅本专栏,也欢迎您将本专栏分享给您身边的工程师同学. 本文中的项目使用Github托管,已打Tag,执行git che ...

  7. java天气信息管理系统,基于 Spring Boot + Spring Cloud 实现天气预报系统

    [课程内容] 第1章 导学及SpringCloud介绍 1-1 spring boot简介 1-2 开启第一个spring boot 项目 第2章 基于Spring Boot快速构建天气预报系统 2- ...

  8. 60. Spring Boot写后感【从零开始学Spring Boot】

    从2016年4月15日到2016年7月20日经历长达3个月的时间,[从零开始学习Spring Boot]系列就要告一段落了.国内的各种资源都比较乱或者是copy 来copy去的,错了也不加以修正下,导 ...

  9. (10)Spring Boot修改端口号【从零开始学Spring Boot】

    Spring boot 默认端口是8080,如果想要进行更改的话,只需要修改applicatoin.properties文件,在配置文件中加入: server.port=9090 常用配置: #### ...

最新文章

  1. Ruby Metaprogramming
  2. knn闽南语是什么意思_小丑竟是我自己是什么意思梗 小丑竟是我自己bgm是什么...
  3. python批量下载网页文件-超简单超详细python小文件、大文件、批量下载教程
  4. 算法导论 第六章 堆排序 习题6.5-8 k路合并排序
  5. [zz]jQuery.extend 函数详解
  6. 【Linux】一步一步学Linux——dpkg-split命令(274)
  7. 项目管理九要与八不要
  8. android恶意软件流量,基于流量分析的安卓恶意软件检测
  9. 微信小程序 图片轮播 视频
  10. swagger/knife4j小事记录(二)
  11. CSS动态绑定背景图片
  12. Linu系统——基础知识1
  13. API的防篡改和防重放机制
  14. vue导出excel加一个进度条_vue纯前端导出excel表格
  15. 字节跳动校招笔试题汇总
  16. 利用计算思维解决问题人和计算机都能完成,对计算思维能力养成与大学计算机基础课程改革的思考...
  17. 苹果批Meta高额抽成行为:暴露了其虚伪面孔
  18. Ubuntu系统下卸载命令apt-get remove/purge/autoremove/clean/autoclean的区别
  19. 笔记本连接苹果耳机的方法
  20. H3C--多区域OSPF配置实践

热门文章

  1. 2.2_枚举类型的特点
  2. js实现异步任务调度器
  3. 送30本曹大的《你凭什么做好互联网》
  4. win10非分页缓冲池占用过大的解决方法
  5. B端与C端产品有何不同?
  6. AI落地制造业:智能机器人应具备这4种能力
  7. 分享某盘不限速神器,免费好用
  8. Java实现数据库敏感信息脱敏方案
  9. AJAX()请求参数
  10. 【简单】唯一摩尔斯密码词