springboot的自动配置流程:
主程序启动会扫描@SpringBootApplication
@SpringBootApplication
public class HelloWorldMainApplication {
public static void main(String[] args) {
SpringApplication.run(HelloWorldMainApplication.class,args);
}
}
@SpringBootApplication元信息
起到主要作用的是@SpringBootConfiguration和@EnableAutoConfiguration
@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:
其实就是@Configuration,标注当前类为主配置类(主容器),当前类指的HelloWorldMainApplication
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
@EnableAutoConfiguration:
包含两个重要注解,@AutoConfigurationPackage和@Import({EnableAutoConfigurationImportSelector.class})
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({EnableAutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
@AutoConfigurationPackage的作用:相当于包扫描器,扫描主配置类所在包下的所有自定义的bean,例如@Controller、@Service等注解标识的bean。
它的内部是通过@Import({Registrar.class}),利用ImportBeanDefinitionRegistrar的registerBeanDefinitions方法,向BeanDefinitionRegistry注册主配置类所在包下自定义的逻辑组件
@Import({EnableAutoConfigurationImportSelector.class})的作用:通过ImportSelector选择器执行selectImports方法获得最终启用(符合当前场景)的自动配置类的全类名的String数组,主配置类就会把这些自动配置类的bean加载进spring容器,@Configuration其实也是spring容器的组件,内部@Component修饰
selectImports方法内部:
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
try {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
configurations = this.sort(configurations, autoConfigurationMetadata);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return (String[])configurations.toArray(new String[configurations.size()]);
} catch (IOException var6) {
throw new IllegalStateException(var6);
}
}
}
自动配置的核心就在于selectImports方法,下面一步步的剖析这个方法
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
return loadMetadata(classLoader, "META-INF/spring-autoconfigure-metadata.properties");
}
org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration=
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration.ConditionalOnClass=org.apache.solr.client.solrj.SolrClient,org.springframework.data.solr.repository.SolrRepository
org.springframework.boot.autoconfigure.mobile.SitePreferenceAutoConfiguration.Configuration=
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration.AutoConfigureBefore=org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration.Configuration=
org.springframework.boot.autoconfigure.jms.artemis.ArtemisXAConnectionFactoryConfiguration=
static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources(path) : ClassLoader.getSystemResources(path);
Properties properties = new Properties();
while(urls.hasMoreElements()) {
properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource((URL)urls.nextElement())));
}
return loadMetadata(properties);
} catch (IOException var4) {
throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", var4);
}
}
loadMetadata方法内部遍历应用下的所有jar,加载了spring-boot-autoconfigure-1.5.9.RELEASE.jar下的META-INF/spring-autoconfigure-metadata.properties,配置文件里面的信息为自动配置类xxxAutoConfiguration的@ConditionalOnClass、@AutoConfigureAfter、@AutoConfigureOrder等注解参数的key-value信息,因为自动配置类是否启用要看符不符合这些条件,如果启用,还要对这些自动配置类的加载顺序进行排序,返回的autoConfigurationMetadata 封装了这些信息的Properties,Properties把spring-autoconfigure-metadata.properties封装了
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
参数annotationMetadata为主启动类的信息(以及它的注解及元注解信息),返回的attributes为@EnableAutoConfiguration注解的exclude和excludeName的值
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
}
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
ArrayList result = new ArrayList();
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
String factoryClassNames = properties.getProperty(factoryClassName);
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
}
return result;
} catch (IOException var8) {
throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8);
}
}
只截取一部分,org.springframework.boot.autoconfigure.EnableAutoConfiguration为key
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
getCandidateConfigurations方法返回所有自动配置类的全类名集合(从META-INF/spring.factories原封不动地拿),参数getSpringFactoriesLoaderFactoryClass()其实就EnableAutoConfiguration.class类对象,loadFactoryNames方法内部会读取该应用所有jar包下的META-INF/spring.factories,(其实只有spring-boot-autoconfigure-1.5.9.RELEASE.jar包下有)取出factories文件中key为EnableAutoConfiguration全类名的值的集合,装进集合configurations 返回
configurations = this.removeDuplicates(configurations);
返回原集合,不必理会
configurations = this.sort(configurations, autoConfigurationMetadata);
对集合排序,根据autoConfigurationMetadata里面的AutoConfigureOrder、AutoConfigureAfter、AutoConfigureBefore等信息对自动配置类集合configurations排序,自动配置类加载是有顺序的,
例如WebMvcAutoConfiguration,就必须等到DispatcherServletAutoConfiguration加载成功之后再加载,而且还要根据@Order的派生注解@AutoConfigureOrder决定加载优先级
@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
处理需要排除的自动配置类,不必理会
configurations = this.filter(configurations, autoConfigurationMetadata);
根据@ConditionalOnClass(类.Class)、@ConditionalOnMissingBean(类.Class)等注解,判断注解中的类.Class是否存在或不存在,从而筛选出符合启用的自动配置类。
例如我们的应用没有导入redis场景的starter,而RedisAutoConfiguration里
@Configuration
@ConditionalOnClass({JedisConnection.class, RedisOperations.class, Jedis.class})
@EnableConfigurationProperties({RedisProperties.class})
public class RedisAutoConfiguration {
如果我们应用里没有这三个JedisConnection.class, RedisOperations.class, Jedis.class,那么RedisAutoConfiguration就不会生效,filter方法会把它过滤掉,最终返回的configurations就是经过过滤后的、符合我们当前应用场景的自动配置类的String数组。
这些自动配置类在被@Import进主容器之后,spring创建这些自动配置类的bean,这些bean各自完成初始化自己内部组件的工作
至此,springboot自动配置类的默认初始化完成
springboot修改默认配置:
springboot为我们自动配置了各个场景下的所有默认配置,那它是如何设置的默认配置呢,再者如果我们需要修改默认配置,应该从哪入手?
首先要明确一点,每个自动配置类的主要功能就是给容器中加入所需的组件
以HttpEncodingAutoConfiguration为例
例如HttpEncodingAutoConfiguration类给容器中加入一个解决编码的CharacterEncodingFilter组件
HttpEncodingAutoConfiguration类信息如下:
@Configuration //表示这是一个配置类,以前编写的配置文件一样,也可以给容器中添加组件
@EnableConfigurationProperties(HttpEncodingProperties.class) //启动指定类的 ConfigurationProperties功能;将配置文件中对应的值和HttpEncodingProperties绑定起来;并把 HttpEncodingProperties加入到ioc容器中,以便自动配置类以有参构造器的方式注入Properties
@ConditionalOnWebApplication //Spring底层@Conditional注解(Spring注解版),根据不同的条件,如果 满足指定的条件,整个配置类里面的配置就会生效; 判断当前应用是否是web应用,如果是,当前配置类生效
@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(CharacterEncodingFilter.class) //判断容器有没有这个组件? 没有才会生效
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
return filter;
}
}
我们只需要指定HttpEncodingProperties类中的charset属性的值即可,而HttpEncodingProperties通过@ConfigurationProperties(prefix = "")又和配置文件绑定了,
这里说的配置文件是我们应用的配置文件,因为spring-boot-autoconfigure-1.5.9.RELEASE.jar没有配置文件,会扫描我们应用的配置文件,
只要对应上prefix前缀和HttpEncodingProperties类的属性名就行了
HttpEncodingProperties类如下:
可以看出charset的默认值是UTF-8
@ConfigurationProperties(prefix = "spring.http.encoding") //从配置文件(我们自己应用的配置文件)中获取指定的值和bean的属性进行绑定
public class HttpEncodingProperties {
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private Charset charset;
private Boolean force;
private Boolean forceRequest;
private Boolean forceResponse;
private Map<Locale, Charset> mapping;
public HttpEncodingProperties() {
this.charset = DEFAULT_CHARSET;
}
set/get方法......
}
我们自己在application.properties中自定义配置
spring.http.encoding.enabled=true
spring.http.encoding.charset=gbk
spring.http.encoding.force=true