springboot的主启动类分析(默认情况下):
原类:
@SpringBootApplication // @SpringBootApplication 来标注一个主程序类,说明这是一个Spring Boot应用
public class SuperMallApplication {
public static void main(String[] args) {
SpringApplication.run(SuperMallApplication.class, args);
}
}
1.@SpringBootApplication //@SpringBootApplication 来标注一个主程序类,说明这是一个Spring Boot应用 ,是把多个元注解的属性组合在一起形成新的注解
源码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited //这个注解可以被继承
@SpringBootConfiguration //这个注解实际上和@Configuration有相同的作用,配备了该注解的类就能够以JavaConfig的方式完成一些配置,可以不再使用XML配置。
@EnableAutoConfiguration //
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
//组合元注解EnableAutoConfiguration的exclude属性,作用:排除特定的自动配置类,使其永远不会被自动配置,通过 .class的方式指定
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
/组合元注解EnableAutoConfiguration的excludeName属性,作用:排除特定的自动配置类,使其永远不会被自动配置,通过类名的方式指定
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
//扫描指定报下的实体并且加入到spring容器中,参数是包名的字符串数组,
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
//扫描指定报下的实体并且加入到spring容器中,参数类似是Class类型数组。
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
2.@AutoConfigurationPackage
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class) //使用Spring的底层注解@Import,给容器中导入AutoConfigurationPackages.Registrar.组件
public @interface AutoConfigurationPackage {
}
AutoConfigurationPackages.Registrar组件的功能:
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
//将注解标注的元信息传入,获取到相应的包名
register(registry, new PackageImport(metadata).getPackageName());
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImport(metadata));
}
}
总结: 因此可以得知使用@AutoConfigurationPackage注解就是将主程序类所在包及所有子包下的组件到扫描到spring容器中
3.@EnableAutoConfiguration springboot的配置注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited //表明这个注解是可以被继承的
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class) //使用Spring的底层注解@Import,给容器中导入AutoConfigurationImportSelector组件
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 {
//......
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// 如果AutoConfiguration没开,返回{}
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// 将spring-autoconfigure-metadata.properties的键值对配置载入到PropertiesAutoConfigurationMetadata对象中并返回
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
// 基于各种配置计算需要import的configuration和exclusion
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
// 判断AudoConfiguration是否开启
protected boolean isEnabled(AnnotationMetadata metadata) {
if (getClass() == AutoConfigurationImportSelector.class) {
// 如果配置文件中有"spring.boot.enableautoconfiguration",返回该字段的值;否则返回true
return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);
}
return true;
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 获取注解的属性值
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 从META-INF/spring.factories文件中获取EnableAutoConfiguration所对应的configurations
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 去重,List转Set再转List
configurations = removeDuplicates(configurations);
// 从注解的exclude/excludeName属性中获取排除项
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
// 对于不属于AutoConfiguration的exclude报错
checkExcludedClasses(configurations, exclusions);
// 从configurations去除exclusions
configurations.removeAll(exclusions);
// 由所有AutoConfigurationImportFilter类的实例再进行一次筛选,去
configurations = filter(configurations, autoConfigurationMetadata);
// 把AutoConfigurationImportEvent绑定在所有AutoConfigurationImportListener子类实例上
fireAutoConfigurationImportEvents(configurations, exclusions);
// 返回(configurations, exclusions)组
return new AutoConfigurationEntry(configurations, exclusions);
}
// ......
}
总结:可见selectImports()是AutoConfigurationImportSelector的核心函数,其核心功能就是获取spring.factories中EnableAutoConfiguration所对应的Configuration类列表,
由@EnableAutoConfiguration注解中的exclude/excludeName参数筛选一遍,再由AutoConfigurationImportFilter类所有实例筛选一遍,得到最终的用于Import的configuration和exclusion。
该函数在org.springframework.context.annotation.ConfigurationClassParser类中被processImports()调用,而processImports()函数被doProcessConfigurationClass()调用。
详细解释这位老兄写的很到位:https://www.cnblogs.com/desertfish/p/11637933.html
注意:
// AutoConfigurationImportSelector-->getAutoConfigurationEntry()-->getCandidateConfigurations()
protected 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类下方法
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
//SpringFactoriesLoader类下方法
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
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;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
在AutoConfigurationImportSelector自动配置导入选择组件中可以看到,getCandidateConfigurations方法会执行最终会调用SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
SpringFactoriesLoader.loadFactoryNames() 会扫描所有jar包类路径下 META‐INF/spring.factories 把扫描到的这些文件的内容包装成properties对象
从properties中获取到EnableAutoConfiguration.class类(类名)对应的值,每一个这样的 xxxAutoConfiguration类都是容器中的一个组件,都加入到容器中然后用他们来做自动配置;
spring.factories部分内容:
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,\
...
简单分析一个:HttpEncodingAutoConfiguration
@Configuration
//启动指定类的@ConfigurationProperties的配置功能,将配置文件对应的值和HttpEncodingProperties的属性绑定起来
@EnableConfigurationProperties(HttpEncodingProperties.class)
//条件注解:条件满足的时候这个配置类才能生效
@ConditionalOnWebApplication //这个就是当前的springboot项目是一个web项目的时候HttpEncodingAutoConfiguration配置类才能生效
//条件注解:条件满足的时候这个配置类才能生效,条件是当前项目必须有CharacterEncodingFilter这个类
@ConditionalOnClass(CharacterEncodingFilter.class)
//条件注解:判断配置文件中是否存在某个属性
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
private final HttpEncodingProperties properties;
.....
}
如上分析:springboot不会一下子,把spring.factories下配置指定的配置类都加载到容器中,因为配置类有@ConditionalXXX注解,配置类只有满足这些XXX条件的时候才会被加载
ConditionalXXX注解整理:
@ConditionalXXX 扩展注解 ||作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效;
--------------------------------------------------
@ConditionalOnJava || 系统的java版本是否符合要求
@ConditionalOnBean || 容器中存在指定Bean;
@ConditionalOnMissingBean || 容器中不存在指定Bean;
@ConditionalOnExpression || 满足SpEL表达式指定
@ConditionalOnClass || 系统中有指定的类
@ConditionalOnMissingClass || 系统中没有指定的类
@ConditionalOnSingleCandidate || 容器中只有一个指定的Bean,或者这个Bean是首选Bean
@ConditionalOnProperty || 系统中指定的属性是否有指定的值
@ConditionalOnResource || 类路径下是否存在指定资源文件
@ConditionalOnWebApplication || 当前是web环境
@ConditionalOnNotWebApplication || 当前不是web环境
@ConditionalOnJndi || JNDI存在指定项