为什么说Spring Boot使用了"习惯优于配置"(约定优于配置)的理念?难道Spring Boot真的不需要配置吗?
其实Spring Boot使用自动配置,它将这些配置当做一种约定或习惯,让开发人员很容易创建一个独立运行、准生产级别的基于Spring框架的项目。
那么这种自动配置是怎么实现的呢?
Spring Boot通常都有一个名为*Application的入口类,而入口类中有一个入口方法(main方法),在main方法中使用SpringApplication.run(*Application.class, args),启动Spring Boot项目。
@SpringBootApplication
public class SpringBootDemoHelloworldApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootDemoHelloworldApplication.class, args);
}
@SpringBootApplication是Spring Boot的核心注解,也是一个组合注解(主要组合了@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan)。源码如下:
@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 {};
}
其中:
@SpringBootApplication注解是一个组合注解,而@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 {};
}
从@EnableAutoConfiguration注解的源码可以看出,其功能主要是由@AutoConfigurationPackage和@Import({AutoConfigurationImportSelector.class})提供
该注解的作用就是将主配置类(即@SpringBootApplication 标注的类)所在的包及其子包里的所有类都纳入 Spring 容器。开启此注解,Spring Boot会自动扫描@SpringBootApplication所在类的同级包以及下级包中的Bean。故一般建议入口类放置在groupId+arctifactId组合的包名下。
注解@AutoConfigurationPackage源码:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}
从源码中可以看出Import(导入)了Registrar,其源码如下:
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
}
}
以上的调用register方法其实是注册了一个Bean的定义,new AutoConfigurationPackages.PackageImport(metadata)).getPackageName()返回了当前主程序类的同级以及子级的包组件。
@Import({AutoConfigurationImportSelector.class}),导入自动配置的组件,@Import导入了选择器AutoConfigurationImportSelector。
查看AutoConfigurationImportSelector源码,其中通过spring工厂类加载工厂配置的方法源码如下
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;
}
从上面源码中可以看到这个方法调用了SpringFactoriesLoader.loadFactoryNames方法
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() {
}
......
}
综上可知,AutoConfigurationImportSelector使用SpringFactoriesLoader.loadFactoryNames方法来扫描具有META-INF/spring.factories文件的jar包,而spring-boot-autoconfigure-2.1.0.RELEASE.jar中就有一个spring.factories文件,此文件声明的自动配置如下:
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
......
这就是Spring Boot自动配置的设计,从配置文件加载常用的默认配置。
Spring Boot 自定义自动配置(自定义starter pom)
使用@SpringBootApplication注解的exclude参数,例:
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
《JavaEE开发的颠覆者 Spring Boot实战》