浅谈SpringBoot的自动配置原理
SpringBoot的运行原理就是基于SpringBoot的自动配置来实现。我们要想熟练的使用SpringBoot这一框架技术,就必须对其深入学习。
一:
选择SpringBoot的依赖版本:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
<relativePath/>
</parent>
首先,我们先来看一下,当我们启动一个SpringBoot项目时,SpringBoot为我们自动启动了哪些自动配置类。
操作(以下三种方式任选其一):
(1)在application.properties中设置属性:
debug=true
(2)通过cmd命令窗口运行xxx.jar文件
java -jar xxx.jar --debug
(3)在Idea 中设置运行时参数
结果:
-—————————-
-—————————-
二:
接下来,我们需要学习一个注解@SpringBootApplication,这个注解是一个组合注解,他的核心功能是由@EnableAutoConfiguration注解提供。
好的,然后我们来看一下@EnableAutoConfiguration 的源码:
@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 {};
}
兄弟们,快看!!!这其中有一个@Import注解,注意到了吗???它的作用可是非常关键的!
它的作用就是将AutoConfigurationImportSelector类导入至Spring IOC容器。于是我们就进去看看,这个类中有一个selectImports方法。具体源码如下:
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return StringUtils.toStringArray(configurations);
}
}
在这个方法中,调用了AutoConfigurationMetadataLoader类的一个静态方法loadMetadata(ClassLoader classLoader)。
public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
return loadMetadata(classLoader, "META-INF/spring-autoconfigure-metadata.properties");
}
这个方法用来扫描具有”META-INF/spring-autoconfigure-metadata.properties”文件的jar包,而我们的spring-boot-autoconfigure-2.0.1RELEASE.jar里就有spring.factories文件,然后我们将其打开,查看一下此文件具体有哪些自动配置。如图所示:
然后我选择一个我们较为熟悉的HttpEncodingAutoConfiguration类
@Configuration //表示这是一个配置类,以前编写的配置文件一样,也可以给容器中添加组件
@EnableConfigurationProperties({HttpEncodingProperties.class}) //启动指定类的ConfigurationProperties功能;将配置文件中对应的值和HttpEncodingProperties绑定起来;并把HttpEncodingProperties加入到ioc容器中
@ConditionalOnWebApplication(
type = Type.SERVLET
)
@ConditionalOnClass({CharacterEncodingFilter.class}) //判断当前项目有没有这个类CharacterEncodingFilter ; SpringMVC中进行乱码解决的过滤器;
@ConditionalOnProperty(
prefix = "spring.http.encoding",
value = {"enabled"},
matchIfMissing = true
)//判断配置文件中是否存在某个配置spring.http.encoding.enabled ;如果不存在,判断也是成立的//即使我们配置文件中不配置pring.http.encoding.enabled=true ,也是默认生效的;
public class HttpEncodingAutoConfiguration {
private final HttpEncodingProperties properties;
//只有一个有参构造器的情况下,参数的值就会从容器中拿
public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) {
this.properties = properties;
}
@Bean //给容器中添加一个组件,这个组件的某些值需要从properties中获取
@ConditionalOnMissingBean //判断容器没有这个组件?
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpEncodingProperties.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpEncodingProperties.Type.RESPONSE));
return filter;
}
@Bean
public HttpEncodingAutoConfiguration.LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
return new HttpEncodingAutoConfiguration.LocaleCharsetMappingsCustomizer(this.properties);
}
private static class LocaleCharsetMappingsCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {
private final HttpEncodingProperties properties;
LocaleCharsetMappingsCustomizer(HttpEncodingProperties properties) {
this.properties = properties;
}
public void customize(ConfigurableServletWebServerFactory factory) {
if (this.properties.getMapping() != null) {
factory.setLocaleCharsetMappings(this.properties.getMapping());
}
}
public int getOrder() {
return 0;
}
}
}
以上源码就是根据当前不同的条件判断,决定这个配置类是否生效?一但这个配置类生效 ;这个配置类就会给容器中添加各种组件;这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的;
所有在配置文件中能配置的属性都是在XxxxProperties类中封装者;配置文件能配置什么就可以参照某个功
能对应的这个属性类,比如:
@ConfigurationProperties(
prefix = "spring.http.encoding"
) //从配置文件中获取指定值和bean的属性进行绑定
public class HttpEncodingProperties {
public static final Charset DEFAULT_CHARSET;
private Charset charset;
private Boolean force;
private Boolean forceRequest;
private Boolean forceResponse;
private Map<Locale, Charset> mapping;
public HttpEncodingProperties() {
this.charset = DEFAULT_CHARSET;
}
public Charset getCharset() {
return this.charset;
}
public void setCharset(Charset charset) {
this.charset = charset;
}
public boolean isForce() {
return Boolean.TRUE.equals(this.force);
}
public void setForce(boolean force) {
this.force = force;
}
public boolean isForceRequest() {
return Boolean.TRUE.equals(this.forceRequest);
}
public void setForceRequest(boolean forceRequest) {
this.forceRequest = forceRequest;
}
public boolean isForceResponse() {
return Boolean.TRUE.equals(this.forceResponse);
}
public void setForceResponse(boolean forceResponse) {
this.forceResponse = forceResponse;
}
public Map<Locale, Charset> getMapping() {
return this.mapping;
}
public void setMapping(Map<Locale, Charset> mapping) {
this.mapping = mapping;
}
public boolean shouldForce(HttpEncodingProperties.Type type) {
Boolean force = type == HttpEncodingProperties.Type.REQUEST ? this.forceRequest : this.forceResponse;
if (force == null) {
force = this.force;
}
if (force == null) {
force = type == HttpEncodingProperties.Type.REQUEST;
}
return force;
}
static {
DEFAULT_CHARSET = StandardCharsets.UTF_8;
}
public static enum Type {
REQUEST,
RESPONSE;
private Type() {
}
}
}
总结:
1) SpringBoot启动会加载大量的自动配置类
2) 我们看我们需要的功能有没有SpringBoot默认写好的自动配置类;
3) 我们再来看这个自动配置类中到底配置了哪些组件; (只要我们要用的组件有,我们就不需要再来配置了)
4) 给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们就可以在配置文件中指定这些属性的值;
5) xxxxAutoConfigurartion :自动配置类;给容器中添加组件
xxxxProperties:封装配置文件中相关属性;
还没有评论,来说两句吧...