springBoot是如何自动装配的(源码分析)

本文深入探讨SpringBoot的自动装配机制,解析@SpringBootApplication注解如何触发自动配置,通过@Import注解引入AutoConfigurationImportSelector类,最终加载spring.factories文件中的配置类。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

众所周知springboot是微服务开发的神器,只需在maven中导入对应的起步依赖,springboot就会自动识别,并自动创建对应的实体类(自动装配)。今天我们就来聊聊springboot的自动装配问题。(篇幅较长)
先来一段源码:

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

这是springboot中的引导类,自动装配就要从这里面的@SpringBootApplication这个注解开始说起,执行main方法,会加载@SpringBootApplication,这里面又配置了@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan这三个注解,下面是源码:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
**//以上四个都是jdk的注解,这里暂不讲解**
@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 {};

@SpringBootConfiguration
@SpringBootConfiguration标注这个类是一个配置类;
它只是@Configuration注解的派生注解;
它与@Configuration注解的功能一致;
只不过@SpringBootConfiguration是springboot的注解,而@Configuration是spring的注解。今天主要讲 自动配置,这里暂时不做深入讲解。
@ComponentScan
@ComponentScan这个注解主要是针对于第三方jar包中注解的应用,这里也不做深入讲解。
接下来就是我们今天的重点: @EnableAutoConfiguration
springboot自动装配的秘密就来源于此,此注释自动载入应用程序所需的所有Bean——这依赖于Spring Boot在类路径中的查找。下面进入这个注解的内部:
还是先来一段源码:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//上面依然是jdk注解
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

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

    String[] excludeName() default {};
}

@AutoConfigurationPackage
@AutoConfigurationPackage注解的作用是将 添加该注解的类所在的package 作为 自动配置package 进行管理。但重点并不在这里,而是在与这个注解下面的@Import({AutoConfigurationImportSelector.class})。自动配置的神秘面纱被一层一层的揭开,下面开始真正的神奇操作。
@Import注解仅从字面意思就可以看出这是导入的意思。但重点并不是这个而是被遗落在括号中的那个AutoConfigurationImportSelector.class。这个类里面真正意义上的完成了自动配置关键性的一步。
下面继续上源码:
由于空间有限只展示重要代码部分:

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() {
    }
    //重点在这
    //重点在这
    //重点在这(说三遍)
     protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
        return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
        }
     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.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);**这段代码,你就已经离真相越来越近了。也就是在SpringFactoriesLoader类中的loadFactories这个方法内实现的起步依赖包的配置类的加载。
下面进入这个类:

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() {
    }
 	
    //下面这个方法只截取加载部分的代码
    public static <T> List<T> loadFactories(Class<T> factoryClass, @Nullable ClassLoader classLoader) {
        Assert.notNull(factoryClass, "'factoryClass' must not be null");
        ClassLoader classLoaderToUse = classLoader;
        if (classLoader == null) {
            classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
        }

        List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);
        if (logger.isTraceEnabled()) {
            logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames);
        }

        List<T> result = new ArrayList(factoryNames.size());
        Iterator var5 = factoryNames.iterator();

        while(var5.hasNext()) {
            String factoryName = (String)var5.next();
            result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
        }

        AnnotationAwareOrderComparator.sort(result);
        return result;
    }

   

List factoryNames = loadFactoryNames(factoryClass, classLoaderToUse); 调用本类loadFactoryNames() 方法,下面看loadFactoryNames()这个方法:


public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    }

loadFactoryNames()这个方法中的return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); 调用本类中loadSpringFactories()这个方法中这行代码Enumeration urls = classLoader != null ? classLoader.getResources(“META-INF/spring.factories”) : ClassLoader.getSystemResources(“META-INF/spring.factories”); 读取了META-INF文件夹下的spring.factories这个文件中的所有的所有全类名,保存到集合中,然后遍历将读取的全类名,作为key值,获得对应的一组@Configuration类。


 private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            try {
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result = new LinkedMultiValueMap();

这是文件架构图
下面是部分spring.factories 文件内容:
在这里插入图片描述
以redis为例:


@Configuration
@ConditionalOnClass({RedisConnectionFactory.class})
@ConditionalOnBean({RedisConnectionFactory.class})
@ConditionalOnEnabledHealthIndicator("redis")
@AutoConfigureBefore({HealthIndicatorAutoConfiguration.class})
@AutoConfigureAfter({RedisAutoConfiguration.class, RedisReactiveHealthIndicatorAutoConfiguration.class})
public class RedisHealthIndicatorAutoConfiguration extends CompositeHealthIndicatorConfiguration<RedisHealthIndicator, RedisConnectionFactory> {
    private final Map<String, RedisConnectionFactory> redisConnectionFactories;

    public RedisHealthIndicatorAutoConfiguration(Map<String, RedisConnectionFactory> redisConnectionFactories) {
        this.redisConnectionFactories = redisConnectionFactories;
    }

    @Bean
    @ConditionalOnMissingBean(
        name = {"redisHealthIndicator"}
    )
    public HealthIndicator redisHealthIndicator() {
        return this.createHealthIndicator(this.redisConnectionFactories);
    }
}

加载到redis相关的类文件,会根据@ConditionalOnBean()仅仅在当前上下文中存在某个对象时,才会实例化一个Bean
@ConditionalOnClass()某个class位于类路径上,才会实例化一个Bean,经过一系类注解通过执行下面的代码,完成一个对应的bean创建,并将其放到spring的IOC容器中,才实现完成自动装配。

完成~
谢谢观众老爷们的观看,请点赞支持谢谢。

世界地图矢量数据可以通过多种网站进行下载。以下是一些提供免费下载世界地图矢量数据的网站: 1. Open Street Map (https://www.openstreetmap.org/): 这个网站可以根据输入的经纬度或手动选定范围来导出目标区域的矢量图。导出的数据格式为osm格式,但只支持矩形范围的地图下载。 2. Geofabrik (http://download.geofabrik.de/): Geofabrik提供按洲际和国家快速下载全国范围的地图数据。数据格式支持shape文件格式,包含多个独立图层,如道路、建筑、水域、交通、土地利用分类、自然景观等。数据每天更新一次。 3. bbbike (https://download.bbbike.org/osm/): bbbike提供全球主要的200多个城市的地图数据下载,也可以按照bbox进行下载。该网站还提供全球的数据,数据格式种类齐全,包括geojson、shp等。 4. GADM (https://gadm.org/index.html): GADM提供按国家或全球下载地图数据的服务。该网站提供多种格式的数据下载。 5. L7 AntV (https://l7.antv.antgroup.com/custom/tools/worldmap): L7 AntV是一个提供标准世界地图矢量数据免费下载的网站。支持多种数据格式下载,包括GeoJSON、KML、JSON、TopJSON、CSV和高清SVG格式等。可以下载中国省、市、县的矢量边界和世界各个国家的矢量边界数据。 以上这些网站都提供了世界地图矢量数据的免费下载服务,你可以根据自己的需求选择合适的网站进行下载。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值