java面试:有了解过springboot的自动装配流程吗?

spring的自动装配是一个十分重要的功能,也是spring当中是十分核心的,也是让spring能够做到集成多个生态的关键一步,因此这在java面试当中是一个老生常谈的核心问题了,今天我们就针对这个问题进行讲解,希望大家都能有所收获。

1.什么是spring的自动装配

所谓自动装配,就是我们的程序依赖一些第三方的功能组件的时候,不需要手动去获取这些组件类来加载到IOC容器里面,而通过spring的自动装配机制,就可以自动把相关组件注入到IOC容器当中去,而你做的只需要导入相关的依赖就可以了。(例如在使用Redis的时候,只需要在pom文件当中导入**"spring-boot-starter-data-redis"**的包,就可以从IOC容器当中直接拿到操作类叫做RedisTemplate来直接操作redis)

2.自动装配的流程是什么样的

spring的自动装配在代码层面被封装成了一个EnableAutoConfiguration的注解,而在这个注解当中包含了一个import的注解,将一个AutoConfigurationImportSelector的class对象导入到了IOC容器当中,而这个类又实现了一个ImportSelector的接口,里面有一个ImportSelector的方法会返回一个String类型的数组,这个数组的内容由一段方法实现,首先会加载所有需要的对象,然后过滤某些不符合条件的对象,在加载的过程当中会通过SpringFactoryLoader去获取META-INF/spring.factories这个文件,同时去遍历文件中的url地址,在过滤掉条件后拿到对象并加载到IOC容器当中。

复制代码
// EnableAutoConfiguration.java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage                    // 将主类所在包注册为自动配置包
@Import(AutoConfigurationImportSelector.class)  // 关键:导入配置选择器
public @interface EnableAutoConfiguration {
    
    // 开关属性,可通过 spring.boot.enableautoconfiguration=false 关闭
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    
    // 排除特定自动配置类
    Class<?>[] exclude() default {};
    
    // 通过全限定名排除(类不可用时用)
    String[] excludeName() default {};
}

// AutoConfigurationImportSelector.java
public class AutoConfigurationImportSelector implements 
        DeferredImportSelector,           // 延迟导入,在其他配置之后处理
        BeanClassLoaderAware, 
        ResourceLoaderAware,
        BeanFactoryAware, 
        EnvironmentAware, 
        Ordered {
    
    // 核心方法:返回需要导入的配置类
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        // 检查是否开启了自动配置
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        
        // 加载自动配置元数据(spring-autoconfigure-metadata.properties)
        AutoConfigurationMetadata autoConfigurationMetadata = 
            AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
        
        // 获取所有候选配置类
        AutoConfigurationEntry autoConfigurationEntry = 
            getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
        
        // 返回配置类全限定名数组
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
}

// SpringFactoriesLoader.java - Spring 标准工厂加载机制
public static List<String> loadFactoryNames(Class<?> factoryType, ClassLoader classLoader) {
    String factoryTypeName = factoryType.getName();
    
    // 读取所有 META-INF/spring.factories 文件
    return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}

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 {
        // 扫描所有 JAR 包中的 META-INF/spring.factories
        Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
        
        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            UrlResource resource = new UrlResource(url);
            // 解析 properties 文件
            Properties properties = PropertiesLoaderUtils.loadProperties(resource);
            
            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, k -> new ArrayList<>())
                          .add(factoryImplementationName.trim());
                }
            }
        }
    } catch (IOException ex) {
        throw new IllegalArgumentException("Unable to load factories from location [" +
            FACTORIES_RESOURCE_LOCATION + "]", ex);
    }
    
    cache.put(classLoader, result);
    return result;
}

今天的分享就到这里了,希望这篇博客能给你一些帮助,让你对关于有了解过springboot的自动装配流程问题得到进一步的提升,在面试的时候能从容面对面试官。

相关推荐
哦虎!2 小时前
【JavaEE】网络初识
java
for_ever_love__2 小时前
MySQL ORDER BY 排序优化:从 Using filesort 到索引有序扫描(含双路 / 单路排序原理)
java·mysql·优化·存储·by·order·filesort
西柚小萌新2 小时前
【岗位面试】--成都博维数孪 - 工业 AI 解决方案顾问
人工智能·面试·职场和发展
小 黄 鸡3 小时前
Flutter 面试必会知识点详解(从基础到进阶)
flutter·面试·职场和发展
我是人✓3 小时前
Java文件上传三方式对比:从传统到SpringBoot
java·开发语言
欣栀♡3 小时前
JVM 频繁 Full GC 先看日志括号里的 cause,jstat 的 FGC 在 JDK 8 上会骗人
java·jvm
IT研究室4 小时前
最新计算机毕业设计选题推荐-基于spring boot的葡萄酒文化宣传网站-网站-文档指导-Java-springboot
java·spring boot·课程设计
慧都小项4 小时前
MyEclipse 中创建和测试 JAX-RS REST 接口:从项目配置到 REST Explorer
java·restful·myeclipse·jax-rs
Wang's Blog4 小时前
Java 项目实战: 交友项目 - 入门案例:编写 Dubbo 服务提供者
java·dubbo·交友
秋饼4 小时前
Spring AI 2.0 结构化输出企业级实战:从 BeanOutputConverter 源码到生产级解析容错
java·ai·技术分享·后端开发