spring boot 自动跑批 spring boot自动配置原理_spring boot 自动跑批


在Spring Boot 中我们想要使用某个功能只需要在POM文件中添加对应的依赖。然后整个应用程序便具备了这个功能。那么这是如何实现的呢?

通过分析Spring Boot 示例程序来分析Spring Boot 自动配置原理。

1、Spring Boot自动配置原理

Spring Boot 应用启动的时候,需要传入一个标注@SpringBootApplication注解,该注解是一个组合注解。

@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 {
  //省略
}

1.1、@SpringBootConfiguration注解

该注解是Spring Boot 对Spring 的@Configuration注解的一个包装,它的作用就是表名被标注的类是一个配置类!这里就不在过多描述,需要了解可以通过查看官方文档

1.2、@EnableAutoConfiguration注解

该注解开启了自动配置功能,它也是一个组合注解。

@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

@AutoConfigurationPackage注解 配和 @ComponentScan 来决定Spring Boot 从那个包开始扫描组件,有怎样的扫描规则等等。 我们现在主要关心@Import(EnableAutoConfigurationImportSelector.class)给应用带来了什么

1.3、分析@Import注解

@Import注解的作用,已经在给IOC容器添加组件的几种方式总结 做过介绍分析。 直接点击EnableAutoConfigurationImportSelector观察,该类继承了AutoConfigurationImportSelector,关注这个类的selectImports方法

@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		try {
			AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
					.loadMetadata(this.beanClassLoader);
			AnnotationAttributes attributes = getAttributes(annotationMetadata);
			//获取所有的候选配置信息
			List<String> configurations = getCandidateConfigurations(annotationMetadata,
					attributes);
			configurations = removeDuplicates(configurations);
			configurations = sort(configurations, autoConfigurationMetadata);
			Set<String> exclusions = getExclusions(annotationMetadata, attributes);
			checkExcludedClasses(configurations, exclusions);
			configurations.removeAll(exclusions);
			configurations = filter(configurations, autoConfigurationMetadata);
			fireAutoConfigurationImportEvents(configurations, exclusions);
			return configurations.toArray(new String[configurations.size()]);
		}
		catch (IOException ex) {
			throw new IllegalStateException(ex);
		}
	}

观察getCandidateConfigurations方法

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
			AnnotationAttributes attributes) {
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
				getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
		return configurations;
	}

观察getSpringFactoriesLoaderFactoryClass方法,该方法返回了EnableAutoConfiguration的类类型

protected Class<?> getSpringFactoriesLoaderFactoryClass() {
		return EnableAutoConfiguration.class;
	}

返回到getCandidateConfigurations这个方法,观察loadFactoryNames方法

public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
		//获取EnableAutoConfiguration全限定名称
		String factoryClassName = factoryClass.getName();
		try {
			//加载 类路径下 META-INF文件夹中的spring.factories文件
			Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			List<String> result = new ArrayList<String>();
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				//将spring.factories文件转为Properties文件
				Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
				//读取key为EnableAutoConfiguration全限定名称对应的所有值
				String factoryClassNames = properties.getProperty(factoryClassName);
				result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
			}
			return result;
		}
		catch (IOException ex) {
		}
	}

这个常量FACTORIES_RESOURCE_LOCATION的值为:META-INF/spring.factories

1.4、查看spring.factories

# 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,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
## 省略

再返回到selectImports方法。实际上就是将这个配置文件中org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的值,按照一定的过滤规则,最后把他们交给了容器。 每一个这样的xxxAutoConfiguration类都是容器中的一个组件,并且它们都是一个配置类,当这些组件交给Spring容器管理,也就使得应用程序完成了自动配置。

1.5、扩展

分析完上述内容,就可以自定义一个应用场景启动器。具体步骤如下

  • 创建Maven工程,引入Spring Boot提供的最基本的配置
  • 创建自定义配置类
  • 在类路径下创建META-INF文件夹,在该文件夹中创建spring.factories文件。
  • 在该文件中添加org.springframework.boot.autoconfigure.EnableAutoConfiguration=自定义配置类的全限定名称
  • 打包,完成