1.解析pom.xml文件
1.1父项目做依赖管理------<parent>
打开spring-boot-starter-parent
打开spring-boot-dependencies
上面的<dependencies>元素中配置的是我们所创建的项目的所有依赖包的导入。有了【spring‐boot‐dependencies】项目以后我们新建的SpringBoot项目所需要的依赖包导入就不用再配置版本和<dependencies>元素导入依赖,从【spring‐boot‐dependencies】项目中继承就可以了。在自己的项目中配置需要功能的启动器就好。
总结:
<parent>元素配置的实际上是一个springboot项目,项目名称spring-boot-starter-parent。
我们自己创建的springboot项目实际上是spring-boot-starter-parent项目的子项目。
自己创建的springboot项目继承了spring-boot-starter-parent。之所以在自己创建的springboot项目中的pom文件中配置<parent>配置父项目,是为了从父项目中继承某一些元素。
1.2 SpringBoot启动器
我们在创建springboot项目的时候,选择对应的springboot版本,此时springboot就会将我们创建spring项目所需要的所有依赖包导入。
当构建程序的时候,这些依赖包有的会用到,有的不会用到,此时springboot版本就会将这些依赖包按照不同的使用场景/功能,划分成很多组,将实现某一个具体功能/适用于某一个具体开发场景的依赖包归结到一起,被归结在一起实现某一个具体功能/适用于某一个具体开发场景的依赖包就组成了一个启动器。
我们在创建自己具体功能的时候,只需要导入对应功能/对应场景的启动器就可以。此时我们就不用担心因为实现的功能时而将某一个依赖包遗漏的情况。
基本的启动器:
Spring Boot应用启动器基本的一共有44种,具体如下:
1)spring-boot-starter
这是Spring Boot的核心启动器,包含了自动配置、日志和YAML。
2)spring-boot-starter-actuator
帮助监控和管理应用。
3)spring-boot-starter-amqp
通过spring-rabbit来支持AMQP协议(Advanced Message Queuing Protocol)。
4)spring-boot-starter-aop
支持面向方面的编程即AOP,包括spring-aop和AspectJ。
5)spring-boot-starter-artemis
通过Apache Artemis支持JMS的API(Java Message Service API)。
6)spring-boot-starter-batch
支持Spring Batch,包括HSQLDB数据库。
7)spring-boot-starter-cache
支持Spring的Cache抽象。
8)spring-boot-starter-cloud-connectors
支持Spring Cloud Connectors,简化了在像Cloud Foundry或Heroku这样的云平台上连接服务。
9)spring-boot-starter-data-elasticsearch
支持ElasticSearch搜索和分析引擎,包括spring-data-elasticsearch。
10)spring-boot-starter-data-gemfire
支持GemFire分布式数据存储,包括spring-data-gemfire。
11)spring-boot-starter-data-jpa
支持JPA(Java Persistence API),包括spring-data-jpa、spring-orm、Hibernate。
12)spring-boot-starter-data-mongodb
支持MongoDB数据,包括spring-data-mongodb。
13)spring-boot-starter-data-rest
通过spring-data-rest-webmvc,支持通过REST暴露Spring Data数据仓库。
14)spring-boot-starter-data-solr
支持Apache Solr搜索平台,包括spring-data-solr。
15)spring-boot-starter-freemarker
支持FreeMarker模板引擎。
16)spring-boot-starter-groovy-templates
支持Groovy模板引擎。
17)spring-boot-starter-hateoas
通过spring-hateoas支持基于HATEOAS的RESTful Web服务。
18)spring-boot-starter-hornetq
通过HornetQ支持JMS。
19)spring-boot-starter-integration
支持通用的spring-integration模块。
20)spring-boot-starter-jdbc
支持JDBC数据库。
21)spring-boot-starter-jersey
支持Jersey RESTful Web服务框架。
22)spring-boot-starter-jta-atomikos
通过Atomikos支持JTA分布式事务处理。
23)spring-boot-starter-jta-bitronix
通过Bitronix支持JTA分布式事务处理。
24)spring-boot-starter-mail
支持javax.mail模块。
25)spring-boot-starter-mobile
支持spring-mobile。
26)spring-boot-starter-mustache
支持Mustache模板引擎。
27)spring-boot-starter-redis
支持Redis键值存储数据库,包括spring-redis。
28)spring-boot-starter-security
支持spring-security。
29)spring-boot-starter-social-facebook
支持spring-social-facebook
30)spring-boot-starter-social-linkedin
支持pring-social-linkedin
31)spring-boot-starter-social-twitter
支持pring-social-twitter
32)spring-boot-starter-test
支持常规的测试依赖,包括JUnit、Hamcrest、Mockito以及spring-test模块。
33)spring-boot-starter-thymeleaf
支持Thymeleaf模板引擎,包括与Spring的集成。
34)spring-boot-starter-velocity
支持Velocity模板引擎。
35)spring-boot-starter-web
支持全栈式Web开发,包括Tomcat和spring-webmvc。
36)spring-boot-starter-websocket
支持WebSocket开发。
37)spring-boot-starter-ws
支持Spring Web Services。
Spring Boot应用启动器面向生产环境的还有2种,具体如下:
38)spring-boot-starter-actuator
增加了面向产品上线相关的功能,比如测量和监控。
39)spring-boot-starter-remote-shell
增加了远程ssh shell的支持。
最后,Spring Boot应用启动器还有一些替换技术的启动器,具体如下:
40)spring-boot-starter-jetty
引入了Jetty HTTP引擎(用于替换Tomcat)。
41)spring-boot-starter-log4j
支持Log4J日志框架。
42)spring-boot-starter-logging
引入了Spring Boot默认的日志框架Logback。
43)spring-boot-starter-tomcat
引入了Spring Boot默认的HTTP引擎Tomcat。
44)spring-boot-starter-undertow
引入了Undertow HTTP引擎(用于替换Tomcat)
2.SpringBoot的主类
SpringApplication 将一个典型的 Spring 应用启动的流程“模板化”(这里是动词),在没有特殊需求的情况下,默认模板化后的执行流程就可以满足需求了但有特殊需求也没关系,SpringApplication 在合适的流程结点开放了一系列不同类型的扩展点,我们可以通过这些扩展点对 SpringBoot 程序的启动和关闭过程进行扩展。
两个重要的点:@SpringBootApplication和run方法执行
2.1 SpringApplication 的 run 方法【背】
该方法的主要流程大体可以归纳如下:
(1)SpringApplicatiopn调用run方法,需要先创建出SpringApplicatiopn实例对象,因此第一步是SpringApplication实例初始化,但在初始化时,会提前做几件事情:
①根据classpath里面是否存在某个特征类
(org.springframework.web.context.ConfigurableWebApplicationContext)来决定是否应该创建一个web应用使用的ApplicationcContext类型还是标准的Standalone应用使用的ApplicationContext类型。
②使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationContextInitializer。
③使用SpringFactoriesLoader在应用的classpath中查找并加载可用的ApplicationListener。
④推断并设置main方法的定义类,找到运行的主类。
(2)初始化完成后,开始执行run方法的逻辑。SpringApplication遍历执行所有通过SpringFactoriesLoader查找并加载的SpringApplicationRunListener,调用它们的started()方法。【告诉SpringApplicationRunListener,“嘿,SpringBoot应用要开始执行咯”】
(3)创建并配置当前SpringBoot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile)。
(4)遍历调用所有SpringApplicationRunListener 的 environmentPrepared()方法【告诉它们:“当前SpringBoot应用使用的Environment准备好了”】
(5)如果SpringApplication的showBanner 属性被设置为 true,则打印 banner。
(6)【创建ApplicationContext】根据用户是否明确设置了applicationContextClass 类型以及初始化阶段的推断结果,决定该为当前 SpringBoot 应用创建什么类型的 ApplicationContext ,创建完成,将之前准备好的 Environment 设置给创建好的 ApplicationContext 使用。
(7)ApplicationContext 创建好之后,SpringApplication遍历调用之前查找并加载的 ApplicationContextInitializer 的 initialize(applicationContext)方法来对已经创建好的 ApplicationContext 进行进一步的处理。
(8)遍历调用所有 SpringApplicationRunListener 的 contextPrepared()方法【通知它们:“SpringBoot 应用使用的ApplicationContext准备好啦!”】
(9)最核心的一步,将之前通过@EnableAutoConfiguration获取的所有配置以及其他形式的IoC容器配置加载到已经准备完毕的ApplicationContext。
(10)遍历调用所有 SpringApplicationRunListener 的 contextLoaded() 方法【告知所有 SpringApplicationRunListener,ApplicationContext "装填完毕"!】
(11)调用 ApplicationContext 的 refresh() 方法,完成 IoC 容器可用的最后一道工序。
(12)查找当前 ApplicationContext 中是否注册有 CommandLineRunner,如果有,则遍历执行它们。
(13)遍历执行 SpringApplicationRunListener 的 finished() 方法【告知它们:“搞定!”】
一个完整的 SpringBoot 应用启动完毕!
总结:
1.准备阶段----准备创建ApplicationContext的阶段
【将创建ApplicationContext的元素准备完毕】
结果:ApplicationContext创建好
2.设置阶段----对ApplicationContext进行设置
【设置执行过程中所需的元素】
3.运行阶段---ApplicationContext按照之前设置好的流程开始运行。
结果:所有 SpringApplicationRunListener 的 finished() 方法
2.2 @SpringBootApplication-----自动配置方式
作用:代替配置文件applicationContext.xml
@SpringBootApplication注解源码:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
对于@SpringBootApplication来说,内部包含的@SpringBootConfiguration
@EnableAutoConfiguration【重担压身】 @ComponentScan 才是最主要的。
2.2.1 @SpringBootConfiguration
@SpringBootConfiguration注解的源码:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
@SpringBootConfiguration注解包含了@Configuration注解,@Configuration注解标注在哪一个java类上,那么这个java类就是一个JavaConfig配置类,这个JavaConfig 配置类可以代替掉Spring配置文件【applicationContext.xml】,当我们在主类上标注@SpringBootApplication时,意味着主类是一个JavaConfig 配置类,因此我们在创建SptingBoot项目的时候才不用去编写Spring配置文件【applicationContext.xml】。
2.2.2 @EnableAutoConfiguration
@EnableAutoConfiguration源码:
(1)@AutoConfigurationPackage---指定了默认包规则
源码中@Import({Registrar.class})------里面有一方法,利用Registrar给容器中导入一些列组件,将指定的一个包下【主类所在包】的所有组件导入进来。
(2)Import(AutoConfigurationImportSelector.class).
-
- 利用getAutoConfigurationEntry(annotationMetadata)给容器中批量导入一些组件;
- 调用getCandidateConfigurations方法获取到所有需要到导入到容器中的配置类;
- 利用工厂加载loadSpringFactories得到所有的组件
- 从META-INF/spring.factories位置加载一个文件,默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件,spring-boot-autoconfigure-2.6.4.jar包里面也有META-INF/spring.factories
它里面有
# Auto Configure 127个
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
。。。。
这个文件里面也死了Springboot只要一启动就要给容器中加载所有的配置类。虽然我们127个场景的所有自动配置启动的时候默认全部加载,但是最后都会按照条件装配规则,会被按需配置。
2.2.3 @ComponentScan
【配置自动扫描包,就可以让类上带有@Component 和 @Repository,@Service注解的java类创建出对象】
@ComponentScan(basePackages="基础包名称")【主类上】 配置自动扫描包,将java类上带有
@Component("自定义名称"),@Repository("自定义名称"),@Service("自定义名称"),@Controller
注解的java类实例化。
代替了xml配置文件中
<context:component-scan base-package="基础包名称"></context:component-scan>
主程序所在包及其下面的所有子包里面的组件都会默认被扫描进来
3. SpringBoot的自动配置原理?
(1)SpringBoot启动的时候创建主配置类,因为@SpringBootApplication中包含了@SpringBootConfiguration,这个注解为我们创建好了主配置类。
(2)将其他的配置类信息合并到主配置类中,因为@SpringBootApplication中包含@EnableAutoConfiguration注解,@EnableAutoConfiguration注解中包含了@Import(AutoConfigurationImportSelector.class)注解,@Import(AutoConfigurationImportSelector.class)中的AutoConfigurationImportSelector.class里面的方法getCandidateConfigurations中读取并加载创建spring-boot-autoconfigure-2.6.4.jar/META-INF/spring.factories文件中的自动配置类。
(3)每一个自动配置类进行自动配置功能。
(4)根据当前不同的条件判断,决定这个配置类是否生效?一旦这个配置类生效,这个配置类就会给容器中添加各种组件,这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的。
(5)所有在配置文件中能配置的属性都是在xxxxPropertites类中封装着,配置文件能配置什么就可以参照某个功能对应的这个属性类;
以HttpEncodingAutoConfiguration (Http编码自动配置)为例解释自动配置原理:
@Configuration(proxyBeanMethods = false)
1: 如果为true, 则表示被@Bean标识的方法都会被CGLIB进行代理,而且会走bean的生命周期中的一些行为(比如:@PostConstruct,@Destroy等 spring中提供的生命周期), 如果bean是单例的,那么在同一个configuration中调用
@Bean标识的方法,无论调用几次得到的都是同一个bean,就是说这个bean只初始化一次。
2: 如果为false,则标识被@Bean标识的方法,不会被拦截进行CGLIB代理,也就不会走bean的生命周期中的一些行为(比如:@PostConstruct,@Destroy等 spring中提供的生命周期),如果同一个configuration中调用@Bean标识的方法,就只是普通方法的执行而已,并不会从容器中获取对象。所以如果单独调用@Bean标识的方法就是普通的方法调用,而且不走bean的生命周期。
所以,如果配置类中的@Bean标识的方法之间不存在依赖调用的话,可以设置为false,可以避免拦截方法进行代理操作,也是提升性能的一种优化。但是需要注意,@Bean标识的返回值对象还是会放入到容器中的,从容器中获取bean还是可以是单例的,会走生命周期。
@EnableConfigurationProperties({ServerProperties.class})
启动指定类的ConfigurationProperties功能,将配置文件中对应的值和HttpEncodingAutoConfigurationProperties绑定起来;
前缀server意味着在application.properties中可以修改默认值
@ConditionalOnWebApplication(type = Type.SERVLET)
Spring底层@Conditional注解,根据不同的条件,如果满足指定的条件,整个配置类里面的配置就会生效(即判断当前应用是否是web应用)
@ConditionalOnClass({CharacterEncodingFilter.class})
判断当前项目有没有这个类CharacterEncodingFilter;SpringMVC中进行乱码处理的过滤器
@ConditionalOnProperty(
prefix = "server.servlet.encoding",
value = {"enabled"},
matchIfMissing = true
)