《Spring源码深度解析 郝佳 第2版》SpringBoot体系分析、Starter的原理

2023-05-16

往期博客


《Spring源码深度解析 郝佳 第2版》容器的基本实现与XML文件的加载

《Spring源码深度解析 郝佳 第2版》XML标签的解析

《Spring源码深度解析 郝佳 第2版》bean的加载、循环依赖的解决

《Spring源码深度解析 郝佳 第2版》ApplicationContext

《Spring源码深度解析 郝佳 第2版》AOP

《Spring源码深度解析 郝佳 第2版》JDBC、MyBatis原理


SpringBoot是为了简化新Spring应用的初始化搭建以及开发过程。该框架使用了特定的方式来进行配置,主要特点

  1. 内嵌tomcat
  2. 简化Maven配置
  3. 自动配置Spring
  4. 对xml配置没有严格要求

目录

  1. 自己写一个Starter
  2. Spring启动流程run方法
  3. Starter自动化配置原理
  4. Conditional机制实现
  5. Tomcat启动

一、自己写一个Starter

写简单的栗子,基本步骤

  1. 新建Maven工程,在pom文件定义相关信息,如需要用到Spring则引入spring-boot-dependencies进而引入spring-boot-autoconfigure
  2. 定义一个接口如XxxService,对外暴露可以直接调用。定义一个实现类XxxServiceImpl实现接口,加上@Component注解放置Spring容器
  3. 编写XxxAutoConfiguration 自动配置类,类上标注@Configuration注解标识是一个类似xml的配置类,然后配置@ComponentScan扫描XxxServiceImpl所在的包(将扫包的义务交给Starter自己,如果都需要主模块main函数去扫包,则后续不易维护,在SpringBoot之前,需要将扫包配置到xml文件中,交给主模块完成,耦合度较高)
  4. 最后在Spring的根路径下,即resources文件夹下创建META-INF/spring.factories文件,用于指定当前Starter的XxxAutoConfiguration自动配置类的路径

// 1. 新建study-springboot-mystarter,配置xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>study-springboot-mystarter</artifactId>
    <version>1.0-SNAPSHOT</version>

<!--    Starter导入自己的依赖-->
    
    
<!--    自动配置的依赖-->

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
    </dependencies>

    <dependencyManagement>
        
        
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.0.1.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

</project>

// 2. 编写对外暴露的接口HelloService以及实现类HelloServiceImpl

package service;

public interface HelloService {
    
    public void sayHello();
}

package service.impl;

import org.springframework.stereotype.Component;
import service.HelloService;

@Component
public class HelloServiceImpl implements HelloService {
    public void sayHello() {
        System.out.println("你好呀!");
    }
}

// 3. 编写自动配置类

package config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration // 声明当前类是等同于xml的配置类
@ComponentScan({"service.impl"}) // 扫描包路径配置
public class HelloServiceAutoConfiguration {
}

// 4. 创建spring.factories文件

其实不在Starter中写 @ComponentScan,在Spring主服务模块是可以扫外部包的,但是有时候主服务扫描外部引入的Starter的全部包或者不知道需要扫哪些包,会很麻烦,耦合度较高,现在直接在Starter中的spring.factories指定特定的包即可,符合单一职责

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  config.HelloServiceAutoConfiguration

// 5.使用

在这里插入图片描述
在这里插入图片描述

二、SpringBoot启动run方法流程

往期博客分析过run方法,参考 《Spring源码深度解析 郝佳 第2版》ApplicationContext

在这里插入图片描述

// ConfigurableApplicationContext 

public ConfigurableApplicationContext run(String... args) {
		// 1. 计时器
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        // 2. 容器
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        this.configureHeadlessProperty();
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting();

        Collection exceptionReporters;
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            
            // 3.准备环境,根据webApplicationType 准备上下文环境
            // webApplicationType 是在构造函数时根据ClassPath下内容设置的
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            
            Banner printedBanner = this.printBanner(environment);
            
            // 4. 创建容器,根据webApplicationType给contextClass赋值,然后初始化对应上下文容器
            context = this.createApplicationContext();
            
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            // 5. 准备容器相关
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            
            // 6. 刷新容器,这里委托ConfigurableApplicationContext接口的refesh(),
            // 真实调用的是AbstractApplicationContext.refresh(),也就是上面分析的部分
            this.refreshContext(context);
            
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

            listeners.started(context);
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, exceptionReporters, listeners);
            throw new IllegalStateException(var10);
        }

        try {
            listeners.running(context);
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }


其中关键步骤

  • // 4.创建容器,根据webApplicationType给contextClass赋值,然后初始化对应上下文容器,context = this.createApplicationContext();
  • // 5. 准备容器,调用prepareContext,内部调用load方法完成BeanDefinitionLoader的初始化,然后进行BeanDefinition的注册加载
  • // 6. 刷新容器,委托ConfigurableApplicationContext接口的refesh()。真实调用的是AbstractApplicationContext.refresh(),也就是上面分析的部分this.refreshContext(context),后面会调用this.afterRefresh(context, applicationArguments);

// 1. 创建容器createApplicationContext

该步骤就是实例化一个ApplicationContext,需要根据webApplicationType的值来判断初始化,而webApplicationType是在SpringApplication构造函数时根据ClassPath下内容设置的,这也是后面启动Tomcat的关键地方

  • SERVLET,Web上下文,包含web服务,对应AnnotationConfigServletWebServerApplicationContext
  • REACTIVE,反应式上下文,对应AnnotationConfigReactiveWebServerApplicationContext
  • DEFAULT,Spring上下文,对应AnnotationConfigApplicationContext

private Class<? extends ConfigurableApplicationContext> applicationContextClass;

protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                switch(this.webApplicationType) {
                // 有web的默认上下文
                case SERVLET:
                    contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
                    break;
                case REACTIVE:
                    contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
                    break;
                 // 没有web的默认上下文
                default:
                    contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
                }
            } catch (ClassNotFoundException var3) {
                throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
            }
        }

        return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
    }

三、Starter自动化配置原理

1. @SpringBootApplication

研究Starter到底是怎么启动的?分析入口需要是@SpringBootApplication注解



package org.springframework.boot.autoconfigure;


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited

@SpringBootConfiguration // 标注当前为配置类,相当于xml
@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 {};

	
	@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;

}


2. @EnableAutoConfiguration

然后接着分析@EnableAutoConfiguration ,它是自动装配全局开关,想要使用Starter你得首先需要导入它啊,其实也就是将Starter中暴露的bean放到Spring中,这个过程是自动的,因此导入Starter之后可以直接@Autowired注入

他的原理就是 @Import(AutoConfigurationImportSelector.class) ,导入了AutoConfigurationImportSelector类,实现Starter自动化导入

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class) // 导入了一个AutoConfigurationImportSelector

public @interface EnableAutoConfiguration {

	
	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

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

	
	String[] excludeName() default {};

}

3. AutoConfigurationImportSelector类

重写selectImports选择自动装配Starter

自动配置类位置从该Starter的spring.factories中获得,可能获取到的并不一定要自动装配,如根本没有引入对应的Starter

因此依靠selectImports自动化导入内部的自动配置类

// AutoConfigurationImportSelector
@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		// 1. 是否需要自动导入,也就是是否开启全局开关
		if (!isEnabled(annotationMetadata)) {
		
			return NO_IMPORTS;
		}
		// 2. 读取spring.factories的自动配置类,封装到 AutoConfigurationEntry 
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}
	

3.1 isEnabled是否开启装配Starter

// AutoConfigurationImportSelector
protected boolean isEnabled(AnnotationMetadata metadata) {
		if (getClass() == AutoConfigurationImportSelector.class) {
			return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);
		}
		return true;
	}

3.2 spring.factories的读取逻辑

找出spring.factories中全部的自动配置类全路径名,包含主服务模块的和引入Starter的,但是主模块中会有可能根本没有引入对应的Starter对应的自动配置类路径,哪如何处理?等会分析…。然就封装到AutoConfigurationEntry中的List<String> configurations,以便后续通过自动配置类上的@ComponentScan注册各个Starter暴露的bean

读取的时机则是在SpringBoot启动的时候完成,具体原理就是使用了Spring的BeanDefinitionRegistryPostProcessor拓展点实现了ConfigurationClassPostProcessor从而实现一系列拓展,后面再分析启动时加载spring.factories的原理逻辑。

// AutoConfigurationImportSelector
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
		// 1. 不需要自动装配Starter
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		// 2. 获取所有注解类
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		// 3. 需要导入的Starter的自动配置类,
		// 以便后面获取里面配置的@ComponentScan扫描各个Starter中暴露的bean
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		
		configurations = removeDuplicates(configurations);
		
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);
		configurations = getConfigurationClassFilter().filter(configurations);
		
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}
// 3. 需要导入的Starter的自动配置类,以便后面获取里面配置的@ComponentScan扫描各个Starter中暴露的bean
// AutoConfigurationImportSelector
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		// 3.1 委托 SpringFactoriesLoader 读取Starter的spring.factpries
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				getBeanClassLoader());
		// Starter必须指定spring.factories,否则报错提示
		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;
	}
// // 3.1 委托 SpringFactoriesLoader 读取Starter的spring.factpries
// SpringFactoriesLoader
 public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        String factoryTypeName = factoryType.getName();
        // loadSpringFactories方法返回一个map
        // 包含spring.factories里面配置的自动各个Starter的自动配置类
        return (List)
        	loadSpringFactories(classLoader)
       		 .getOrDefault(factoryTypeName, Collections.emptyList());
    }

委托方法loadSpringFactories读取加载spring.factories


// SpringFactoriesLoader
// 读取spring.factories内容到map
    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 {
            	// 读取 META-INF/spring.factories
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result = new LinkedMultiValueMap();

                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();

                    while(var6.hasNext()) {
                        Entry<?, ?> entry = (Entry)var6.next();
                        String factoryTypeName = ((String)entry.getKey()).trim();
                        String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        int var10 = var9.length;

                        for(int var11 = 0; var11 < var10; ++var11) {
                            String factoryImplementationName = var9[var11];
                            result.add(factoryTypeName, factoryImplementationName.trim());
                        }
                    }
                }

                cache.put(classLoader, result);
                return result;
            } catch (IOException var13) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
            }
        }
    }

可以debug查看AutoConfigurationEntry看到

在这里插入图片描述

3.3 spring.factories的读取时机

那么Spring在启动的时候如何加载spring.factories呢?结合往期博客对启动run方法的分析
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

ConfigurationClassPostProcessor类作为Spring的拓展点,是SpringBoot一系列功能的基础入口。

在这里插入图片描述

// ConfigurationClassPostProcessor
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
		// 需要注册的BeanDefinitionHolder
        List<BeanDefinitionHolder> configCandidates = new ArrayList();	
		// 需要注册的全路径名
        String[] candidateNames = registry.getBeanDefinitionNames();
        String[] var4 = candidateNames;
        int var5 = candidateNames.length;

        for(int var6 = 0; var6 < var5; ++var6) {
            String beanName = var4[var6];
            BeanDefinition beanDef = registry.getBeanDefinition(beanName);
            // 该配置类已经注册
            if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
                }
            } else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
            	// 将全路径名转为BeanDefinitionHolder,保存到configCandidates
                configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
            }
        }
		// 需要注册的configCandidates不为空
        if (!configCandidates.isEmpty()) {
        	// 排序
            configCandidates.sort((bd1, bd2) -> {
                int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
                int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
                return Integer.compare(i1, i2);
            });
            SingletonBeanRegistry sbr = null;
            // 单例注册器
            if (registry instanceof SingletonBeanRegistry) {
                sbr = (SingletonBeanRegistry)registry;
                if (!this.localBeanNameGeneratorSet) {
                    BeanNameGenerator generator = (BeanNameGenerator)sbr.getSingleton("org.springframework.context.annotation.internalConfigurationBeanNameGenerator");
                    if (generator != null) {
                        this.componentScanBeanNameGenerator = generator;
                        this.importBeanNameGenerator = generator;
                    }
                }
            }

            if (this.environment == null) {
                this.environment = new StandardEnvironment();
            }
			// 接下来就是进入 ConfigurationClassParser  实例的parse方法
			
            ConfigurationClassParser parser = new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry);
            // 待注册的 BeanDefinitionHolder
            Set<BeanDefinitionHolder> candidates = new LinkedHashSet(configCandidates);
            HashSet alreadyParsed = new HashSet(configCandidates.size());

            do {
            	// 1. 完成解析,内部完成selectImports的调用和processImports的调用
                parser.parse(candidates);
                parser.validate();
                
                Set<ConfigurationClass> configClasses = new LinkedHashSet(parser.getConfigurationClasses());
                configClasses.removeAll(alreadyParsed);
                if (this.reader == null) {
                    this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry());
                }
				
				// 2. 完成注册
                this.reader.loadBeanDefinitions(configClasses);
                alreadyParsed.addAll(configClasses);
                candidates.clear();
                if (registry.getBeanDefinitionCount() > candidateNames.length) {
                    String[] newCandidateNames = registry.getBeanDefinitionNames();
                    Set<String> oldCandidateNames = new HashSet(Arrays.asList(candidateNames));
                    Set<String> alreadyParsedClasses = new HashSet();
                    Iterator var12 = alreadyParsed.iterator();

                    while(var12.hasNext()) {
                        ConfigurationClass configurationClass = (ConfigurationClass)var12.next();
                        alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
                    }

                    String[] var23 = newCandidateNames;
                    int var24 = newCandidateNames.length;

                    for(int var14 = 0; var14 < var24; ++var14) {
                        String candidateName = var23[var14];
                        if (!oldCandidateNames.contains(candidateName)) {
                            BeanDefinition bd = registry.getBeanDefinition(candidateName);
                            if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && !alreadyParsedClasses.contains(bd.getBeanClassName())) {
                                candidates.add(new BeanDefinitionHolder(bd, candidateName));
                            }
                        }
                    }

                    candidateNames = newCandidateNames;
                }
            } while(!candidates.isEmpty());

            if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
                sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
            }

            if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
                ((CachingMetadataReaderFactory)this.metadataReaderFactory).clearCache();
            }

        }
    }

在这里插入图片描述

总结:在ApplicationContext的refresh方法中,会调用invokeBeanFactoryPostProcessors激活相应的处理器,具体的方法就是在postProcessBeanDefinitionRegistry方法内部一系列逻辑委托ConfigurationClassPostProcessor的processConfigBeanDefinitions拿到了spring.factories的自动配置类全路径名,然后封装为BeanDefinitionHolder,然后下面进行

  • ConfigurationClassParser的parse方法,解析BeanDefinitionHolder,内部调用selectImports先解析、processImports再解析,完成自动装配Starter对应的bean
  • ConfigurationClassBeanDefinitionReader的loadBeanDefinitions注册BeanDefinition

3.4 自动配置类的解析

前面分析完了ConfigurationClassPostProcessor的postConfigBeanDefinitions方法,接下来就是分析ConfigurationClassParser类的parse方法,也就是这里先后调用selectImports和proceImports方法

// ConfigurationClassParser
 public void parse(Set<BeanDefinitionHolder> configCandidates) {
        Iterator var2 = configCandidates.iterator();

        while(var2.hasNext()) {
            BeanDefinitionHolder holder = (BeanDefinitionHolder)var2.next();
            // 拿到BeanDefinition
            BeanDefinition bd = holder.getBeanDefinition();

            try {
            	// BeanDefinition的类型,三种
            	// 以此种为例,注解配置的自动配置类
                if (bd instanceof AnnotatedBeanDefinition) {
                    this.parse(((AnnotatedBeanDefinition)bd).getMetadata(), holder.getBeanName());
                } 
				else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition)bd).hasBeanClass()) {
                    this.parse(((AbstractBeanDefinition)bd).getBeanClass(), holder.getBeanName());
                } 
				else {
                    this.parse(bd.getBeanClassName(), holder.getBeanName());
                }

            } catch (BeanDefinitionStoreException var6) {
                throw var6;
            } catch (Throwable var7) {
                throw new BeanDefinitionStoreException("Failed to parse configuration class [" + bd.getBeanClassName() + "]", var7);
            }
        }
		// 最后调用
        this.deferredImportSelectorHandler.process();
    }
// 委托
public void process() {
            List<ConfigurationClassParser.DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
            this.deferredImportSelectors = null;

            try {
                if (deferredImports != null) {
                    ConfigurationClassParser.DeferredImportSelectorGroupingHandler handler = ConfigurationClassParser.this.new DeferredImportSelectorGroupingHandler();
                    deferredImports.sort(ConfigurationClassParser.DEFERRED_IMPORT_COMPARATOR);
                    deferredImports.forEach(handler::register);
                    // 委托
                    handler.processGroupImports();
                }
            } finally {
                this.deferredImportSelectors = new ArrayList();
            }

        }
// 委托
public void processGroupImports() {
            Iterator var1 = this.groupings.values().iterator();

            while(var1.hasNext()) {
                ConfigurationClassParser.DeferredImportSelectorGrouping grouping = (ConfigurationClassParser.DeferredImportSelectorGrouping)var1.next();
                Predicate<String> exclusionFilter = grouping.getCandidateFilter();
                grouping.getImports().forEach((entry) -> {
                    ConfigurationClass configurationClass = (ConfigurationClass)this.configurationClasses.get(entry.getMetadata());

                    try {
                    	// 进入processImports
                        ConfigurationClassParser.this.processImports(configurationClass, ConfigurationClassParser.this.asSourceClass(configurationClass, exclusionFilter), Collections.singleton(ConfigurationClassParser.this.asSourceClass(entry.getImportClassName(), exclusionFilter)), exclusionFilter, false);
                    } catch (BeanDefinitionStoreException var5) {
                        throw var5;
                    } catch (Throwable var6) {
                        throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" + configurationClass.getMetadata().getClassName() + "]", var6);
                    }
                });
            }

        }

processImports方法处理spring.factories中的自动配置类首先就是就是需要加载自动配置类,这个时候AutoConfigurationImportSelector的selectImports方法就要发挥作用了

他返回的自动配置类需要进一步解析,因为可能对应@Import、@Bean、@ComponentScan多种类型,进一步解析递归调用processImports方法

 private void processImports(ConfigurationClass configClass, ConfigurationClassParser.SourceClass currentSourceClass, Collection<ConfigurationClassParser.SourceClass> importCandidates, Predicate<String> exclusionFilter, boolean checkForCircularImports) {
        if (!importCandidates.isEmpty()) {
            if (checkForCircularImports && this.isChainedImportOnStack(configClass)) {
                this.problemReporter.error(new ConfigurationClassParser.CircularImportProblem(configClass, this.importStack));
            } else {
                this.importStack.push(configClass);

                try {
                    Iterator var6 = importCandidates.iterator();

                    while(var6.hasNext()) {
                        ConfigurationClassParser.SourceClass candidate = (ConfigurationClassParser.SourceClass)var6.next();
                        Class candidateClass;
                        // 1. 要先对 XxxImportSelector类处理,调用它的selectImports、processImports方法
                        if (candidate.isAssignable(ImportSelector.class)) {
                            candidateClass = candidate.loadClass();
                            ImportSelector selector = (ImportSelector)ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class, this.environment, this.resourceLoader, this.registry);
                            Predicate<String> selectorFilter = selector.getExclusionFilter();
                            if (selectorFilter != null) {
                                exclusionFilter = exclusionFilter.or(selectorFilter);
                            }
							// DeferredImportSelector
                            if (selector instanceof DeferredImportSelector) {
                                this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector)selector);
                            } else {
                            	// 1.1 slelectImports方法
                                String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                                Collection<ConfigurationClassParser.SourceClass> importSourceClasses = this.asSourceClasses(importClassNames, exclusionFilter);
                                // 1.2 processImports方法
                                this.processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
                            }
                        }	
                        // 2. 对XxxImportBeanDefinitionRegistrar类处理
					 else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                            candidateClass = candidate.loadClass();
                            ImportBeanDefinitionRegistrar registrar = (ImportBeanDefinitionRegistrar)ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class, this.environment, this.resourceLoader, this.registry);
                            configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
                        } 
                        // 3. 一般配置类处理
						else {
                            this.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
                            // 如@Bean、@ComponentScan等类
                            this.processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
                        }
                    }
                } catch (BeanDefinitionStoreException var17) {
                    throw var17;
                } catch (Throwable var18) {
                    throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" + configClass.getMetadata().getClassName() + "]", var18);
                } finally {
                    this.importStack.pop();
                }
            }

        }
    }
// 自动配置类解析前置处理
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
		// 这里 shouldSkip 是 @Conditional 注解实现原理的时机,下面会说明
		// 即不满足@Conditional注解的话,就不会再进行下面selectImports、processImports等
        if (!this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
            ConfigurationClass existingClass = (ConfigurationClass)this.configurationClasses.get(configClass);
            if (existingClass != null) {
                if (configClass.isImported()) {
                    if (existingClass.isImported()) {
                        existingClass.mergeImportedBy(configClass);
                    }

                    return;
                }

                this.configurationClasses.remove(configClass);
                this.knownSuperclasses.values().removeIf(configClass::equals);
            }

            ConfigurationClassParser.SourceClass sourceClass = this.asSourceClass(configClass, filter);

            do {
            	// 真正的解析
                sourceClass = this.doProcessConfigurationClass(configClass, sourceClass, filter);
            } while(sourceClass != null);

            this.configurationClasses.put(configClass, configClass);
        }
    }

其中对于@ComponentScan的注册,有可能需要递归的完成,其中注册核心逻辑就是在processImports—>processConfigurationClass—>doProcessConfigurationClass方法

@Nullable
    protected final ConfigurationClassParser.SourceClass doProcessConfigurationClass(ConfigurationClass configClass, ConfigurationClassParser.SourceClass sourceClass, Predicate<String> filter) throws IOException {
        if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
            this.processMemberClasses(configClass, sourceClass, filter);
        }
		// 1. 处理 @PropertySources 注解
        Iterator var4 = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class, PropertySource.class).iterator();

        AnnotationAttributes importResource;
        while(var4.hasNext()) {
            importResource = (AnnotationAttributes)var4.next();
            if (this.environment instanceof ConfigurableEnvironment) {
                this.processPropertySource(importResource);
            } else {
                this.logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment");
            }
        }
		// 2. 处理 @ComponentScan 注解
        Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
        if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
            Iterator var14 = componentScans.iterator();

            while(var14.hasNext()) {
                AnnotationAttributes componentScan = (AnnotationAttributes)var14.next();
                // 解析全路径
                Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
                Iterator var8 = scannedBeanDefinitions.iterator();

                while(var8.hasNext()) {
                    BeanDefinitionHolder holder = (BeanDefinitionHolder)var8.next();
                    BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
                    if (bdCand == null) {
                        bdCand = holder.getBeanDefinition();
                    }

                    if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
                    	// 递归调用
                        this.parse(bdCand.getBeanClassName(), holder.getBeanName());
                    }
                }
            }
        }

        this.processImports(configClass, sourceClass, this.getImports(sourceClass), filter, true);
		// 3. 处理 @ImportSource 注解
        importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
        if (importResource != null) {
            String[] resources = importResource.getStringArray("locations");
            Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
            String[] var20 = resources;
            int var22 = resources.length;

            for(int var23 = 0; var23 < var22; ++var23) {
                String resource = var20[var23];
                String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
                configClass.addImportedResource(resolvedResource, readerClass);
            }
        }
		// 4. @Bean的Methods处理
        Set<MethodMetadata> beanMethods = this.retrieveBeanMethodMetadata(sourceClass);
        Iterator var18 = beanMethods.iterator();

        while(var18.hasNext()) {
            MethodMetadata methodMetadata = (MethodMetadata)var18.next();
            configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
        }
		// 5. 处理接口中的默认方法,在JDK9之后,接口中可以包含default修饰的包含方法体的方法
        this.processInterfaces(configClass, sourceClass);
        if (sourceClass.getMetadata().hasSuperClass()) {
            String superclass = sourceClass.getMetadata().getSuperClassName();
            if (superclass != null && !superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) {
                this.knownSuperclasses.put(superclass, configClass);
                return sourceClass.getSuperClass();
            }
        }

        return null;
    }

最终对于@ComponentScan的扫描是委托于ClassPathBeanDefinitionScanner来实现的,他通过字节码扫描效率会比反射机制要高很多。

3.5 Conditional机制

它的作用:它是Spring提供的一个更通用的基于条件的bean的创建,通过使用@Conditional注解,会根据满足的某一个特定条件创建一个特定的bean。如一个JAR包中包含多个bean,这多个bean之间存在先后创建的依赖关系,因此可以使用@Conditional注解完成自动配置。或者是配置自动装配的开关。

在这里插入图片描述

它的原理:分析注解@ConditionalOnProperty 原理,首先需要判断哪里调用了这个注解,发现是OnPropertyConditional类,查看他的方法,内部只有一个public的方法是getMatchOutcome,得知是由这个方法完成核心逻辑的

// OnPropertyConditional

// 将是否匹配的条件疯转到 ConditionOutcome 中,内部包含成员变量match 为true、false
@Override
	public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
		// 扫描出 @ConditionalOnProperty 注解的信息
		List<AnnotationAttributes> allAnnotationAttributes = annotationAttributesFromMultiValueMap(
				metadata.getAllAnnotationAttributes(ConditionalOnProperty.class.getName()));
		// 不匹配的配置类,不需要自动装配
		List<ConditionMessage> noMatch = new ArrayList<>();
		
		// 匹配的配置类,需要自动装配
		List<ConditionMessage> match = new ArrayList<>();
		
		for (AnnotationAttributes annotationAttributes : allAnnotationAttributes) {
			// 核心的验证逻辑
			ConditionOutcome outcome = determineOutcome(annotationAttributes, context.getEnvironment());
			// 加入对应的结果
			(outcome.isMatch() ? match : noMatch).add(outcome.getConditionMessage());
		}
		if (!noMatch.isEmpty()) {
			return ConditionOutcome.noMatch(ConditionMessage.of(noMatch));
		}
		return ConditionOutcome.match(ConditionMessage.of(match));
	}

// 核心的验证逻辑 determineOutcome

private ConditionOutcome determineOutcome(AnnotationAttributes annotationAttributes, PropertyResolver resolver) {
		Spec spec = new Spec(annotationAttributes);
		// 处理不匹配的两种情况
		// 1. missProperties对应属性缺失
		List<String> missingProperties = new ArrayList<>();
		// 2. nonMatchingProperties对应不匹配
		List<String> nonMatchingProperties = new ArrayList<>();
		
		// 这两个结果的填充,内部使用PropertyResolver来验证
		spec.collectProperties(resolver, missingProperties, nonMatchingProperties);

	
		if (!missingProperties.isEmpty()) {
			return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnProperty.class, spec)
					.didNotFind("property", "properties").items(Style.QUOTE, missingProperties));
		}
		if (!nonMatchingProperties.isEmpty()) {
			return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnProperty.class, spec)
					.found("different value in property", "different value in properties")
					.items(Style.QUOTE, nonMatchingProperties));
		}
		return ConditionOutcome
				.match(ConditionMessage.forCondition(ConditionalOnProperty.class, spec).because("matched"));
	}
1. getMatchOutcome调用切入点

结合前面processImports方法调用的流程,在processConfigurationClass步骤中主要逻辑就是对即将解析的注解做预处理

// 自动配置类解析前置处理
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
		// 这里 shouldSkip 是 @Conditional 注解实现原理的时机 
		// 即不满足@Conditional注解的话,就不会再进行下面selectImports、processImports等
        if (!this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
            ConfigurationClass existingClass = (ConfigurationClass)this.configurationClasses.get(configClass);
            if (existingClass != null) {
                if (configClass.isImported()) {
                    if (existingClass.isImported()) {
                        existingClass.mergeImportedBy(configClass);
                    }

                    return;
                }

                this.configurationClasses.remove(configClass);
                this.knownSuperclasses.values().removeIf(configClass::equals);
            }

            ConfigurationClassParser.SourceClass sourceClass = this.asSourceClass(configClass, filter);

            do {
            	// 真正的解析
                sourceClass = this.doProcessConfigurationClass(configClass, sourceClass, filter);
            } while(sourceClass != null);

            this.configurationClasses.put(configClass, configClass);
        }
    }

分析shouldSkip方法,这个方法关键地方

  1. condition的获取,代码走到这一步已经是对一个特定的自动配置类解析,可以拿到这个配置类上面的注解等信息
  2. condition的运行匹配,如@ConditionalOnProperty注解就是调用OnPropertyCondition类的getMatchOutcome方法
public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
        if (metadata != null && metadata.isAnnotated(Conditional.class.getName())) {
            if (phase == null) {
                return metadata instanceof AnnotationMetadata && ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata)metadata) ? this.shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION) : this.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
            } else {
                List<Condition> conditions = new ArrayList();
                // 1. 获取所有的condition
                Iterator var4 = this.getConditionClasses(metadata).iterator();

                while(var4.hasNext()) {
                    String[] conditionClasses = (String[])var4.next();
                    String[] var6 = conditionClasses;
                    int var7 = conditionClasses.length;

                    for(int var8 = 0; var8 < var7; ++var8) {
                        String conditionClass = var6[var8];
                        Condition condition = this.getCondition(conditionClass, this.context.getClassLoader());
                        conditions.add(condition);
                    }
                }

                AnnotationAwareOrderComparator.sort(conditions);
                var4 = conditions.iterator();

                Condition condition;
                ConfigurationPhase requiredPhase;
                do {
                    do {
                        if (!var4.hasNext()) {
                            return false;
                        }

                        condition = (Condition)var4.next();
                        requiredPhase = null;
                        if (condition instanceof ConfigurationCondition) {
                            requiredPhase = ((ConfigurationCondition)condition).getConfigurationPhase();
                        }
                    } while(requiredPhase != null && requiredPhase != phase);
                    // 2. condition匹配
                } while(condition.matches(this.context, metadata));

                return true;
            }
        } else {
            return false;
        }
    }
2. PropertyResolver属性自动化解析

SpringBoot会读取properties文件的原理就是通过PropertyResolver来实现的,如我们使用@Value来读取properties文件中的内容也是此原理

我们以@Value注解来分析,看看那个类调用了Value.class,发现是QualifierAnnotationAutowireCandidateResolver类

在这里插入图片描述

debug查看解析完properties中的值后,是怎么填充到属性中的?发现在BeanFactory接口中有resloveDependcy方法,该方法主要是依赖查找,对于@Value类型的依赖则是需要读取properties文件,参考:https://www.cnblogs.com/binarylei/p/12337145.html

发现获取Value表达式属性之后,会进入DefaultListableBeanFactory类的resloveEmbeddedValue方法

在这里插入图片描述

// AbstractBeanFactoy
@Nullable
    public String resolveEmbeddedValue(@Nullable String value) {
        if (value == null) {
            return null;
        } else {
            String result = value;
            Iterator var3 = this.embeddedValueResolvers.iterator();

            do {
                if (!var3.hasNext()) {
                    return result;
                }

                StringValueResolver resolver = (StringValueResolver)var3.next();
                // 交给了 resolveStringValue
                result = resolver.resolveStringValue(result);
            } while(result != null);

            return null;
        }
    }

该方法内部委托resolveStringValue实现properties文件的读取解析,他的作用时机切入点是在PropertySourcePlaceholderConfigurer类,它实现了BeanFactoryPostProcess
在这里插入图片描述

而StringValueResolver初始化就是在PropertySourcePlaceholderConfigurer的postProcessBeanFactory方法中,分析这个方法

  1. 初始化MutablePropertySources
  2. 初始化PropertySourcesPropertyResolver
  3. StringValueResolver初始化
  4. StringValueResolver注册
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

《Spring源码深度解析 郝佳 第2版》SpringBoot体系分析、Starter的原理 的相关文章

随机推荐

  • c语言入门这一篇就够了-学习笔记(一万字)

    内容来自慕课网 xff0c 个人学习笔记 加上了mtianyan标签标记知识点 C语言入门 gt Linux C语言编程基本原理与实践 gt Linux C语言指针与内存 gt Linux C语言结构体 https www imooc co
  • GPS接收机(一)概述

    概述 接下来的几篇博客包括如下内容 1 xff0c 圆极化天线 xff1a 包括圆极化天线的设计 xff0c 场路协同仿真 xff08 电磁场和电路 xff09 xff0c 相位中心的计算 2 xff0c 低噪放 xff1a 包括低噪放的设
  • ERROR: invalid message type: fl_com/sensor_connect_state. If this is a valid message type, perhaps y

    ERROR invalid message type fl com sensor connect state If this is a valid message type perhaps you need to type rosmake
  • libcurl进行post

    libcurl进行post main函数 xff0c 初始化和清理curl 全局初始化curl curl global init CURL GLOBAL ALL std string url 61 34 http xxxx 34 std s
  • STL几个容器的比较

    vector xff1a 连续内存 xff0c 随机访问数据成员快 xff0c 但是频繁的插入 xff08 需要移动要插入的元素的后面的所有元素 xff09 或者扩容 vector扩容后会清掉原来的数据 xff0c 拷贝到新的申请的大的内存
  • STL注意问题

    1 由于继承的存在 xff0c 拷贝会导致分割 那就是说 xff0c 如果你以基类对象建立一个容器 xff0c 而你试图插入派生类对象 xff0c 那么当对象 xff08 通过基类的拷贝构造函数 xff09 拷入容器的时候对象的派生部分会被
  • CAN总线通信协议详讲

    CAN简介 CAN是Controller Area Network 的缩写 xff08 以下称为CAN xff09 xff0c 是ISO国际标准化的串行通信协议 由德国电气商博世公司在1986 年率先提出 此后 xff0c CAN 通过IS
  • gazebo仿真——controller配置(transmission/hardwareInterface标签)

    参考roswiki controller官方说明 本文作为古月大神的补充ROS探索总结 xff08 三十一 xff09 ros control 为了在gazebo中实现机器人关节的控制 xff0c 需要求建立一个controller控制器
  • NDK--CMakeLists配置第三方so库

    当我们创建一个NDK工程时 xff0c 会自动创建一个CMakeLists txt的文件 xff0c 在AS中c 43 43 的编译器是使用LLVM xff0c 规则为cmake xff0c 今天来学习下cmake的基本套路 首先 xff0
  • postman插件下载安装教程(详细)

    一 前言 postman是一款强大网页接口调试工具 xff0c 我们在平时开发过程中经常会使用到 xff0c 一般使用最多的是postman的客户端 xff0c 实际上postman在谷歌浏览器上也提供了插件 xff0c 可以不必要安装客户
  • CMake交叉编译简单教程

    首先要安装cmaek 然后安装交叉编译链 一 CMake简介 xff1a CMake是一个跨平台的安装 编译 工具 可以通过简单的语句来描述所有平台的安装 编译过程 他能够输出各种各样的 makefile 或者 project 文件 二 C
  • 锂电池的常见接口

    我们在做一些小型化便携式设备的时候 xff0c 经常会用到锂电池 xff0c 常见的锂电池接口如图 xff1a
  • Ubuntu14.04_ROS学习笔记(7) odroid板上操作系统和电脑端主从连接

    4 29日 xff0c 距离上次写过于odroid ROS的博客已经过去近4周 xff0c 在这四周发生了很多曲折事 xff0c 研究生的调剂和面试问题 xff0c 导师双向选择也出现了问题 xff0c 调档问题 xff0c 然后和GF出去
  • ROS学习----Publisher与Subscriber

    1 Publisher 发布者 与subscriber 订阅者 关系 Publisher的主要作用是对于指定话题发布特定数据类型的消息 下面是利用代码实现一个节点 xff0c 节点创建一个Publisher并发布字符串 Hello worl
  • liunx 下如何查看make与cmake版本

    cmake cmake version 即可查看cmake的版本 make 如果是在 shell 中查看 xff0c 那么直接 make v 即可 如果是在 makefile 中获取 xff0c 则用 MAKE VERSION xff0c
  • CAN协议解析

    CAN协议解析 CAN 总线组网连线图CAN的报文格式报文格式扩展CAN错误检测 波形解析ID数据长度数据字段CRC CAN 总线组网连线图 根据CAN总线的硬件特性 xff0c 当一条CAN总线上挂接多个驱动器的时候 xff0c 应当按照
  • 字节序的大端和小端

    字节序 字节序 xff08 Byte Order xff09 是指在多字节的数据类型 xff08 如整型 浮点型等 xff09 在内存中存储时 xff0c 字节的排列顺序 大端字节序 xff08 Big Endian xff09 xff1a
  • STM32 HAL 串口收发(无DMA,中断接收)

    STM32CUBE配置 一 使用printf发送数据 xff0c 在usart c中添加代码串口重定向 USER CODE BEGIN 0 include lt stdio h gt ifdef GNUC define PUTCHAR PR
  • 2019年电赛综合测评题详解

    2019年全国大学生电子设计竞赛综合测评已经结束 xff0c 邀请到西电研究生李天红同学给大家做重点分析 首先看题目 xff1a 视频要点提示 xff1a 题目分析 常用波形变换电路 两种可行方案 方案仿真 实际过程中遇到的问题分析 完整视
  • 《Spring源码深度解析 郝佳 第2版》SpringBoot体系分析、Starter的原理

    往期博客 Spring源码深度解析 郝佳 第2版 容器的基本实现与XML文件的加载 Spring源码深度解析 郝佳 第2版 XML标签的解析 Spring源码深度解析 郝佳 第2版 bean的加载 循环依赖的解决 Spring源码深度解析