关于SpringBoot自动化配置原理的理解

130 阅读4分钟

1.引导类注解@SpringBootApplication 是一个组合注解

里面有

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
@Documented

作用:表明该注解应被包含在 JavaDoc 中。 使用场景:通常用于元注解(如 @SpringBootApplication),表示其子类也应被文档化。

@Inherited

作用:允许子类继承父类的注解。 示例:如果父类被 @SpringBootApplication 标注,子类默认也会继承该注解的配置。

另外三个是重要注解

@SpringBootConfiguration

  • 底层就是一个@Configuration,表示当前引导类就是一个配置类,用于定义 Bean。

@componentSan

组件扫描,扫描当前包及其子包下的组件(如 @Component@Service@Controller 等),使其能够被Spring识别

@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)

@Filter 是 Spring 中用于 定义组件扫描过滤规则 的注解,通常配合@ComponentSan的 includeFilters 或 excludeFilters 属性使用,用于精确控制哪些组件需要被扫描或排除的Spring 管理的 Bean。

包含特定组件(includeFilters):只扫描符合条件的类。

排除特定组件(excludeFilters):跳过不符合条件的类。

灵活匹配规则:支持按注解、类名、正则表达式、AspectJ 表达式或自定义逻辑过滤。

@EnableAutoConfiguration

是一个组合注解,由两个注解构成

@AutoConfigUrationPackage

该类会自动去调用registerBeanDefinitions方法,该方法中能够获取到引导类所在的包,配置@ComponentScan就可以让SpringBoot去扫描引导类包下的组件

@import

该注解导入一个AutoConfigurationImportSelector类,该类会自动调用selectImports方法,方法内部会调用getAutoConfigurationEntry方法,这个方法内部又会调用getCandidateConfigurations方法最终调用了SpringFactoriesLoader这个方法传入的参数(AutoConfiguration.class)获取并构建构建资源路径:META-INF/spring/<注解全限定名>.imports,然后通过findUrlsInClasspath方法 在类路径下查找所有匹配的 URL 资源,遍历后返回一个字符串集合,然后经过排除,判断,过滤,继续返回,然后转化为字符串数组返回.

@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

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

    String[] excludeName() default {};
}
             //selectImports`方法
public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    } else {
        AutoConfigurationEntry autoConfigurationEntry = 
       // getAutoConfigurationEntry方法
        this.getAutoConfigurationEntry(annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    } else {
        AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
        List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
        //去重
        configurations = this.removeDuplicates(configurations);
        //获取需要排除的配置类
        Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
        //检查排除的配置类是否有不存在的
        this.checkExcludedClasses(configurations, exclusions);
        //去除需要排除的类
        configurations.removeAll(exclusions);
        // 应用自动配置类过滤器(基于 @Conditional 条件进一步筛选)
        configurations = this.getConfigurationClassFilter().filter(configurations);
        this.fireAutoConfigurationImportEvents(configurations, exclusions);
        return new AutoConfigurationEntry(configurations, exclusions);
    }
}

`

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {

                                                //load方法
    List<String> configurations = ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).getCandidates();
    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you are using a custom packaging, make sure that file is correct.");
    return configurations;
}
public static ImportCandidates load(Class<?> annotation, ClassLoader classLoader) {
    Assert.notNull(annotation, "'annotation' must not be null");
    ClassLoader classLoaderToUse = decideClassloader(classLoader);
    //获取并构建构建资源路径:`META-INF/spring/<注解全限定名>.imports,
    String location = String.format("META-INF/spring/%s.imports", annotation.getName());
    //在类路径下查找所有匹配的 URL 资源
    Enumeration<URL> urls = findUrlsInClasspath(classLoaderToUse, location);
    List<String> importCandidates = new ArrayList();
   //遍历
    while(urls.hasMoreElements()) {
        URL url = (URL)urls.nextElement();
        //存入集合
        importCandidates.addAll(readCandidateConfigurations(url));
    }

    return new ImportCandidates(importCandidates);
}

2、一个方法 SpringApplication.run(引导类.class, args);

底层就是(new SpringApplication(primarySources)).run(args)

  • 构造器【赋值操作】

    • new SpringApplication(primarySources)
        public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
            this.sources = new LinkedHashSet();
            this.bannerMode = Mode.CONSOLE;
            this.logStartupInfo = true;
            this.addCommandLineProperties = true;
            this.addConversionService = true;
            this.headless = true;
            this.registerShutdownHook = true;
            this.additionalProfiles = Collections.emptySet();
            this.isCustomEnvironment = false;
            this.lazyInitialization = false;
            this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
            this.applicationStartup = ApplicationStartup.DEFAULT;
            this.resourceLoader = resourceLoader;
            Assert.notNull(primarySources, "PrimarySources must not be null");
            this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
            //判断有没有Servlet环境
            this.webApplicationType = WebApplicationType.deduceFromClasspath();
            this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
            //把 spring.factories 文件中key为ApplicationContextInitializer的内容加载进内存
            this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
            //把 spring.factories 文件中key为ApplicationListener的内容加载进内存
            this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
            //判断引导类是否有一个main方法
            this.mainApplicationClass = this.deduceMainApplicationClass();
        }
    
  • run方法

        public ConfigurableApplicationContext run(String... args) {
            long startTime = System.nanoTime();
            DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
            ConfigurableApplicationContext context = null;
            this.configureHeadlessProperty();
            //封装之前构造器加载进内存监听器为SpringApplicationRunListeners
            SpringApplicationRunListeners listeners = this.getRunListeners(args);
            //开启监听器
            listeners.starting(bootstrapContext, this.mainApplicationClass);
    
            try {
                ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
                //预处理环境
                ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
                this.configureIgnoreBeanInfo(environment);
                //打印Logo
                Banner printedBanner = this.printBanner(environment);
                //创建容器
                context = this.createApplicationContext();
                context.setApplicationStartup(this.applicationStartup);
                //预解析spring容器
                this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
                //初始化spring容器
                this.refreshContext(context);
                this.afterRefresh(context, applicationArguments);
                //统计启动时间
                Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
                if (this.logStartupInfo) {
                    (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);
                }
    
                //开启监听器
                listeners.started(context, timeTakenToStartup);
                //回调接口,应用运行器,命令行运行器
                this.callRunners(context, applicationArguments);
            } catch (Throwable var12) {
                this.handleRunFailure(context, var12, listeners);
                throw new IllegalStateException(var12);
            }
    
            try {
                Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
                listeners.ready(context, timeTakenToReady);
                return context;
            } catch (Throwable var11) {
                this.handleRunFailure(context, var11, (SpringApplicationRunListeners)null);
                throw new IllegalStateException(var11);
            }
        }
    

run方法会去读取@EnableAutoConfigurationl加载进内存的字符串数组,通过反射创建实例,最终作为Bean并注入到Spring容器中