Spring Boot
文章目录
pom.xml
父依赖
主要管理项目的资源过滤及插件
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
父项目还有一个父项目,这个才是真正管理springboot应用里所有依赖版本的地方,springboot的版本控制中心
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.3.RELEASE</version>
</parent>
以后我们导入依赖默认是不需要写版本的,但是如果导入的包没有在以来中管理,就需要手动配置版本
启动器 spring-boot-starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
spring-boot-starter-xxx: 就是springboot的场景启动器;
spring-boot-starter-web: 帮我们导入了web模块正常与逆行所以来的组件;
SpringBoot将所有的功能场景都抽取出来,做成一个个的starter(启动器),只需要在项目主引入这些starter即可,所有相关依赖都会导入进来, 我们需要什么功能就到处对应的启动器即可; 未来也可以自定义自己的 starter;
主启动类
@SpringBootApplication //标注这是一个springBoot的应用类
public class Springboot02WebApplication {
public static void main(String[] args) {
//以为是启动了一个方法,没想到是启动了一个服务tomcat
SpringApplication.run(Springboot02WebApplication.class, args);
}
}
@SpringBootApplication
作用: 标注这个类是springboot的主配置类, 运行这个类的main方法来启动springboot
点入层次结构如下:
@SpringBootConfiguration//标注这是一个springboot的配置类
|
|___@Configuration//这是一个配置类,对应spring的XML配置文件
|
|___@Component//这是一个组件,被注入spring中, 说明启动类本身也是spring的一个组件
@ComponentScan//它对应XML配置中的元素,自动扫描并加载符合条件的组件或者bean,将它定义加载到IOC容器
@EnableAutoConfiguration//开启自动配置功能, 加了它自动配置才能生效
|
|___@AutoConfigurationPackage//自动配置包
|
|___@Import(AutoConfigurationPackages.Registrar.class)//spring底层注解,给容器导入一个组件
//Registrar.class 作用: 将主启动类的所在包以及包下面所有子包里面的所有组件扫描到spring容器
|
|___@Import(AutoConfigurationImportSelector.class)//导入一个自动配置导入选择器
- AutoConfigurationImportSelector : 自动配置导入选择器, 点入查看源码
//获取候选的配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//这里的getSpringFactoriesLoaderFactoryClass()方法
//返回的就是我们最开始看的 启动自动导入配置文件的 注解类 EnableAutoConfiguration
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader());
return configurations;
}
- 这里又调用了SpringFactoriesLoader类的静态方法,进入SpringFactoriesLoader.loadFactoryNames()
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
//这里调用了loadSpringFactories方法
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
- 继续点击进入loadSpringFactories()方法
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
//获取classLoader, 我们返回可以看到这里得到的就是 EnableAutoConfiguration 注解类本身
MultiValueMap<String, String> result = cache.get(classLoader);
//去获取一个资源"META-INF/spring.factories"
Enumeration<URL> urls = (classLoader != null ?classLoader.getResources("META-INF/spring.factories") :ClassLoader.getSystemResources("META-INF/spring.factories"));
result = new LinkedMultiValueMap<>();
//将读取到的资源遍历, 封装成为一个 Properties
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
- 发现多次出现spring.factories
WebMvcAutoConfiguration
我们在上面的自动配置类中打开一个.可以看到这些都是JavaConfig配置类,而且都注入了一些bean
所以自动配置的实现是从classpath中搜寻所有的META-INF/spring.factories配置文件,并将其对应的org.springframwork.boot.autoconfigure.包下的配置项,通过反射实例化对应标注了@Configuration的JavaConfig形式的IOC容器配置类,然后将这些都汇总成为一个实例并加载到IOC容器中.
结论:
-
SptingBoot在启动的时候从类路径下的MATE-INF/spring.factories中获取EnableAutoConfiguration指定的值
-
将这些值作为自动配置类导入容器,自动配置类就生效,帮我们进行自动配置工作
-
整个J2EE的整体解决方案和自动配置都在sprigboot-autoconfigure的jar包中
-
他会给容器中导入非常多的自动配置类(xxxAutoConfiguration),就是给容器中导入这个场景需要的所有组件,并配置好这个组件
-
有了自动配置类,免去了我们手动编写配置注入功能组件等的工作
SpringApplication
他启动了一个服务
@SpringBootApplication
public class Springboot02WebApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot02WebApplication.class, args);
}
}
SpringApplication.run
SpringApplication类主要做了一下四件事:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//推断应用类型是普通的项目还是web项目
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//查找并加载所有可用初始化容器,设置到initializers属性中
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//找出所有的应用程序监听器,设置到listeners属性值
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//推断并设置main方法的定义类,找到运行的主类
this.mainApplicationClass = deduceMainApplicationClass();
}
run方法流程分析
自动配置原理
//表示这是一个配置类,可以编写的配置文件一样,也可以给容器中添加组件
@Configuration(proxyBeanMethods = false)
//启动指定类的ConfigurationProperties功能
//进入这个ServerProperties类查看,将配置文件中对应的值和ServerProperties绑定起来
//并把ServerProperties加入到IOC容器中
@EnableConfigurationProperties(ServerProperties.class)
//Spring底层@Conditional注解
//根据不同的条件判断,如果满足指定条件,整个配置类里面的配置就会生效
//这里的意思就是判断当前应用是否是Web应用,如果是,则配置类生效
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
//判断当前项目中有没有这个类CharacterEncodingFilter;SpringMVC中进行乱码解决的过滤器
@ConditionalOnClass(CharacterEncodingFilter.class)
//判断配置文件中是否存在某个配置:server.servlet.encoding.enabled
//即使不存在,判断也是成立的matchIfMissing
//即使我们配置文件中不配置server.servlet.encoding.enabled=true也是默认生效的
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
//它已经和SpringBoot的配置文件映射了
private final Encoding properties;
//只有一个有参构造器的情况下,参数的值就会从容器中拿
public HttpEncodingAutoConfiguration(ServerProperties properties) {
this.properties = properties.getServlet().getEncoding();
}
//给容器添加一个组件,这个组件的某些值需要从properties中获取
@Bean
@ConditionalOnMissingBean//判断容器中没有这个组件
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));
return filter;
}
//......
}
一句话总结:根据当前不同的条件判断,决定这个配置类是否生效
-
一但这个配置类生效,这个配置类就会给容器中添加各种组件
-
这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的
-
所有在配置文件中能配置的属性都是在xxxProperties类在封装着
-
配置文件能配置什么就可以参照某个功能对应的这个属性类
//从配置文件中获取指定的值和bean的属性进行绑定 @ConfigurationProperties(prefix = "server", ignoreUnknownFields = true) public class ServerProperties { //...... }
精髓
-
SpringBoot启动会加载大量的自动配置类
-
我们看我们需要的功能有没有在springBoot中默认写好的自动配置类中
-
我们再看这个自动配置类中到底配置了哪些组件;只要我们要用的组件存在其中,我们就不需要手动配置了
-
给容器中自动配置类添加组件的时候,会从properties类中获取某些属性.我们只需要在配置文件中指定这些属性的值即可
xxxAutoConfiguration:自动配置类,给容器中添加组件
xxxProperties:封装配置文件中相关属性
了解:@Conditional
自动配置类必须在一定的条件下才能生效
@Condational派生注解(Spring注解版原生的@Condational)
作用: 必须是@Conditional指定的条件成立,才给容器中添加组件,配置里面的所有内容才会生效
那么多自动配置类,必须在一定的条件下才能生效;也就是说,我们加在了那么多配置类,但不是所有的都生效了
我们怎么知道哪些自动配置类生效了?
可以通过开启debug=true的属性,来让控制台打印自动配置报告,这样我们可以很方便的知道哪些自动配置类生效
#开启SpringBoot的调试类
debug=true
Positive matches:(自动配置类启用的正匹配)
Negative matches:(没有启动,没有匹配成功的自动配置类:负配置)
Unconditional classes:(没有条件的类)