前言
本篇文章包含Springboot配置文件解释、热部署、自动装配原理源码级剖析、内嵌tomcat源码级剖析、缓存深入、多环境部署等等,如果能耐心看完,想必会有不少收获。
一、Spring Boot基础应用
Spring Boot特征
概念:
约定优于配置,简单来说就是你所期待的配置与约定的配置一致,那么就可以不做任何配置,约定不符合期待时才需要对约定进行替换配置。
特征:
1. SpringBoot Starter:他将常用的依赖分组进行了整合,将其合并到一个依赖中,这样就可以一次性添加到项目的Maven或Gradle构建中。
2,使编码变得简单,SpringBoot采用 JavaConfig的方式对Spring进行配置,并且提供了大量的注解,极大的提高了工作效率,比如@Configuration和@bean注解结合,基于@Configuration完成类扫描,基于@bean注解把返回值注入IOC容器。
3.自动配置:SpringBoot的自动配置特性利用了Spring对条件化配置的支持,合理地推测应用所需的bean并自动化配置他们。
4.使部署变得简单,SpringBoot内置了三种Servlet容器,Tomcat,Jetty,undertow.我们只需要一个Java的运行环境就可以跑SpringBoot的项目了,SpringBoot的项目可以打成一个jar包。
Spring Boot创建
Spring Boot项目结构图:
Spring Boot热部署
通过引入spring-bootdevtools插件,可以实现不重启服务器情况下,对项目进行即时编译。引入热部署插件的步骤如下:
1. 在pom.xml添加热部署依赖
2. IDEA热部署工具设置
3. 在项目任意页面中使用组合快捷键“Ctrl+Shift+Alt+/”打开Maintenance选项框,选中并打开Registry页面,列表中找到“compiler.automake.allow.when.app.running”,将该选项后的Value值勾选,用于指定IDEA工具在程序运行过程中自动编译,最后单击【Close】按钮完成设置。
热部署原理:
基本原理就是我们在编辑器上启动项目,然后改动相关的代码,然后编辑器自动触发编译,替换掉历史的.class文件后,项目检测到有文件变更后会重启srpring-boot项目。内部主要是通过引入的插件对我们的classpath资源变化进行监听,当classpath有变化,才会触发重启。其实这里对类加载采用了两种类加载器,对于第三方jar包采用baseclassloader来加载,对于开发人员自己开发的代码则使用restartClassLoader来进行加载,这使得比停掉服务重启要快的多,因为使用插件只是重启开发人员编写的代码部分。
排除资源:
默认情况下,改变资源 /META-INF/maven , /META-INF/resources , /resources , /static , /public ,或 /templates 不触发重新启动,但确会触发现场重装。如果要自定义这些排除项,则可以使用该spring.devtools.restart.exclude 属性。例如,仅排除 /static , /public 在application.properties设置以下属性。
spring.devtools.restart.exclude=static/**,public/**,config/**
全局配置文件优先级
优先级:以下图顺序号代表配置文件的优先级,并且相同配置文件按顺序加载可以实现互补,但是不会被覆盖。
注意:Spring Boot 有application.properties 和 application.yaml 两种配置文件的方式,yaml是一种JSON超文本格式文件,如果是2.4.0之前版本,优先级properties>yaml;但是如果是2.4.0的版本,优先级yaml>properties。
填加相应依赖配置可以实现在自定义配置properties配置提示
@ConfigurationProperties(prefix = "person")注解的作用是将配置文件中以person开头的属性值通过setXX()方法注入到实体类对应属性中。
@Component注解的作用是将当前注入属性值的Person类对象作为Bean组件放到Spring容器中,只有这样才能被@ConfigurationProperties注解进行赋值。
application.yaml配置文件
YAML文件格式是Spring Boot支持的一种JSON超集文件格式,以数据为中心,比properties、xml等更
适合做配置文件.
1.yml和xml相比,少了一些结构化的代码,使数据更直接,一目了然
2.相比properties文件更简洁
3.yaml文件的扩展名可以使用.yml或者.yaml。
4.application.yml文件使用 “key:(空格)value”格式配置属性,使用缩进控制层级关系。
属性注入
如果配置属性是Spring Boot已有属性,例如服务端口server.port,那么Spring Boot内部会自动扫描并读取这些配置文件中的属性值并覆盖默认属性。
@Configuration:声明一个类作为配置类。
@Bean:声明在方法上,将方法的返回值加入Bean容器。
@Value:属性注入
@ConfigurationProperties(prefix = "jdbc"):批量属性注入。
@PropertySource("classpath:/jdbc.properties")指定外部属性文件,在类上添加。
第三方配置:
@ConfigurationProperties 用于注释类之外,您还可以在公共 @Bean 方法上使用它。将属性绑定到控件之外的第三方组件
松散绑定:
Spring Boot使用一些宽松的规则将环境属性绑定到@ConfigurationProperties bean,因此环境属性名和bean属性名之间不需要完全匹配,比如在application.properties文件里定义一个first-name=tom,在对应bean类中使用firstName也能获取到对应的值,这就是松散绑定。
Spring Boot日志框架
SLF4J 的使用:
注意:由于每一个日志的实现框架都有自己的配置文件,所以在使用 SLF4j 之后,配置文件还是要使用实现日志框架的配置文件。
统一日志框架使用:
实现步骤
1. 排除系统中的其他日志框架。
2. 使用中间包替换要替换的日志框架。
3. 导入我们选择的 SLF4J 实现。
Spring Boot 默认已经使用了 SLF4J + LogBack . 所以我们在不进行任何额外操作的情况下就可以使用 SLF4J + Logback 进行日志输出。SLF4J 日志级别从小到大trace,debug,info,warn,error,默认是info级别。
二、Spring Boot源码分析
spring-boot-starter-parent
Spring Boot项目的统一版本父项目依赖管理。
在底层源文件定义了工程的Java版本;工程代码的编译源文件编码格式;工程编译后的文件编码格式;Maven打包编译的版本。
接着在build节点做了资源过滤
接着从spring-boot-starter-parent找到他父依赖 spring-boot-dependencies,从里面就可以发现里面定义了各种版本声明,通过这里声明可以让部分依赖不需要写版本号,一些没有引入的第三方jar包仍然需要自己声明版本号。
spring-boot-starter-web
Spring Boot项目的所依赖jar包进行打包起步依赖管理
在spring-boot-starter-web的父依赖spring-boot-starters包中,可以发现在他的dependencies标签有着各种依赖包引入,点进去就是具体包的导入配置管理。
注意:Spring Boot官方并不是针对所有场景开发的技术框架都提供了场景启动器,例如阿里巴巴的Druid数据源等,Spring Boot官方就没有提供对应的依赖启动器。为了充分利用Spring Boot框架的优势,在Spring Boot官方没有整合这些技术框架的情况下,Druid等技术框架所在的开发团队主动与Spring Boot框架进行了整合,实现了各自的依赖启动器,例如druid-spring-boot-starter等。我们在pom.xml文件中引入这些第三方的依赖启动器时,切记要配置对应的版本号。
自动配置@SpringBootApplication
他是一个组合注解,核心代码:
自动配置@SpringBootConfiguration
通过上面可以发现我们的核心启动类注解源码中含此注解,这个注解标注在某个类上,表示这是一个 Spring Boot的配置类。他的核心代码中,内部有一个核心注解@Configuration来表明当前类是配置类,并且可以被组件扫描器扫到,所以@SpringBootConfiguration与@Configuration具有相同作用,只是前者又做了一次封装。
自动配置@ EnableAutoConfiguration
Spring 中有很多以 Enable 开头的注解,其作用就是借助 @Import 来收集并注册特定场景相关的Bean ,并加载到 IOC 容器。而这个注解就是借助@Import来收集所有符合自动配置条件的bean定义,并加载到IoC容器,他的核心源码如下:
通过@AutoConfigurationPackage注解进入类别,发现他通过import引入了一个AutoConfigurationPackages.Registrar.class,在Registrar.class中就重写了一个registerBeanDefinitions方法,在方法内部调用了一个register方法来实现将注解标注的元信息传入,获取到相应的包名。通俗点就是注册bean,然后根据 @AutoConfigurationPackage找到需要注册bean的类路径,这个路径就被自动保存了下来,后面需要使用bean,就直接获取使用,比如Spring Boot整合JPA可以完成一些注解扫描。
自动配置@Import(AutoConfigurationImportSelector.class)
该注解是Spring boot的底层注解,AutoConfigurationImportSelector类可以帮助 Springboot 应用将所有符合条件的 @Configuration配置都加载到当前Spring Boot创建并使用的IOC容器( ApplicationContext )中。
该注解实现了实现了 DeferredImportSelector 接口和各种Aware 接口,在源码中截图中,通过四个接口回调,把值返回给了
定义的四个成员变量。
1.自动配置逻辑相关的入口方法在 DeferredImportSelectorGrouping 类的 getImports 方法。
2.自动配置的相关的绝大部分逻辑全在第一处也就是this.group.proces方法里,主要做的事就是在方法中,传入的 AutoConfigurationImportSelector对象来选择一些符合条件的自动配置类,过滤掉一些不符合条件的自动配置类,而第二处的this.group.selectImports的方法主要是针对前面的process方法处理后的自动配置类再进一步有选择的选择导入。
3.进入getAutoConfigurationEntry方法,这个方法主要是用来获取自动配置类有关,承担了自动配置的主要逻辑。AutoConfigurationEntry 方法主要做的事情就是获取符合条件的自动配置类,避免加载不必要的自动配置类从而造成内存浪费
4.进入getCandidateConfigurations方法,里面有一个重要方法 loadFactoryNames ,这个方法是让 SpringFactoryLoader 去加载一些组件的名字。
5.进入 loadFactoryNames方法,获取到出入的键factoryClassName。
6.进入loadSpringFactories方法,加载配置文件,而这个配置文件就是spring.factories文件
由此我们可以知道,在这个方法中会遍历整个ClassLoader中所有jar包下的spring.factories文件。spring.factories里面保存着springboot的默认提供的自动配置类。
AutoConfigurationEntry 方法主要做的事情:
【1】从 spring.factories 配置文件中加载 EnableAutoConfiguration 自动配置类),获取的自动配置类如图所示。
【2】若 @EnableAutoConfiguration 等注解标有要 exclude 的自动配置类,那么再将这个自动配置类排除掉;
【3】排除掉要 exclude 的自动配置类后,然后再调用 filter 方法进行进一步的过滤,再次排除一些不符合条件的自动配置类;
【4】经过重重过滤后,此时再触发 AutoConfigurationImportEvent 事件,告诉ConditionEvaluationReport 条件评估报告器对象来记录符合条件的自动配置类;
【5】 最后再将符合条件的自动配置类返回。
AutoConfigurationImportSelector 的 filter 方法
主要做的事情就是调用AutoConfigurationImportFilter 接口的 match 方法来判断每一个自动配置类上的条件注解(若有的话) @ConditionalOnClass , @ConditionalOnBean 或 @ConditionalOnWebApplication 是否满足条件,若满足,则返回true,说明匹配,若不满足,则返回false说明不匹配。其实就是排除自动配置类,因为全部加载出来的类太多,不需要全部都反射成对象,而这个方法就是通过注解进行该自动配置类是否有相应匹配的类的判断,存在即加入,不存在即过滤。
@Conditional是Spring4新提供的注解,它的作用是按照一定的条件进行判断,满足条件给容器注册bean。
@ConditionalOnBean:仅仅在当前上下文中存在某个对象时,才会实例化一个Bean。
@ConditionalOnClass:某个class位于类路径上,才会实例化一个Bean。
@ConditionalOnExpression:当表达式为true的时候,才会实例化一个Bean。基于SpEL表达式的条件判断。
@ConditionalOnMissingBean:仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean。
@ConditionalOnMissingClass:某个class类路径上不存在的时候,才会实例化一个Bean。
@ConditionalOnNotWebApplication:不是web应用,才会实例化一个Bean。
@ConditionalOnWebApplication:当项目是一个Web项目时进行实例化。
@ConditionalOnNotWebApplication:当项目不是一个Web项目时进行实例化。
@ConditionalOnProperty:当指定的属性有指定的值时进行实例化。
@ConditionalOnJava:当JVM版本为指定的版本范围时触发实例化。
@ConditionalOnResource:当类路径下有指定的资源时触发实例化。
@ConditionalOnJndi:在JNDI存在的条件下触发实例化。
@ConditionalOnSingleCandidate:当指定的Bean在容器中只有一个,或者有多个但是指定了首选的Bean时触发实例化。
有选择的导入自动配置类
在第一步最后的一个方法this.group.selectImports主要是针对经过排除掉 exclude 的和被AutoConfigurationImportFilter 接口过滤后的满足条件的自动配置类再进一步排除 exclude 的自动配置类,然后再排序,至此实现了自动配置。
深知java小白学习有多么的困难,找不到一份好的资源,所以呕心沥血做了一份java基础全套的教程资源。
无论你是大牛还是java刚入行的小白,这套视频都可以满足你(300多个g)。
https://docs.qq.com/doc/DTFhtSExtQWlKUHVz