Spring Boot 项目创建之后会默认生成一个入口类,通过该类的main方法即可启动Spring Boot项目:
@SpringBootApplication
public class StartApplication {
public static void main(String args[]){
SpringApplication.run(StartApplication.class,args);
}
}
其中最重要的注解为@SpringBootApplication,以2.1.2版本为例:
@SpringBootApplication的源码如下,其中作为一个组合注解,包含了@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan等关键注解。
package org.springframework.boot.autoconfigure;
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.boot.SpringBootConfiguration;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AliasFor;
@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 {
@AliasFor(
annotation = EnableAutoConfiguration.class
)
Class<?>[] exclude() default {};
@AliasFor(
annotation = EnableAutoConfiguration.class
)
String[] excludeName() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackages"
)
String[] scanBasePackages() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackageClasses"
)
Class<?>[] scanBasePackageClasses() default {};
}
//更高版本的springboot中包含有proxyBwanMethods()
其中:
通过这些属性,@SpringBootApplication组合了三个重要的注解,@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan
@SpringBootConfiguration其中组合了@Configuration注解,二者作用一致
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
该注解用于实现Spring Boot的核心功能之一:约定优于配置,其主要功能是启动Spring应用程序上下文时进行自动配置,它会尝试配置项目可能需要的Bean,自动配置通常是基于项目classpath中引入的类和已定义的Bean实现的。在此过程中,被自动配置的组件来自项目本身和项目依赖的jar包中。
举个例子:如果将tomcat-embedded.jar添加到classpath下,那么@EnableAutoConfiguration类会为你准备使用TomcatServletWebServerFactory类,并帮你初始化相关配置。与此同时,如果自定义了基于ServletWebServerFactory的Bean,那么@EnableAutoConfiguration将不会进行TomcatServletWebServerFactory类的初始化。这一系列的操作判断都由SpringBoot来完成。(《Spring Boot 技术内幕》)
其源码为:
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 {};
}
其中:
而@EnableAutoConfiguration 的关键功能是由@Import({AutoConfigurationImportSelector.class})实现的
此处使用的@Import注解使用的是ImportSelector接口的方式,来将AutoConfigurationImportSelector引入,具体的@Import用法可以参考: @Import注解的作用.
AutoConfigurationImportSelector被调用的基本流程为:
selectImports方法
selectImports方法基本包含了组件自动装配的所有处理逻辑,其源代码为:
public String[] selectImports(AnnotationMetadata annotationMetadata) {
//检查自动配置是否开启
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
//加载类路径下的metadata配置
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
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);
}
}
检查自动配置是否开启
首先检查自动配置是否开启,如果自动配置功能开启,则继续执行,其判断的方式为:
protected boolean isEnabled(AnnotationMetadata metadata) {
return this.getClass() == AutoConfigurationImportSelector.class ? (Boolean)this.getEnvironment().getProperty("spring.boot.enableautoconfiguration", Boolean.class, true) : true;
}
该方法获得配置文件中“spring.boot.enableautoconfiguration”的值,通过其判断是否开启自动配置;如果获取不到,则默认为true;可以通过在配置文件中修改spring.boot.enableautoconfiguration为false来关闭自动配置。
加载类路径下的metadata配置
加载元数据主要是为后续过滤自动配置使用
final class AutoConfigurationMetadataLoader {
protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";
private AutoConfigurationMetadataLoader() {
}
public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
return loadMetadata(classLoader, "META-INF/spring-autoconfigure-metadata.properties");
}
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);
}
}
static AutoConfigurationMetadata loadMetadata(Properties properties) {
return new AutoConfigurationMetadataLoader.PropertiesAutoConfigurationMetadata(properties);
}
private static class PropertiesAutoConfigurationMetadata implements AutoConfigurationMetadata {
...
}
}
}
其中文件"META-INF/spring-autoconfigure-metadata.properties"中记录的是每一个需自动配置的类所需要的加载条件,后续步骤中只有满足该条件的自动配置类才会被加载,从而进行过滤。
该方法会将从文件中获取到的配置条件存储到Properties中,返回AutoConfigurationMetadata结构作为后续进行过滤的条件。
加载spring.factories中的EnableAutoConfiguration配置类
所需要加载的自动配置组件在类路径下的META-INF文件夹下的spring.factories文件中进行注册,在AutoConfigurationImportSelector中采用getCandidateConfigurations方法来获取需要配置的组件
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;
}
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
可见该方法通过SpringFactoriesLoader类的loadFactoryNames方法获得全部自动加载配置,并返回List;其中SpringFactoriesLoader的实现方式为:
public final class SpringFactoriesLoader {
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap();
private SpringFactoriesLoader() {
}
public static <T> List<T> loadFactories(Class<T> factoryClass, @Nullable ClassLoader classLoader) {
Assert.notNull(factoryClass, "'factoryClass' must not be null");
ClassLoader classLoaderToUse = classLoader;
if (classLoader == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);
if (logger.isTraceEnabled()) {
logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames);
}
List<T> result = new ArrayList(factoryNames.size());
Iterator var5 = factoryNames.iterator();
while(var5.hasNext()) {
String factoryName = (String)var5.next();
result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
}
AnnotationAwareOrderComparator.sort(result);
return result;
}
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable 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);
}
}
}
private static <T> T instantiateFactory(String instanceClassName, Class<T> factoryClass, ClassLoader classLoader) {
try {
Class<?> instanceClass = ClassUtils.forName(instanceClassName, classLoader);
if (!factoryClass.isAssignableFrom(instanceClass)) {
throw new IllegalArgumentException("Class [" + instanceClassName + "] is not assignable to [" + factoryClass.getName() + "]");
} else {
return ReflectionUtils.accessibleConstructor(instanceClass, new Class[0]).newInstance();
}
} catch (Throwable var4) {
throw new IllegalArgumentException("Unable to instantiate factory class: " + factoryClass.getName(), var4);
}
}
}
其中:
配置类去重
protected final <T> List<T> removeDuplicates(List<T> list) {
return new ArrayList(new LinkedHashSet(list));
}
获得被排除类集合
根据上文可知,可以通过exclude和excludeName设置需要排除的配置;这里通过getExclusions()函数获得所有的需要排除的配置;
protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
Set<String> excluded = new LinkedHashSet();
excluded.addAll(this.asList(attributes, "exclude"));
excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
excluded.addAll(this.getExcludeAutoConfigurationsProperty());
return excluded;
}
private List<String> getExcludeAutoConfigurationsProperty() {
if (this.getEnvironment() instanceof ConfigurableEnvironment) {
Binder binder = Binder.get(this.getEnvironment());
return (List)binder.bind("spring.autoconfigure.exclude", String[].class).map(Arrays::asList).orElse(Collections.emptyList());
} else {
String[] excludes = (String[])this.getEnvironment().getProperty("spring.autoconfigure.exclude", String[].class);
return excludes != null ? Arrays.asList(excludes) : Collections.emptyList();
}
}
除了exclude和excludeName属性设置,getExcludeAutoConfigurationsProperty()函数读取配置文件中的spring.autoconfigure.exclude配置,来获得需要排除的配置项;
检查排除的类是否合法
checkExcludedClasses()函数检查需要排除的类是否合法,主要检查内容为需要排除的类是否存在于从文件META-INF/spring.factories中;
private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) {
List<String> invalidExcludes = new ArrayList(exclusions.size());
Iterator var4 = exclusions.iterator();
while(var4.hasNext()) {
String exclusion = (String)var4.next();
if (ClassUtils.isPresent(exclusion, this.getClass().getClassLoader()) && !configurations.contains(exclusion)) {
invalidExcludes.add(exclusion);
}
}
if (!invalidExcludes.isEmpty()) {
this.handleInvalidExcludes(invalidExcludes);
}
}
protected void handleInvalidExcludes(List<String> invalidExcludes) {
StringBuilder message = new StringBuilder();
Iterator var3 = invalidExcludes.iterator();
while(var3.hasNext()) {
String exclude = (String)var3.next();
message.append("\t- ").append(exclude).append(String.format("%n"));
}
throw new IllegalStateException(String.format("The following classes could not be excluded because they are not auto-configuration classes:%n%s", message));
}
过滤自动加载组件
过滤部分主要使用的是AutoConfigurationImportFilter接口的相关实现
将配置类和排除类通过事件传入监听器中
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
List<AutoConfigurationImportListener> listeners = this.getAutoConfigurationImportListeners();
if (!listeners.isEmpty()) {
AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
Iterator var5 = listeners.iterator();
while(var5.hasNext()) {
AutoConfigurationImportListener listener = (AutoConfigurationImportListener)var5.next();
this.invokeAwareMethods(listener);
listener.onAutoConfigurationImportEvent(event);
}
}
}
protected List<AutoConfigurationImportListener> getAutoConfigurationImportListeners() {
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class, this.beanClassLoader);
}
@ComponentScan默认会扫描该类所在的包下所有的配置类,并装配到容器中
(《Spring Boot 技术内幕》)