背景:
三天前结束了 MyBatis 的学习,SSM框架的学习也告一段落。本想写一个小Demo来锻炼一下巩固一下学习的知识,却发现自己前端的东西一点不会,还是太菜了(ノへ ̄、)
而且在 Github 上找到的项目大部分都是 SpringBoot 为骨架搭建的,没办法还是开启 SpringBoot 的学习。然而事情一般不会这么简单,老师一上课还没有写出 Demo 就直接开始讲底层源码,呕吼,直接亲妈上天(;´д`)ゞ
现在还是经过一下午的整理和晚上视频的学习,再次梳理一遍原理
一、项目结构
Version 2.2.4
当我们创建项目完成时,将会出现如上图所示的项目结构。
二、入口及其原理分析
其中的 SpringBootApplication
就是整个项目的主入口,当我们打开此文件时,就会看到如下的代码
package com.biggun;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
// 程序的主入口
@SpringBootApplication
public class SpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootApplication.class, args);
}
}
而我们需要重点关注的则是这个 @SpringBootApplication
注解,因为存在这个注解,这个入口才能被 SpringBoot
识别并启动。
为了进行原理分析,我们找到这个接口注解的源代码,可以看到其定义如下
@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) })
public @interface SpringBootApplication {
...
}
现在自动装配的秘密就在我们眼前,主要要分析的还是两个定义
- @SpringBootConfiguration
- Spring Boot配置
- @EnableAutoConfiguration
- 开启自动配置
1. @SpringBootConfiguration
1.1 源码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
可以看到这个注解又定义上了另一个关键注解 @Configuration
,这里的 @Configuration
对我们来说不陌生,它就是 JavaConfig
形式的 Spring Ioc
容器的配置类使用的那个 @Configuration
,SpringBoot
社区推荐使用基于 JavaConfig
的配置形式,所以,这里的启动类标注了 @Configuration
之后,本身其实也是一个 IoC
容器的配置类。同样的,我们打开这个注解看一下
1.2 @Configuration
同样是源码部分
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
...
}
可以看到定义中出现了 @Component
注解,表示这个注解的意义为其标注的类会被视为是一个组件,交给 IOC容器接管。
其作用相当于 XML 配置中的
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
default-lazy-init="true">
<!--bean定义-->
</beans>
在标注了此注解的类中方法可以使用 @Bean
,来起到注册 bean 的作用,等同于
<bean name="bean" class="com.biggun.pojo.Biggun">
...
</bean>
1.3 作用
@SpringBootConfiguration
标注在某个类上,表示这个类是一个 SpringBoot
的配置类,而这个注解由 @Configuration
定义,与其拥有相同的功能,即使被标注的类具有等同于 XML
配置文件的作用,也就是 JavaConfig
。
而之间的区别在于一个是 SpringBoot
定义的注解,一个是 Spring
定义的注解
2. @EnableAutoConfiguration
相较 Spring 、 SpringMVC ,我们使用 SpringBoot 的时候,不再需要进行任何配置就可以直接使用,这些配置都由 SpringBoot 为我们完成。这个功能就是由 @EnableAutoConfiguration
完成的。
2.1 源码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
可以看到这个注解主要还是由两个部分构成
-
@AutoConfigurationPackage
-- 自动配置包 -
@Import(AutoConfigurationImportSelector.class)
-- 自动配置导入选择器
2.2 引用
2.2.1 @AutoConfigurationPackage
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
当我们进入 @AutoConfigurationPackage
的源码时,可以发现,在这个注解的定义中出现了 @Import
注解,这个注解是 Spring
的底层注解,在此处的作用为为 Spring
导入一个组件,导入的组件由 AutoConfigurationPackages.Registrar
类决定
2.2.1.1 AutoConfigurationPackages.Registrar
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImport(metadata));
}
}
当我们进入 AutoConfigurationPackages.Registrar
这个静态内部类时,可以发现这个类有两个方法
registerBeanDefinitions()
-- 注册bean定义determineImports
-- 确定入口
我们的关注点主要在 registerBeanDefinitions()
的函数调用上,它通过调用 register
函数来注册 bean
的信息,而且注册 bean
信息的时候需要获取 bean
的元数据 -- metadata
,从而拿到包的名字
为了更加深入的了解其运行原理,我们在此处设置一个断点
并启动 Debug
模式,可以看到
元数据中包含了四个项,但我们主要就看两项
mergedAnnotation
-- 合并注解introspectedClass
-- 自检查类
打开其包含的项可以发现 SpringBoot
已经加载到了程序的主入口类,以及入口类所在的包名
此时我们可以计算一下 new PackageImport(metadata).getPackageName()
所得到的信息
正是主入口类所在的包名
所以可以得出, @EndbleAutoConfiguration
的作用就是将主入口类所在的包下的所有子包中的所有组件导入到 Spring
容器中
故而如果我们将其他的组件不放在主入口类的同级目录下,Spring
容器就无法获取到这个组件的相关信息,也就无法访问到
2.2.2 @Import(AutoConfigurationImportSelector.class)
AutoConfigurationImportSelector.class -- 自动配置导入选择器
当我们进入这个类中,我们可以发现有 AutoConfigurationEntry()
这样一个方法,而这个方法的作用则是返回所有需要导入的组件的全类名,这些组件就会被添加到容器中,会给容器导入非常多的自动配置类 XXAutoConfiguration
,就是给容器导入某个而场景所需要的所有组件,并配置好这些组件
在这个方法中调用了一个方法 getCandidateConfiguration()
,用来获取候选的配置
在 Debug
模式下调试就可以看到,当 SpringBoot
执行这个函数时,会获取到如下的组件
有了自动配置类,就免去了我们编写配置文件和注入功能组件等的工作。
但是 getCandidate()
函数又是如何获取到这些参数的呢?我们就需要打开这个类的定义
可以发现它主要调用了 SpringFatoriesLoader.loadFactoryNames()
这样一个静态函数,调用了两个值
getSpringFactoriesLoaderFactoryClass()
- return EnableAutoConfiguration.class;
getBeanClassLoader()
通过 getSpringFactoriesLoaderFactoryClass()
的定义可知,该函数返回的是一个自动配置的标记,由此标记 Spring
容器才能对其相关的组件进行自动装配
getBeanClassLoader()
,一目了然,这是一个典型的对 POJO
类获取属性值的方法,在此之前必然有其余的函数调用对此属性进行值注入
而 SpringFatoriesLoader.loadFactoryNames()
的作用尚不明晰,故我们又需要进入这个静态函数查看其定义
可以发现我们调用的 SpringfatoriesLoader.loadFactoryNames()
,实际上是调用了同类下的一个静态方法 loadSpringFactories()
在第 133 行,使用类加载器,获取一个静态资源
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
在 139 行,会在类路径下加载完这个静态资源作为一个配置文件 properties
,然后获取到这个配置集合中每个配置的工厂类型名称。
那么总结一下,``SpringBoot启动时调用的 **
SpringfatoriesLoader.loadFactoryNames()**会调用
SpringfatoriesLoader.loadSpringFactories()在类路径下的
META-INF/spring.factories中获取
EnableAutoConfiguration` 指定的值,将这些值作为自动配置类导入容器中,自动配置类就生效,免去我们手动配置。
按照这个信息,我们其实可以在项目资源中来到这些信息,而这些信息也就是我们之前计算出来的值。
以 SpringMVC
为例,SpringBoot
可以做到开箱即用,以前需要配置的过滤器,DispatcherServlet
等等都不需要我们再手动去配置,做到开箱即用,也是因为在这个 spring.factories
中配置了 WebServicesAutoConfiguration
当我们进入这个文件中就会发现许多的配置,如 Filter
,DispatcherServlet
等等
回到之前 Configurations
的配置计算中,可以发现计算出来的所有组件都是在同一个包目录下的
同样的,寻找目录,可知这就是 SpringBoot
对 J2EE
应用包的大整合,其整体整合解决方案和自动配置都在 spring-boot-autoconfigure-2.2.4.RELEASE.jar
中了
以后当我们需要使用对应的模块就只需要在其中找到对应的类,对其中的属性进行配置即可
三、思维导图
参考视频、资料 :
狂神说 SpringBoot:https://www.bilibili.com/video/av75233634?p=6
尚硅谷 SpringBoot 权威教程:https://www.bilibili.com/video/av38657363?p=6
Spring Boot干货系列:(三)启动原理解析 http://tengj.top/2017/03/09/springboot3