SpringBoot的一大好处就是:大大简化了Spring和其他框架和Spring整合时的配置,传统的SSM套装虽然很大程度地简化了Web开发,但是其的配置文件却较为繁琐,为了简化配置文件使开发者更专注于业务编码(懒)可以使用SpringBoot来进行web开发,其精简的配置和庞大繁茂的生态圈绝对令人惊叹!
SpringBoot之所以可以达到如此精简的配置,主要原因就是SpringBoot大量的自动配置!!!
自动配置原理:
SpringBoot应用从启动类的main方法中启动,加载SpringBoot主配置类(依赖@SpringBootApplication注解),主配置类(@SpringBootApplication):
1
2
3
4
5
6
7
8
9
10(ElementType.TYPE)
(RetentionPolicy.RUNTIME)
.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) (excludeFilters = { (type = FilterType.CUSTOM, classes = TypeExcludeFilter
public @interface SpringBootApplication {
。。。
}开启自动配置功能(依赖@EnableAutoConfiguration注解):
1
2
3
4
5
6
7
8
9(ElementType.TYPE)
(RetentionPolicy.RUNTIME)
.class) (AutoConfigurationImportSelector
public @interface EnableAutoConfiguration {
。。。
}用选择器(@Import(AutoConfigurationImportSelector.class))获取候选配置,给容器导入一些组件:
查看AutoConfigurationImportSelector类的public String[] selectImports(AnnotationMetadata annotationMetadata)方法:
1
2
3
4
5
6
7
8
9
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}通过selectImports方法间接调用getCandidateConfigurations(annotationMetadata, attributes)方法:
1
2
3
4
5
6
7protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}再调用SpringFactoriesLoader类的**loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader())**方法去扫描所有jar包类路径下的:
1
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF /spring.factories";
把扫描到的properties文件的内容包装成properties对象:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35private static Map<String, List<String>> loadSpringFactories( ClassLoader classLoader) {
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
String factoryClassName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
String factoryName = var9[var11];
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}从包装好的properties中获取到EnableAutoConfiguration.class类(类名)对应的值,然后把它们添加到容器中去。
☆上面这五个步骤大致讲述了SpringBoot自动配置的原理,可能还是比较抽象不易理解,接下来用一个例子再次具体分析理解☆
spring.factories中每一个XXXAutoConfiguration类都是容器中的一个组件,都加入到容器中,用他们来做自动配置。每一个XXX自动配置类都可以进行自动配置功能,举个简单的例子(HttpEncodingAutoConfiguration):
首先找到HttpEncodingAutoConfiguration类:
1 | //表明这是一个配置类,像编写配置文件一样,也可以向容器中添加组件 |
根据当前不同的条件判断,决定这个配置类是否生效:一旦这个配置类生效,这个配置类就会给容器中添加各种组件,这些组件的属性是从对应的properties类中获取的,properties类里面的每一个属性又是和配置文件绑定的。
来看一看HttpProperties类:
1 | "spring.http")//从配置文件中获取指定的值和bean的属性进行绑定 (prefix = |
这时在我们的application.properties文件中加一条配置:
1 | utf-8 = |
ctrl左键这个Key,发现居然跳到了上面HttpProperties类的Encoding静态内部类的setCharset(Charset charset)方法!!这下子恍然大悟了!当然这只是大量组件中较为简单的一个,但是每个组件的自动配置逻辑大同小异,只有掌握了SpringBoot的这一精髓,才能更好对其他的细节进行深入理解!
自定义配置方法
用户很多时候不可避免的进行自定义配置,SpringBoot除了自动配置,理所当然的支持用户的自定义配置!
SpringBoot的自定义配置主要有两种:1.使用配置文件进行外部属性配置。2.用配置类进行配置。
接下来对两种配制方法展开说明:
1.使用配置文件进行外部属性配置:
SpringBoot中比较常见且推荐的是.properties和.yml两种配置文件。
想知道配置文件中所有可配属性,可以在SpringBoot官方说明文档中找到 Common application properties——SpringBoot2.1.8
YAML是JSON的一个超集,可以将外部配置以层次结构形式存储起来。当项目的类路径中用SnakeYAML库(spring-boot-starter中已经被包含)时,SpringApplication类将自动支持YAML作为properties的替代。所以在优先级上YAML>properties。
YAML的数据格式和JSON很像,都是树状结构都是K-V格式,并通过“:”进行赋值。
properties文件中以”.”进行分割的,在yml文件中用”:”进行分割的。yml文件的每个”:”后面一定都要加一个空格,否则文件会报错。
@Value和@ConfigurationProperties:
可以在编写代码时在属性上使用@Value($(key))来取值并对被注解的属性赋值,也可以在类上使用@ConfigurationProperties(prefix=”xxx”)注解取出所有以xxx为前缀的key所对应的值来对被注解类的属性进行匹配并赋值。
功能 | @ConfigurationProperties | @Value |
---|---|---|
松散绑定(松散语法) | 支持 | 不支持 |
SpEL | 不支持 | 支持 |
JSR303数据校验 | 支持 | 不支持 |
复杂类型封装 | 支持 | 不支持 |
无论是yml文件还是properties文件,这两个注解都能获取到值。
如果只是某个业务逻辑中需要获取一下配置文件中的某个值,建议使用@Value。
如果是编写一个JavaBean和配置文件进行映射,建议直接用@ConfigurationProperties。
@PropertySesource和@ImportResource:
上面介绍的两个注解都是默认从默认全局配置文件(application.properties或application.yml)中读取值,如果需要加载指定配置文件中的值,则需要使用@PropertySesource(value = {“classpath:xxx.propertiex”},…..)来指定需要加载的一个或多个配置文件。
在使用springboot的时候一般是极少需要添加配置文件的(application.properties除外),但是在实际应用中也会存在不得不添加配置文件的情况,例如集成其他框架或者需要配置一些中间件等,在这种情况下,我们就需要引入我们自定义的xml配置文件了。在SpringBoot中依然支持xml文件的配置方式。但是需要在启动类上加@ImportResource(locations = {“classpath:xxx.xml”},…..)注解来指定一个或多个xml文件的位置从而对框架或中间件进行配置。
2.用配置类进行配置:
WebMvcConfigurationSupport
SpringBoot2.0之后建议自定义配置类继承WebMvcConfigurationSupport,在自定义配置类上加上@Component注解就可以自动扫描到配置类并加载了。
WebMvcConfigurationSupport这个类中提供了很多很多方法大多数方法都是顾名思义:处理器异常解析器、添加拦截器等等。。。很多很便利的方法。
1 |
|
@Bean
可以对配置类中的方法进行注解,将方法的返回值添加到容器中,容器中这个组件默认的id就是方法名。
1 |
|