SpringBoot源码笔记2——自动配置原理
Factories配置对象机制(SPI)
* 作用
是SPI的一种实现方法,服务调用方法只需提供接口名称,而服务提供方需要在类路径的META-INF/spring.factories位置配置接口的实现类,服务调用后在根据类名进行反射实例化即可
如下
蓝色org.springframework.boot.SpringApplicationRunListener表示调用方需要的接口

绿色org.springframework.boot.context.event.EventPublishingRunListener表示服务提供方的实现类
* SpringApplication提供的getSpringFactoriesInstances方法就可实现获取指定接口名称的实例集合
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
//获取当前项目所有META-INF/spring.factories的配置信息,找出指定接口名称锁配置的所有实现类名称集合
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//反射获取names的所有对象,parameterTypes对应得构造器的参数类型
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
//基于@Order、Priority注解的排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
@SuppressWarnings("unchecked")
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
ClassLoader classLoader, Object[] args, Set<String> names) {
List<T> instances = new ArrayList<>(names.size());
for (String name : names) {
try {
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
T instance = (T) BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
* SpringFactoriesLoader.loadSpringFactories
获取当前项目所有META-INF/spring.factories的配置信息,找出指定接口名称锁配置的所有实现类名称集合
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> result = cache.get(classLoader);
if (result != null) {
return result;
}
result = new HashMap<>();
try {
//类加载器获取所有META-INF/spring.factories的路径集合
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
//把URL url封装为UrlResource,获取底层的输入流InputStream,再使用Properties.load把文件内容转为Properties 对象
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
//遍历Properties,获取指定接口类型的所有值,存储为Map<String, List<String>>
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
String[] factoryImplementationNames =
StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
for (String factoryImplementationName : factoryImplementationNames) {
result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
.add(factoryImplementationName.trim());
}
}
}
// Replace all lists with unmodifiable lists containing unique elements
result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
cache.put(classLoader, result);
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
return result;
}
自动配置原理解析
@SpringBootApplication注解
@SpringBootApplication()
@ComponentScan(basePackages = {..})
public class PaymentMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8001.class,args);
}
}
* @AliasFor:表示此注解方法继承了annotation 指定注解的方法,可进行覆盖
如这样标注@SpringBootApplication(scanBasePackages = {"com.atguigu.springcloud"})
那么“xxx”最终就会覆盖或指定父注解@ComponentScan中basePackages 的值
* @SpringBootConfiguration元注解为@Configuration,几乎可等同于@Configuration。说明@SpringBootApplication标注的类是一个java配置类
所以@ComponentScan会在容器初始化时的执行bean工厂后置器时,在配置类解析类解析时会被处理
* @EnableAutoConfiguration表示启动自动配置功能,这时核心功能
* @ComponentScan:由于没有显示指定basePackage的值,所以默认的扫描路径为添加的@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 {
//@AliasFor:表示继承了annotation 指定注解的属性,可进行覆盖
@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 {};
@AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
@EnableAutoConfiguration启动自动配置
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
/**
* Environment property that can be used to override when auto-configuration is
* enabled.
*/
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
/**
* 排除指定的字段配置类,不进行处理
*/
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
@AutoConfigurationPackage
* import了AutoConfigurationPackages.Registrar
- 作用创建bean名称为AutoConfigurationPackages、类型为BasePackages的bean
- 此类的作用仅仅是保存了原始注解的包路径
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
//AutoConfigurationPackages
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
//原始注解的包路径
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}
* 向容器定义表注册名称为AutoConfigurationPackages的bean定义,类型为BasePackages,简单的翻转了packageNames自动配置文件的基础包路径信息
使用getInstanceSupplier的方法创建实例BasePackages类型
registry.registerBeanDefinition(BEAN, new BasePackagesBeanDefinition(packageNames));
static final class BasePackagesBeanDefinition extends GenericBeanDefinition {
private final Set<String> basePackages = new LinkedHashSet<>();
BasePackagesBeanDefinition(String... basePackages) {
setBeanClass(BasePackages.class);
setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
addBasePackages(basePackages);
}
@Override
public Supplier<?> getInstanceSupplier() {
return () -> new BasePackages(StringUtils.toStringArray(this.basePackages));
}
private void addBasePackages(String[] additionalBasePackages) {
this.basePackages.addAll(Arrays.asList(additionalBasePackages));
}
}
@Import(AutoConfigurationImportSelector.class)(核心)
* 从环境变量获取spring.boot.enableautoconfiguration的值,作为开关,默认为true
* 从META-INF/spring.factories中获取所有EnableAutoConfiguration接口配置的所有实现类
①获取注解设置的exclude、excludeName和spring.autoconfigure.exclude属性指定排除配置类集合
并检查下最终过滤后的configurations存不存在指定排除的类,存在就报错
②从META-INF/spring.factories中获取AutoConfigurationImportFilter接口配置的所有实现类,遍历调用match对所有configurations的进行匹配过滤
③从META-INF/spring.factories中获取AutoConfigurationImportListener接口配置的所有实现,遍历触发onAutoConfigurationImportEvent方法
* 返回过滤后的自动配置类集合,
由于AutoConfigurationImportSelectort 是DeferredImportSelector类型 ,所有对所有自动配置的import解析是在ConfigurationClassParser.parse的最后一步进行的
public class AutoConfigurationImportSelectort implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
private static final AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationEntry();
private static final String[] NO_IMPORTS = {};
private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";
private ConfigurableListableBeanFactory beanFactory;
private Environment environment;
private ClassLoader beanClassLoader;
private ResourceLoader resourceLoader;
private ConfigurationClassFilter configurationClassFilter;
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
//从环境变量获取spring.boot.enableautoconfiguration的值,作为开关
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
//从META-INF/spring.factories中获取EnableAutoConfiguration接口配置的所有实现类
//经过过滤处理
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
//返回过滤后的自动配置类集合
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
//从环境变量获取spring.boot.enableautoconfiguration的值,作为开关
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
//获取EnableAutoConfiguration主键上设置的属性,是经过@AliasFor处理的
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//从META-INF/spring.factories中获取EnableAutoConfiguration接口配置的所有实现类
//例如org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
// org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
// org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
//去除重复的,使用LinkedHashSet进行封装即可
configurations = removeDuplicates(configurations);
//获取注解设置的exclude、excludeName和spring.autoconfigure.exclude指定排除类集合
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
//检查下指定排除的类是不是收集到自动配置类,即在configurations存不存在,不存在就报错
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
//从META-INF/spring.factories中获取AutoConfigurationImportFilter接口配置的所有实现类,遍历调用match进行匹配过滤
configurations = getConfigurationClassFilter().filter(configurations);
//从META-INF/spring.factories中获取AutoConfigurationImportListener接口配置的所有实现,遍历触发onAutoConfigurationImportEvent方法
fireAutoConfigurationImportEvents(configurations, exclusions);
//封装了configurations和exclusions
return new AutoConfigurationEntry(configurations, exclusions);
}
//略
}

对@Conditional注解shouldSkip的扩展
* @ConditionalXXX
@ConditionalOnProperty (环境中是有指定key,指定value的值,才会实例化一个Bean)
@ConditionalOnBean(仅仅在当前上下文中存在某个对象时,才会实例化一个Bean)
@ConditionalOnClass(某个class位于类路径上,才会实例化一个Bean)
@ConditionalOnExpression(当表达式为true的时候,才会实例化一个Bean)
@ConditionalOnMissingBean(仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean)
@ConditionalOnMissingClass(某个class类路径上不存在的时候,才会实例化一个Bean)
@ConditionalOnNotWebApplication(不是web应用)
* havingValue和matchIfMissing
- 如果havingValue存在指定,那么matchIfMissing就无效了
- 如果havingValue不存在指定:
①matchIfMissing = true,则此@ConditionalOnXXX,一定成立
②matchIfMissing = false,只有name配置项在配置文件中存在才会成立
//AopAutoConfigurat是配置类。以@EnableAutoConfiguration已通过import方法注册到定义表中,在bean工厂后置处理阶段被解析
// 1、先判断配置类的是否符合@Conditiona条件,这里需要环境中存在spring.aop.auto = true, matchIfMissing = true表示不存在也没关系,可以解析
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfigurationxx {
//2、解析内部配置类,一样的流程
//先判断配置类的是否符合@Conditiona条件,这里需要类路径下需要Advice类文件,符合就解析此配置类
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Advice.class)
static class AspectJAutoProxyingConfiguration {
//3、解析内部配置类,一样的流程
//判断配置类的是否符合@Conditiona条件,这里需要环境中存在spring.aop.proxy-target-class = false,如果可以解析
//3.1、解析@ComponentScan、@ComponentScans、@Import、@Bean、@ImportResource、@PropertySource等注解
// 这里@EnableAspectJAutoProxy里有注解@Import(AspectJAutoProxyRegistrar.class),最终向容器db表中注册了AnnotationAwareAspectJAutoProxyCreator定义
@Configuration(proxyBeanMethods = false)
@EnableAspectJAutoProxy(proxyTargetClass = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
matchIfMissing = false)
static class JdkDynamicAutoProxyConfiguration {
}
//同理
@Configuration(proxyBeanMethods = false)
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
matchIfMissing = true)
static class CglibAutoProxyConfiguration {
}
}
//4、这个ClassProxyingConfiguration内部类会在SpringApplication上的@ComponentScan解析扫描到,此时也会进行@Conditiona判断,只有通过了才会注册进行去db表
//因为@Configuration有元注解@Component
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingClass("org.aspectj.weaver.Advice")
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
matchIfMissing = true)
static class ClassProxyingConfiguration {
ClassProxyingConfiguration(BeanFactory beanFactory) {
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
}
}
}
自动配置的顺序控制
Spring Boot给我们提供了控制顺序的注解
* @AutoConfigureBefore:表明当前配置类在某一个配置类加载前加载。
* @AutoConfigureAfter:表明当前配置类在某一个配置类加载后加载。
* @AutoConfigureOrder:表示绝对顺序(数字越小,优先顺序越高)
例如
@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration { ... }
* 执行时机
- 在ConfigurationClassPostProcessor中所有bd注册的配置类解析完成之后,最后才处理下延迟导入类,即this.deferredImportSelectorHandler.process()方法(见上说明)
@EnableAutoConfiguration中@Import()的自动配置处理类AutoConfigurationImportSelector就是实现了DeferredImportSelector
- 在处理此延迟导入类中,获取到所有需要导入的配置类后,会进行exclude属性的排除、再进行排序,调用getInPriorityOrder进行排序
* 排序逻辑
- 先通过字母排序
- 再使用@AutoConfigureOrder排序
- 再使用 @AutoConfigureBefore @AutoConfigureAfter进行排序
//deferredImportSelectorHandler.process()方法最终调用AutoConfigurationSorter.getInPriorityOrder()方法对自动配置类组进行排序
List<String> getInPriorityOrder(Collection<String> classNames) {
AutoConfigurationSorter.AutoConfigurationClasses classes = new AutoConfigurationSorter.AutoConfigurationClasses(this.metadataReaderFactory,
this.autoConfigurationMetadata, classNames);
List<String> orderedClassNames = new ArrayList<>(classNames);
//通过字母排序
Collections.sort(orderedClassNames);
//通过@AutoConfigureOrder
orderedClassNames.sort((o1, o2) -> {
int i1 = classes.get(o1).getOrder();
int i2 = classes.get(o2).getOrder();
return Integer.compare(i1, i2);
});
//通过 @AutoConfigureBefore @AutoConfigureAfter进行排序
orderedClassNames = sortByAnnotation(classes, orderedClassNames);
return orderedClassNames;
}
* @AutoConfigureBefore、@AutoConfigureAfter、@AutoConfigureOrder这三个注解只能作用于自动配置类,而不能是自定义的@Configuration配置类。
- 原因在于这三个注解失效的时机,这方法只会处理通过@Import()导入的类为DeferredImportSelector类型的类,而我们的
说要所有自动配置类,即spring.factories下指定的自动配置类,会在这一步才开始导入,它的优先顺序最低(执行时机相对是最晚的);
通过扫描进来的一般都是你自己自定义的配置类,会自动配置之前载入
- 如果要对自定义的@Configuration配置类进行排序处理,只需要下面两步:
① 把自定义的@Configuration配置类挪动到Application扫描不到的包内,切记:一定且必须是扫描不到的包内
②当前工程里增加配置META-INF/spring.factories,内容为
org.springframework.boot.autoconfigure.EnableAutoConfiguration=自定义的@Configuration配置类
@EnableConfigurationProperties/@ConfigurationProperties属性值绑定
使用
* @EnableConfigurationProperties(xxx.class)
①就会在容器中创建xxx类型的实例,并已经绑定了属性,其他对象就可以注入此属性配置对象,通过getter获取配置值
②如果不指定@EnableConfigurationProperties(),不会加入注册表(需要通过其他方法加入),@ConfigurationProperties注解自然不会被解析
或者属性类(如下LiteServiceConfig)通过其他方法注册bean定义到工厂中,例如在LiteServiceConfig上添加@Component注解
@Configuration
@EnableConfigurationProperties(LiteServiceConfig.class) // 激活自动配置(指定文件中的配置)
public class LiteFlowAutoConfiguration {
}
@ConfigurationProperties(prefix = "app.service")
public class LiteServiceConfig {
private String serviceUid;
}
* 标注在一个@Bean方法上,可把属性绑定到工厂方法返回的对象上
@Configuration
public class test {
@Bean
@ConfigurationProperties(prefix = "kaka")
public User uerskaka(){
return new User();
}
}
* 配置文件中指定的属性值会赋值到对应bean的属性上
如果属性为对象类型,环境中存在属性对应的值,为基本类型,那么这个值将会尝试作为此对象类型的构造参数进行创建,例如
spring:
cloud:
gateway:
routes:
# =============第一个route========================
- host_example_to_httpbin=${test.uri}, Host=**.example.org
# ==============第二个route=======================
- id: host_foo_path_headers_to_httpbin
uri: ${test.uri}
ConfigurationProperties(GatewayProperties.PREFIX)
@Validated
public class GatewayProperties {
public static final String PREFIX = "spring.cloud.gateway";
/**
* List of Routes.
*/
@NotNull
@Valid
private List<RouteDefinition> routes = new ArrayList<>();
}
@Validated
public class RouteDefinition {
private String id;
public RouteDefinition() {}
public RouteDefinition(String text) {
int eqIdx = text.indexOf('=');}
setId(text.substring(0, eqIdx));
...
}
原理
(1)@EnableConfigurationProperties注解
@Import(EnableConfigurationPropertiesRegistrar.class)
public @interface EnableConfigurationProperties {
String VALIDATOR_BEAN_NAME = "configurationPropertiesValidator";
Class<?>[] value() default {};
}
- EnableConfigurationPropertiesRegistrar
* 添加一份bean后处理器,ConfigurationPropertiesBindingPostProcessor后置处理器,在bean初始化后解析bean上的@ConfigurationProperties注解,对属性进行绑定
基于Binder.get(environment).bind("属性key", 对象);这样可以把属性值合适的绑定到一个对象中
* 获取EnableConfigurationProperties注解中value指定的类,直接注册到bd表中
* 因此可见,此@ConfigurationProperties注解配置的属性时没有动态刷新的功能的,可以添加@RefreshScope且在配置修改时发布RefreshEvent事件解决
class EnableConfigurationPropertiesRegistrarxxx implements ImportBeanDefinitionRegistrar {
private static final String METHOD_VALIDATION_EXCLUDE_FILTER_BEAN_NAME = Conventions
.getQualifiedAttributeName(EnableConfigurationPropertiesRegistrarxxx.class, "methodValidationExcludeFilter");
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
//注册必要的处理bean,,比如注册ConfigurationPropertiesBindingPostProcessor
registerInfrastructureBeans(registry);
registerMethodValidationExcludeFilter(registry);
ConfigurationPropertiesBeanRegistrar beanRegistrar = new ConfigurationPropertiesBeanRegistrar(registry);
//获取基础配置类上所有EnableConfigurationProperties的类型的注解(包括元注解),父类上允许基础的EnableConfigurationProperties(添加@Inheric)
//遍历调用ConfigurationPropertiesBeanRegistrar.register方法进行处理:
//收集每个配置信息类的ConfigurationProperties注解,使用prefix值-type作为bean内注入定义表
getTypes(metadata).forEach(beanRegistrar::register);
}
//略
}
更多推荐
所有评论(0)