Spring Boot最核心的功能就是自动配置,功能的实现都是基于约定优于配置的原则。那么Spring Boot是如何约定?又是如何实现自动配置功能的呢?下面我们来分析一下。
我们开发Spring Boot项目的时候,都会用到如下的启动类:
@SpringBootApplication
public class ServerApplication{
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
从上面代码可以看出,启动类上面唯一的注解就是@SpringBootApplication,它是Spring Boot项目的核心注解,所以要揭开SpringBoot的神秘面纱,我们要从这位开始就可以了。
@SpringBootApplication隐藏的秘密
@SpringBootApplication注解是Spring Boot的核心注解,它其实是一个组合注解:
@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 {
// 排除指定自动配置类,该成员属性覆盖了@EnableAutoConfiguration中定义的exclude()成员属性
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
// 排除指定自动配置类名,该成员属性覆盖了@EnableAutoConfiguration中定义的excludeName()成员属性
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
// 指定扫描的基础package,用于激活@Component等注解的初始化
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
// 扫描指定的类,用于组件的初始化
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
// 指定是否代理@Bean方法以强制执行bean的生命周期行为
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
关于@AliasFor注解,该注解用于桥接到其他注解。如上所示@SpringBootApplication并没有定义新的属性而是复用其他注解已有的注解属性并对其进行组合形成新的注解从而达到便捷的目的。
注解@SpringBootConfiguration源码分析
继承自@Configuration,二者功能也一致,标注当前类是配置类,并会将当前类内声明的一个或多个以@Bean注解标记的方法的实例纳入到Spring容器中,并且实例名就是方法名。
源码如下:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
从源码来看,主要就是@Configuration注解,这里我说明一点,@Configuration标注在类上,@Configuration等价于Spring的xml配置文件中的Beans节点。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
default-lazy-init="true">
<!--bean定义-->
</beans>
注解@ComponentScan介绍
@ComponentScan注解是什么?其实很简单,它主要作用就是定义扫描的路径并从中找出标识了需要装配的类自动装配到spring容器中。
@ComponentScan注解的源码:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
// 对应的包扫描路径 可以是单个路径 也可以是扫描的路径数组
@AliasFor("basePackages")
String[] value() default {};
// 和value一样是对应的包扫描路径 可以是单个路径 也可以是扫描的路径数组
@AliasFor("value")
String[] basePackages() default {};
// 指定具体的扫描的类
Class<?>[] basePackageClasses() default {};
// 对应的bean名称的生成器
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
// 处理检测到的bean的scope范围
Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
// 是否为检测到的组件生成代理
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
// 控制符合组件检测条件的类文件
String resourcePattern() default "**/*.class";
// 是否对带有@Component @Repository @Service @Controller注解的类开启检测 默认是开启的
boolean useDefaultFilters() default true;
/**
* 指定某些定义Filter满足条件的组件 FilterType有5种类型
* ANNOTATION 注解类型 默认
* ASSIGNABLE_TYPE 指定固定类
* ASPECTJ 根据AspectJ表达式
* REGEX 正则表达式
* CUSTOM 自定义类型 需要实现org.springframework.core.type.filter.TypeFilter接口
*/
ComponentScan.Filter[] includeFilters() default {};
// 用于排除不需要扫描的类
ComponentScan.Filter[] excludeFilters() default {};
// 扫描到的类是都开启懒加载 默认是不开启的
boolean lazyInit() default false;
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Filter {
FilterType type() default FilterType.ANNOTATION;
@AliasFor("classes")
Class<?>[] value() default {};
@AliasFor("value")
Class<?>[] classes() default {};
String[] pattern() default {};
}
}
- 自定扫描路径下边带有@Controller,@Service,@Repository,@Component注解加入spring容器
- 通过includeFilters加入扫描路径下没有以上注解的类加入spring容器
- 通过excludeFilters过滤出不用加入spring容器的类
@ComponentScan(
excludeFilters = {@Filter(type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class}),
@Filter(type = FilterType.CUSTOM, classes = {AutoConfigurationExcludeFilter.class})}
)
TypeExcludeFilter的作用是加载spring bean池中所有针对TypeExcludeFilter的扩展,并循环遍历这些扩展类调用其match方法。
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
if (this.beanFactory instanceof ListableBeanFactory && this.getClass() == TypeExcludeFilter.class) {
Iterator var3 = this.getDelegates().iterator();
while(var3.hasNext()) {
TypeExcludeFilter delegate = (TypeExcludeFilter)var3.next();
if (delegate.match(metadataReader, metadataReaderFactory)) {
return true;
}
}
}
return false;
}
AutoConfigurationExcludeFilter的作用是过滤掉会自动配置的配置类,避免重复
public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoaderAware {
private ClassLoader beanClassLoader;
private volatile List<String> autoConfigurations;
public AutoConfigurationExcludeFilter() {
}
public void setBeanClassLoader(ClassLoader beanClassLoader) {
this.beanClassLoader = beanClassLoader;
}
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throwsIOException {
//如果这个类被@Configuration标注,且属于自动加载的配置,那么过滤它,避免重复
return this.isConfiguration(metadataReader) && this.isAutoConfiguration(metadataReader);
}
private boolean isConfiguration(MetadataReader metadataReader) {
return metadataReader.getAnnotationMetadata().isAnnotated(Configuration.class.getName());
}
private boolean isAutoConfiguration(MetadataReader metadataReader) {
return this.getAutoConfigurations().contains(metadataReader.getClassMetadata().getClassName());
}
protected List<String> getAutoConfigurations() {
if (this.autoConfigurations == null) {
// 从META-INF/spring.factories文件中,找出EnableAutoConfiguration.class
this.autoConfigurations = SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, this.beanClassLoader);
}
return this.autoConfigurations;
}
}
注解@EnableAutoConfiguration源码分析
我们知道Spring是无法自动配置@Configuration注解标注的类的,而Spring Boot的核心功能之一就是根据约定自动管理该注解标注的类。用来实现这一功能的组件之一便是@EnableAutoConfiguration。@EnableAutoConfiguration注解的主要功能是启动Spring应用程序上下文时进行自动配合,它会尝试猜测并配置项目可能需要的Bean。
@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 {};
}
正如上文所说,它会猜测你需要使用的Bean,但如果实际开发过程中,你并不需要它预置初始化的Bean,可以通过注解的exclude()和excludeName()进行有针对性的排除,例如排除数据库的自动配置:
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
或
@Configuration
@EnableAutoConfiguration(exclude = DataSourceAutoConfiguration.class)
从源码中我们可以看到,@AutoConfigurationPackage 和 @Import({AutoConfigurationImportSelector.class})最为重要,所以我们从它们开始分析。
@AutoConfigurationPackage源码分析
@AutoConfigurationPackage注解的作用是将添加该注解的类所在的package作为自动配置package进行管理。
源码如下:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}
@Import({Registrar.class}):默认将主配置类(@SpringBootApplication)所在的包及其子包里面的所有组件扫描到Spring容器中
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
// 用来注册bean的定义的
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// 默认将会扫描@SpringBootApplication标注的主配置类所在的包及其子包下所有组件
AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
}
}
(new AutoConfigurationPackages.PackageImport(metadata)).getPackageName():它其实返回了当前主程序类的同级以及子级的包组件。
@Import({AutoConfigurationImportSelector.class})
AutoConfigurationImportSelector: 导入哪些组件的选择器,将所有需要导入的组件以全类名的方式返回,这些组件就会被添加到容器中。
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());
}
}
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);
}
}
List 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;
}
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, 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 factoryTypeName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
String factoryImplementationName = var9[var11];
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}
从源码中我们可以看到,springboot启动的时候会从类路径下的 META-INF/spring.factories中获取EnableAutoConfiguration指定的值,并将这些值作为自动配置类导入到容器中,自动配置类就会生效,最后完成自动配置工作。EnableAutoConfiguration默认在spring-boot-autoconfigure这个包中,如下图: