对于Spring框架,我们接触得比较多的是Spring mvc,Spring IOC、AOP、DI。而这框架如果在使用过程中,随着项目越来越大,引入的技术越来越多,会导致配置越来越复杂。因为要手动配置Bean,配置文件、配置类等,十分繁琐。
而spring boot框架的出现就是为了解决这一个问题,能够帮助Spring框架使用者快速构建一个基于Spring框架以及Spring生态体系的应用解决方案。他是对约定优于配置这个理念下的最佳实践,因此他是一个服务于框架的框架,服务的范围是简化配置开发。
约定优于配置是指约定好一些规范(默认规则),然后如果开发者遵守这个约定的规范,就可以不用多余的配置,直接可以达到使用效果。
约定优于配置的一些例子体现:
看@SpringBootApplication注解上的@SpringBootConfiguration注解。发现该注解与@configuration注解一样的效果,类似@Service与@Component之间的关系,为了使得语义更清晰,可读性更强。
看@EnableAutoConfiguration,这个是Springboot自动配置的核心。也是一个复合注解。
@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 {};
}
主要看selectImports方法,该方法是实现ImportSelector接口的方法,作用是自定义一些规则,返回Spring要加载的类的全限定名的数组。
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
private static final AutoConfigurationImportSelector.AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationImportSelector.AutoConfigurationEntry();
private static final String[] NO_IMPORTS = new String[0];
private static final Log logger = LogFactory.getLog(AutoConfigurationImportSelector.class);
private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";
private ConfigurableListableBeanFactory beanFactory;
private Environment environment;
private ClassLoader beanClassLoader;
private ResourceLoader resourceLoader;
public AutoConfigurationImportSelector() {
}
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
//首先,判断是否启动通过这个Selector来实现自动配置,isEnabled方法在下面。
return NO_IMPORTS;
} else {
//这段逻辑分支就是加载指定的类。
//加载元数据,方法定义在下面
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
//这里解释真正加载自动配置类了。自动配置类实际上就是一个配置类。方法定义在下面
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
//转成数组返回。
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
}
protected boolean isEnabled(AnnotationMetadata metadata) {
//一个判断,首先判断当前类是不是AutoConfigurationImportSelector类,如果不是,就返回True,表示启动。
//如果不是AutoConfigurationImportSelector类,
//那么就获取环境变量spring.boot.enableautoconfiguration的值来判断是否启动,如果没有配置该环境变量,默认使用默认值true。
return this.getClass() == AutoConfigurationImportSelector.class ? (Boolean)this.getEnvironment().getProperty("spring.boot.enableautoconfiguration", Boolean.class, true) : true;
}
public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
//加载类路径classpath下的META-INF/spring-autoconfigure-metadata.properties元数据文件。下面讲解 该文件,现在直接跳到该文件解析更佳。
return loadMetadata(classLoader, "META-INF/spring-autoconfigure-metadata.properties");
}
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);
//删除重复值,可能多个spring.factories文件会定义同样的自动配置类,在这里去重。
configurations = this.removeDuplicates(configurations);
//排除一些指定排除的类。会根据注解的excluded属性配置。
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 List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//返回要加载的自动配置类,这里使用的是一个SPI扩展机制实现。方法定义在下面
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, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
//这里是真正加载,方法定义在下面
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
//加载FACTORIES_RESOURCE_LOCATION代表的路径的文件,生成URL集合
//FACTORIES_RESOURCE_LOCATION 的值为"META-INF/spring.factories",
//代表会加载类路径下的META-INF/spring.factories文件
//该文件的定义语法在下面
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);
//加载文件形成k v 集合
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
//遍历kv集合,生成要加载的类的集合
String factoryClassName = ((String) entry.getKey()).trim();
for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
//返回
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
META-INF/spring-autoconfigure-metadata.properties文件的说明
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration.Configuration=
org.springframework.boot.autoconfigure.data.neo4j.Neo4jBookmarkManagementConfiguration.Configuration=
上面是spring boot Autoconfiguration模块的自动配置元数据的部分配置,主要是用于配置一些自动配置类加载的一些条件,只有符合该条件才加载该自动配置类,以防止加载无用的没必要的自动配置类。
语法:
自动配置类全路径.条件类型(注解)= 条件的值
比如
org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration.ConditionalOnClass=org.influxdb.InfluxDB
#org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration 这个是要加载的自动配置类。
#ConditionalOnClass 这个是加载条件,表示某个类在类路径存在时才加载该自动配置类。
#org.influxdb.InfluxDB 条件的值
#这个的意思是,只有我的项目类路径中存在org.influxdb.InfluxDB类,才加载InfluxDbAutoConfiguration 配置类。
还有其他很多条件类型,比如:AutoConfigureAfter、AutoConfigureBefore、ConditionalOnSingleCandidate等。如果等于号右边没有值,代表该自动配置类不设置条件。
其实这些条件类型就是注解,那为什么不直接在配置类上加这些注解呢,因为这些注解是spring boot的注解,而我们要自动配置的工程不一定是spring boot工程,也有可能是spring工程
META-INF/spring.factories文件语法:
该文件是配置要被springboot加载的配置类:
# 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,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
上面是Spring boot autoconfig模块的spring.factories的部分配置。
语法是:
注解全限定名=类全限定名1,类全限定名2…表示要使用该类来加载值配置类,比如:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
#表示EnableAutoConfiguration加载SpringApplicationAdminJmxAutoConfiguration和AopAutoConfiguration配置。
总结:AutoConfigurationImportSelector就是选择类路径下的META-INF/spring.factories文件配置的配置类,再配合类路径下的META-INF/spring-autoconfigure-metadata.properties元数据文件来实现条件加载自动配置类。
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}
在这里,如果你对@Import注解和ImportSelector和ImportBeanDefinitionRegistrar不懂的,要去补下功课再回来,不然下面就看不懂了。
该注解的作用是加载一个注册器Registrar.class。
@Order(-2147483648)
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
//ImportSelector是返回要加载的bean数组,而ImportBeanDefinitionRegistrar是直接就注册。
//这个Registrar 的作用是根据注解的属性basePackages等属性用于路径扫描注册Bean。
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));
}
}
TypeExcludeFilter:
//下面是TypeExcludeFilter的match方法
//这个类TypeExcludeFilter相当于一个总类,委托类,他的逻辑就是获取容器中所有它的子类(被委托类),进行排除,我们编写自定义的TypeExcludeFilter加入容器中就是给这里使用的。
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
if (this.beanFactory instanceof ListableBeanFactory && this.getClass() == TypeExcludeFilter.class) {
//只有beanFactory 是ListableBeanFactory 的子类并且这个类是TypeExcludeFilter才进来,
//否则总是返回false。
//获取容器中的所有TypeExcludeFilter的子类组件
Collection<TypeExcludeFilter> delegates = ((ListableBeanFactory)this.beanFactory).getBeansOfType(TypeExcludeFilter.class).values();
Iterator var4 = delegates.iterator();
while(var4.hasNext()) {
//遍历,使用每个TypeExcludeFilter组件进行排除
TypeExcludeFilter delegate = (TypeExcludeFilter)var4.next();
if (delegate.match(metadataReader, metadataReaderFactory)) {
return true;
}
}
}
return false;
}
第二个属性是Filter,,值是AutoConfigurationExcludeFilter。表示AutoConfigurationExcludeFilter的match方法返回true就加载Bean,否则不加载。
AutoConfigurationExcludeFilter:
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
//同时符合this.isConfiguration(metadataReader)和this.isAutoConfiguration(metadataReader);才返回true
return this.isConfiguration(metadataReader) && this.isAutoConfiguration(metadataReader);
}
private boolean isConfiguration(MetadataReader metadataReader) {
//要加载的类要被Configuration注解注解才返回true。
return metadataReader.getAnnotationMetadata().isAnnotated(Configuration.class.getName());
}
private boolean isAutoConfiguration(MetadataReader metadataReader) {
//要加载的类要在上面的AutoConfigurationImportSelector选中的类里面才加载。
return this.getAutoConfigurations().contains(metadataReader.getClassMetadata().getClassName());
}
至此,Springboot的自动配置原理大体已经阐述完毕。
@Configuration
public class DemoConfig {
@Bean
public DemoService demoService(){
return new DemoService();
}
}
public class DemoService {
public DemoService(){
System.out.println("DemoService create");
}
}
public class DemoTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringbootstudyApplication.class);
DemoService bean = context.getBean(DemoService.class);
System.out.println(bean);
}
}
结果:
结果不存在,原因springboot根本就不知道要加载他,原因是我们没有使用spring.factories文件定义自动配置该配置类。
其实这里就能算是一个spring boot Starter了,Starter的本质就是spring或者spring boot项目加jar包加Bean配置加spring.factories 加 spring-autoconfigure-metadata.properties文件(可有可无)。
至此,完毕。