运作原理
调整配置可以打印出日志
通过启用 debug=true属性;来让控制台打印自动配置报告
原作原理-consol-未启用的自动配置.png 原作原理-consol-已启用的自动配置.png
SpringBoot启动的时候加载主配置类,开启了自动配置功能 @EnableAutoConfiguration
@EnableAutoConfiguration注解的源码
一旦加上此注解,那么将会开启自动装配功能,简单点讲,Spring会试图在你的classpath下找到所有配置的Bean然后进行装配。当然装配Bean时,会根据若干个(Conditional)定制规则来进行初始化。
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;
@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 {};
}
AutoConfigurationImportSelector
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
}
public interface DeferredImportSelector extends ImportSelector {}
该类实现了DeferredImportSelector接口,这个接口继承了ImportSelector:
该接口主要是为了导入@Configuration的配置项,而DeferredImportSelector是延期导入,当所有的@Configuration都处理过后才会执行。
EnableAutoConfiguration中的AutoConfigurationImportSelector类扫描spring.factorise文件
AutoConfigurationImportSelector的selectImports方法
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
该方法刚开始会先判断是否进行自动装配,而后会从META-INF/spring-autoconfigure-metadata.properties读取元数据与元数据的相关属性,紧接着会调用getCandidateConfigurations方法:
原作原理-自动装配-spring.factories.png protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
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 new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
ConditionalOnWebApplication类分析
package org.springframework.boot.autoconfigure.condition;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Conditional;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnWebApplicationCondition.class})
public @interface ConditionalOnWebApplication {
ConditionalOnWebApplication.Type type() default ConditionalOnWebApplication.Type.ANY;
public static enum Type {
ANY,
SERVLET,
REACTIVE;
private Type() {
}
}
}
OnWebApplicationCondition.class
private ConditionOutcome isWebApplication(ConditionContext context, AnnotatedTypeMetadata metadata, boolean required) {
switch(this.deduceType(metadata)) {
case SERVLET:
return this.isServletWebApplication(context);
case REACTIVE:
return this.isReactiveWebApplication(context);
default:
return this.isAnyWebApplication(context, required);
}
}
isWebApplication方法可以看出,判断条件是:
springboot内置的自动装配功能
http的编码配置
常规web项目的web.xml里配置一个filter
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter
</filter-class>
<async-supported>true</async-supported>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
自动配置需要满足两个条件
1、能配置CharacterEncodingFilter这个bean
2、能配置encoding和forceEncoding这两个参数
HttpEncodingAutoConfiguration 类
根据条件配置characterEncodingFilter的bean
package org.springframework.boot.autoconfigure.web.servlet;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.autoconfigure.http.HttpProperties;
import org.springframework.boot.autoconfigure.http.HttpProperties.Encoding;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.filter.OrderedCharacterEncodingFilter;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.filter.CharacterEncodingFilter;
@Configuration
// 开启属性注入 通过 @EnableConfigurationProperties 生命,
@EnableConfigurationProperties({HttpProperties.class})
@ConditionalOnWebApplication(
type = Type.SERVLET
)
// 当CharacterEncodingFilter在类路径的条件下
@ConditionalOnClass({CharacterEncodingFilter.class})
// 当设置spring.http.encoding=enabled的情况下,如果没有设置则默认为true,既条件符合
@ConditionalOnProperty(
prefix = "spring.http.encoding",
value = {"enabled"},
matchIfMissing = true
)
public class HttpEncodingAutoConfiguration {
private final Encoding properties;
public HttpEncodingAutoConfiguration(HttpProperties properties) {
this.properties = properties.getEncoding();
}
@Bean
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.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 Encoding properties;
LocaleCharsetMappingsCustomizer(Encoding properties) {
this.properties = properties;
}
public void customize(ConfigurableServletWebServerFactory factory) {
if (this.properties.getMapping() != null) {
factory.setLocaleCharsetMappings(this.properties.getMapping());
}
}
public int getOrder() {
return 0;
}
}
}
相关注释
@ConfigurationPropertie注解
@Component
@ConfigurationProperties(prefix = "author")
@Data
public class AuthorSettings {
private String name;
private Long age;
}
author:
name: SSH
age: 18
使用该注解,可以将配置文件数据引入到类中。把配置文件的信息,读取并自动封装成实体类
@ConditionalOnClass
被引用时@ConditionalOnClass(HelloService.class)
ConditionalOnClass源码类
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Conditional;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnClassCondition.class})
public @interface ConditionalOnClass {
Class<?>[] value() default {};
String[] name() default {};
}
@Conditional指定的条件成立,才给容器中添加组件,配置的所有内容才生效;
点开该注解 @Conditional({OnClassCondition.class})
OnClassCondition类有继承了FilteringSpringBootCondition
最终还是实现Condition 接口
即根据条件返回是否为true来决定支付注册这个bean
类似的:@ConditionalOnWebApplication、@ConditionalOnProperty 满足一定条件才做什么
1、
@Conditional({OnClassCondition.class})
2、
OnClassCondition extends FilteringSpringBootCondition
3、
FilteringSpringBootCondition extends SpringBootCondition
4、
SpringBootCondition implements Condition
@ConditionalOnProperty
@ConditionalOnProperty(prefix = "hello", value = "enable", matchIfMissing = true)
前缀 hello吗,matchIfMissing 为true 没有配置时也会正常加载,在下边的demo中会被引用
package org.springframework.boot.autoconfigure.condition;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Conditional;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional({OnPropertyCondition.class})
public @interface ConditionalOnProperty {
/**
* 数组,获取对应property名称的值,与name不可同时使用
*
* @return
*/
String[] value() default {};
/**
* property名称的前缀,可有可无
* @return
*/
String prefix() default "";
/**
* 数组,property完整名称或部分名称(可与prefix组合使用,组成完整的property名称),与value不可同时使用
* @return
*/
String[] name() default {};
/**
* 可与name组合使用,比较获取到的属性值与havingValue给定的值是否相同,相同才加载配置
* @return
*/
String havingValue() default "";
/**
* 缺少该property时是否可以加载。如果为true,没有该property也会正常加载;反之报错
* @return
*/
boolean matchIfMissing() default false;
}
打开OnPropertyCondition.class类
OnPropertyCondition 类继承 SpringBootCondition
SpringBootCondition 实现Condition 接口
自动装配Demo
不仅是自动装配,更是低耦合的配置
新建项目:
spring-boot-starter-hello
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>hand.shen</groupId>
<artifactId>spring-boot-starter-hello</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>starter-hello</name>
<description>starter-hello project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>
HelloServiceProperties.java
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @description: spring-boot 自动配置作为依赖
* 类型安全的属性获取。
* 在yml文件中 通过 hello.msg = 来设置,如果不设置默认 hello.meg = word
*
* @author: Shenshuaihu
* @version: 1.0
* @data: 2019-07-10 23:26
*/
@ConfigurationProperties(prefix = "hello")
@Data
public class HelloServiceProperties {
private static final String MSG = "word";
private String msg = MSG;
}
HelloService.java
import lombok.Data;
/**
* @description: 判断依据
*
* @author: Shenshuaihu
* @version: 1.0
* @data: 2019-07-10 23:30
*/
@Data
public class HelloService {
private String msg;
public String sayHello() {
return "Hello" + msg;
}
}
HelloServiceAutoConfiguration.java
@ConditionalXXX注释上文有
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @description: 自动配置类
*
* 注释 @ConditionalOnClass 会判断HelloService这个类的路径是否存在,
* 如果没有了 则会自动创建
*
* @author: Shenshuaihu
* @version: 1.0
* @data: 2019-07-10 23:35
*/
@Configuration
@EnableConfigurationProperties(HelloServiceProperties.class)
@ConditionalOnClass(HelloService.class)
@ConditionalOnProperty(prefix = "hello", value = "enable", matchIfMissing = true)
public class HelloServiceAutoConfiguration {
@Autowired
private HelloServiceProperties helloServiceProperties;
@Bean
@ConditionalOnMissingBean(HelloService.class)
public HelloService helloService() {
HelloService helloService = new HelloService();
helloService.setMsg(helloServiceProperties.getMsg());
return helloService;
}
}
spring.factories
文件路径 resource/MEFA-INF/spring.factories,指定文件装配的类
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.starterhello.HelloServiceAutoConfiguration
mvn install 安装到本地库
在另外一个项目中开启
在pom.xml 中引入jar
<dependency>
<groupId>hand.shen</groupId>
<artifactId>spring-boot-starter-hello</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
直接注入helloservice
import com.starterhello.HelloService;
@Autowired
private HelloService helloService;
@RequestMapping("/")
String index() {
return "Hello Spring Boot," +
" \n 自动装配:" + helloService.sayHello();
}
yml配置
# 自动装配
hello:
msg: shenshauihu
原作原理-自动装配-hello.png如何使用了,如果yml文件上没有配置hello:msg中,会使用默认是helloword,没有配置了hello:msg为shenshuaihu时,则会使用了helloshenshuaihu
参考文档:
https://www.cnblogs.com/niechen/p/9027804.html?utm_source=tuicool&utm_medium=referral
https://www.jianshu.com/p/83693d3d0a65
https://www.jianshu.com/p/1d96508085ff
网友评论