SpringBoot自动配置原理

SpringBoot自动配置原理,第1张

SpringBoot自动配置原理 SpringBoot自动配置原理 一、前言 二、原理

我们需要从Spring Boot项目的启动类开始跟踪,在启动类上我们一般会加入SpringBootApplication注解

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

① @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 {
     ......
}

重点介绍如下三个注解:(敲黑板)

@SpringBootConfiguration :作用就相当于**@Configuration**注解,被注解的类将成为一个bean配置类
@EnableAutoConfiguration :这个注解很重要,借助@import的支持,收集和注册依赖包中相关的bean定义
@ComponentScan :作用就是自动扫描并加载符合条件的组件,最终将这些bean加载到spring容器中

②继续跟踪**@EnableAutoConfiguration**注解源码:

@SuppressWarnings("deprecation") 
@Target(ElementType.TYPE) 
@Retention(RetentionPolicy.RUNTIME)
@documented 
@Inherited
@AutoConfigurationPackage
@import(EnableAutoConfigurationimportSelector.class)  //EnableAutoConfigurationimportSelector批量导入bean
public @interface EnableAutoConfiguration { // ... } 
    

@EnableAutoConfiguration注解引入了@import这个注解。

import:导入需要自动配置的组件,此处为EnableAutoConfigurationimportSelector这个类(这块需要了解@import的相关知识)

​ 重写了selectimports方法,将返回的String[]导入到spring容器中

④EnableAutoConfigurationimportSelector类源码如下:

@Override
public String[] selectimports(Annotationmetadata annotationmetadata) {
    if (!isEnabled(annotationmetadata)) {
        return NO_importS;
    }
    
    AutoConfigurationmetadata autoConfigurationmetadata = AutoConfigurationmetadataLoader
        .loadmetadata(this.beanClassLoader);
    
    AnnotationAttributes attributes = getAttributes(annotationmetadata);
    
    List configurations = getCandidateConfigurations(annotationmetadata,attributes);
    configurations = removeDuplicates(configurations);
    
    Set exclusions = getExclusions(annotationmetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);
    
    configurations = filter(configurations, autoConfigurationmetadata);
    
    fireAutoConfigurationimportEvents(configurations, exclusions);
    return StringUtils.toStringArray(configurations);
}


private void fireAutoConfigurationimportEvents(List configurations,
                                               Set exclusions) {
    List listeners = getAutoConfigurationimportListeners();
    if (!listeners.isEmpty()) {
        AutoConfigurationimportEvent event = new AutoConfigurationimportEvent(this,
                                                                              configurations, exclusions);
        for (AutoConfigurationimportListener listener : listeners) {
            invokeAwareMethods(listener);
            listener.onAutoConfigurationimportEvent(event);
        }
    }
 }

继续看**getCandidateConfigurations(annotationmetadata,attributes)**方法

protected List getCandidateConfigurations(Annotationmetadata metadata, AnnotationAttributes attributes) {
        // 加载工程名称
		List configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				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;
	}

Spring工厂加载器加载工厂名称

扫描具有meta-INF/spring.factories文件的jar包

public static List loadFactoryNames(Class factoryType, @Nullable ClassLoader classLoader) {
		String factoryTypeName = factoryType.getName();
		return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
	}

private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {
    MultiValueMap result = cache.get(classLoader);
    if (result != null) {
        return result;
    }

    try {
        Enumeration 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);
            Properties properties = PropertiesLoaderUtils.loadProperties(resource);
            for (Map.Entry entry : properties.entrySet()) {
                String factoryTypeName = ((String) entry.getKey()).trim();
                for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                    result.add(factoryTypeName, factoryImplementationName.trim());
                }
            }
        }
        cache.put(classLoader, result);
        return result;
    }
    catch (IOException ex) {
        throw new IllegalArgumentException("Unable to load factories from location [" +
                                           FACTORIES_RESOURCE_LOCATION + "]", ex);
    }
}

spring.factories文件是Key=Value形式,多个Value时使用,隔开,该文件中定义了关于初始化,监听器等信息,而真正使自动配置生效的key是org.springframework.boot.autoconfigure.EnableAutoConfiguration,如下所示:

 # 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,
//省略

上面的EnableAutoConfiguration配置了多个类,这些都是Spring Boot中的自动配置相关类;在启动过程中会解析对应类配置信息。每个Configuation类都定义了相关bean的实例化配置。都说明了哪些bean可以被自动配置,什么条件下可以自动配置,并把这些bean实例化出来,下面的初始化数据源的部分源码:

@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
@import({ Registrar.class, DataSourcePoolmetadataProvidersConfiguration.class })
public class DataSourceAutoConfiguration {

    private static final Log logger = LogFactory
            .getLog(DataSourceAutoConfiguration.class);

    @Bean
    @ConditionalOnMissingBean
    public DataSourceInitializer dataSourceInitializer(DataSourceProperties properties,
            ApplicationContext applicationContext) {
        return new DataSourceInitializer(properties, applicationContext);
    }
    //略
}

会用到很多常用的注解

三、相关注解
@Configuration:这个配置就不用多做解释了,我们一直在使用
@EnableConfigurationProperties:这是一个开启使用配置参数的注解,value值就是我们配置实体参数映射的ClassType,将配置实体作为配置来源。
以下为SpringBoot内置条件注解:
@ConditionalOnBean:当SpringIoc容器内存在指定Bean的条件
@ConditionalOnClass:当SpringIoc容器内存在指定Class的条件
@ConditionalOnexpression:基于SpEL表达式作为判断条件
@ConditionalOnJava:基于JVM版本作为判断条件
@ConditionalOnJndi:在JNDI存在时查找指定的位置
@ConditionalOnMissingBean:当SpringIoc容器内不存在指定Bean的条件
@ConditionalOnMissingClass:当SpringIoc容器内不存在指定Class的条件
@ConditionalOnNotWebApplication:当前项目不是Web项目的条件
@ConditionalOnProperty:指定的属性是否有指定的值
@ConditionalOnResource:类路径是否有指定的值
@ConditionalOnSingleCandidate:当指定Bean在SpringIoc容器内只有一个,或者虽然有多个但是指定首选的Bean
@ConditionalOnWebApplication:当前项目是Web项目的条件

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/zaji/4690085.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-11-07
下一篇2022-11-07

发表评论

登录后才能评论

评论列表(0条)

    保存