SpringBoot 配置类解析

本文首发于 vivo互联网技术 微信公众号
链接:https://mp.weixin.qq.com/s/NvPO5-FWLiOlrsOf4wLaJA
作者: Li Wanghong

SpringBoot作为Java领域非常流行的开源框架,集成了大量常用的第三方库配置,Spring Boot应用中这些第三方库几乎可以是零配置的开箱即用,大部分的 Spring Boot 应用都只需要非常少量的配置代码,开发者能够更加专注于业务逻辑。SpringBoot上手快,但是如果你的项目中业务场景需要一些特殊定制,甚至对源码进行定制化,那这时候了解原理就变成必需的了,只有充分了解源码,知道框架底层的工作原理,才能对源码中原有的机制进行修改 / 扩展等等。本文介绍了SpringBoot如何解析配置类、如何集成第三方配置。

一、基本概念介绍

在SpringBoot中推荐基于Java Config的方式来代替传统的XML方式去引入Bean,本文就是分析SpringBoot如何解析这些配置类,为容器中注入我们自定义的以及SpringBoot为我们提供的Bean。SpringBoot版本基于2.1.7.RELEASE。

1// 通常一个SpringBoot工程会含有这样一个主配置类,它位于我们项目的根包下,通过启动这个main方法就可以启动我们的项目 2// 下面我们先分析@SpringBootApplication注解有哪些作用,在第二节中分析run方法,在run方法中会进行配置类的解析 3@SpringBootApplication 4public class SpringbootApplication { 5 public static void main(String[] args) { 6 SpringApplication.run(SpringbootApplication.class, args); 7 } 8} 9 10// 点击@SpringBootApplication进去发现它其实是由三个核心注解构成的,下面分别讲解这三个注解 11@SpringBootConfiguration 12@EnableAutoConfiguration 13@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), 14 @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) 15public @interface SpringBootApplication {

1、@SpringBootConfiguration注解

1// 点进去发现它其实就是一个@Configuration注解,SpringBoot解析到就会知道这是一个配置类,会给容器中引入一些bean 2// 一个被@Configuration标注的类,相当于一个applicationContext.xml文件 3// @Configuration点进去发现其实就是一个@Component注解 4@Configuration 5public @interface SpringBootConfiguration { 6 7}

2、@EnableAutoConfiguration注解

1// 结合下面@AutoConfigurationPackage注解,发现@EnableAutoConfiguration注解就是通过@Import注解给容器中引入了两个bean, 2// 分别是AutoConfigurationImportSelector和AutoConfigurationPackages.Registrar,通过这两个类可以给容器中引入更多的类 3// 下面先介绍下@Import注解的使用 4@AutoConfigurationPackage 5@Import(AutoConfigurationImportSelector.class) 6public @interface EnableAutoConfiguration { 7} 8 9@Import(AutoConfigurationPackages.Registrar.class) 10public @interface AutoConfigurationPackage { 11}

@Import注解

在原生SpringFramework中,装配组件有下面三种方式

  1. 使用@Component注解,Spring2.5+
  2. 使用配置类@Configuration与@Bean,Spring3.0+
  3. 使用模块装配@EnableXXX与@Import,Spring3.1+

如果要注册较多的Bean,通过1) 2) 两种方式不太方便,可以通过Spring提供的模块装配功能,通过给配置类标注@Enable注解,再在注解上标注@Import注解,即可完成组件装配的效果,下面通过一个例子讲解@EnableXXX和@Import的使用。

1// 步骤1) 2// 创建几个动物的实体类,如Cat、Bird、Chicken、Dog、Duck、Pig、Sheep、Snake、Tiger 3// 接下来会通过@Import的各种用法将这些bean注入到容器中 4@Data 5public class Cat { 6 private String name; 7} 8 9// 步骤2) 在主配置类上加上我们自定义的@EnableAnimal注解,含义就是通过@EnableAnimal会给容器中导入上述的那些动物Bean 10// @EnableAnimal就可以类比我们上面说的@EnableAutoConfiguration注解,两者功能类似 11@SpringBootApplication 12@EnableAnimal 13public class SpringbootApplication { 14 public static void main(String[] args) { 15 SpringApplication.run(SpringbootApplication.class, args); 16 } 17} 18 19// 步骤3) 20// @EnableAnimal如下所示,这是我们自定义的注解,这个注解的核心是是在上面声明了@Import注解 21// @Import注解可以传入四种类型,普通类、配置类、ImportSelector的实现类、ImportBeanDefinitionRegistar的实现类 22// 下面分别讲解@Import注解导入四种类型的用法 23@Documented 24@Retention(RetentionPolicy.RUNTIME) 25@Target(ElementType.TYPE) 26@Import({Dog.class, AnimalRegisterConfiguration.class, AnimalImportSelector.class, 27 AnimalImportBeanDefinitionRegistar.class}) 28public @interface EnableAnimal { 29} 30 31// 1) 直接@Import({Dog.class}),则容器启动完成之后容器中就有Dog这个bean,注意Dog这个类我们并没有通过@Component方式注入 32 33// 2) @Import ({AnimalRegisterConfiguration.class}),通过这种Import配置类形式也可以导入 34 35@Configuration 36public class AnimalRegisterConfiguration { 37 38 @Bean 39 public Cat cat(){ 40 return new Cat("cat"); 41 } 42} 43 44// 3) @Import({AnimalImportSelector.class}),通过返回一个Spring数组的方式,数组中可以方便的指定多个Bean 45// 我们上面说的AutoConfigurationImportSelector就是这种方式,SpringBoot给容器中导入MybatisAutoConfiguration 46// 等自动配置类就是通过这种方式导入的,第二节配置类解析会讲这个 47public class AnimalImportSelector implements ImportSelector { 48 49 @Override 50 public String[] selectImports(AnnotationMetadata importingClassMetadata) { 51 return new String[] {Tiger.class.getName()}; 52 } 53} 54 55// 4) @Import({AnimalImportBeanDefinitionRegistar.class}),通过手动编码方式向registry注入Bean 56// 我们上面说的AutoConfigurationPackages.Registrar就是这种方式 57 58public class AnimalImportBeanDefinitionRegistar implements ImportBeanDefinitionRegistrar { 59 60 @Override 61 public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { 62 registry.registerBeanDefinition("snake", new RootBeanDefinition(Snake.class)); 63 } 64}

3、@ComponentScan注解

这个注解就是相当于XML中的context:component-scan,它会从定义的扫描包路径(默认是SpringBoot主配置所在的包及其子包)扫描标识了@Controller、@Service、@Repository、@Component注解的类到Spring容器中。

1// 我们可以看到这个@ComponentScan注解上显示指定了两个Filter过滤条件,它是SpringBoot提供的一种扩展机制,能让我们 2// 向IOC容器中注册一些自定义的组件过滤器,以便在包扫描的过程中过滤一些Bean 3@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), 4 @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) 5 6// 这是TypeExcludeFilter的方法,可以看到它是获取容器中所有的TypeExcludeFilter,并遍历它们去过滤一些不想要的Bean 7public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { 8 if (this.beanFactory instanceof ListableBeanFactory && getClass() == TypeExcludeFilter.class) { 9 Collection<typeexcludefilter> delegates 10 = ((ListableBeanFactory) this.beanFactory).getBeansOfType(TypeExcludeFilter.class).values(); 11 for (TypeExcludeFilter delegate : delegates) { 12 if (delegate.match(metadataReader, metadataReaderFactory)) { 13 return true; 14 } 15 } 16 } 17 return false; 18} 19 20// 我们可以自定义一个TypeExcludeFilter,它的功能就是过滤掉User这个bean,不让它注入到容器中 21@Component 22public class MyTypeExcludeFilter extends TypeExcludeFilter { 23 24 @Override 25 public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { 26 return User.class.getName().equals(metadataReader.getClassMetadata().getClassName()); 27 } 28} 29 30@Component 31public class User { 32 private String name; 33} 34 35// 可以看到AutoConfigurationExcludeFilter就是将自动配置类给过滤掉了,因为自动配置类xxxAutoConfiguration是通过 36// 上面说的AutoConfigurationImportSelector方式导入的,没必要导入两次 37public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoaderAware { 38 public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { 39 return isConfiguration(metadataReader) && isAutoConfiguration(metadataReader); 40 } 41}

4、@Conditional注解

我们说SpringBoot约定大于配置,它通过一些xxxAutoConfiguration给容器中导入了一些组件,如果你需要但是没有配置视图解析器,则SpringBoot就会提供其默认配置的视图解析器,这样就简化了配置。那么如果自己定义了一个视图解析器,那么到底SpringBoot会往容器中注入哪个呢? 查看下面默认注入的视图解析器代码,发现其上面有一个@ConditionalOnMissingBean注解,意思就是若容器中没有这个则容器会给你注入一个这样的视图解析器,若容器中有就不注入了。

1// @ConditionalOnMissingBean是通过@Conditional注解和Condition接口的实现类(OnBeanCondition.class)来实现这个效果的 2@Bean 3@ConditionalOnMissingBean 4public InternalResourceViewResolver defaultViewResolver() { 5 6@Conditional(OnBeanCondition.class) 7public @interface ConditionalOnMissingBean { 8 9@FunctionalInterface 10public interface Condition { 11 boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata); 12}

我们也可以自己实现一个自定义的条件注解。

1// 自定义一个Condition接口的实现类MyCondition,通过其matches方法来判断是否符合指定条件 2public class MyCondition implements Condition { 3 @Override 4 public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { 5 // 获取注解MyConditionAnotation注解上指定value对应的值,如果没有这个值,则不符合条件 6 String[] value = (String[]) metadata.getAnnotationAttributes(MyConditionAnotation.class.getName()).get("value"); 7 for (String property : value){ 8 if(StringUtils.isEmpty(context.getEnvironment().getProperty(property))){ 9 return false; 10 } 11 } 12 return true; 13 } 14} 15 16@Retention(RetentionPolicy.RUNTIME) 17@Target({ ElementType.TYPE, ElementType.METHOD }) 18@Documented 19@Conditional(MyCondition.class) 20public @interface MyConditionAnotation { 21 String[] value() default {}; 22} 23 24// 通过@Component给容器中注入一个A类型Bean,条件是@MyConditionAnotation注解指定的key1、key2对应的值有配置 25@Component 26@MyConditionAnotation({"key1", "key2"}) 27public class A { 28}

5、SpringFactoriesLoader

类似Java的SPI、Dubbo的SPI机制,SpringBoot也提供了一种机制,它通过读取META-INF/spring.factories文件(这些文件可能存在于类路径中的多个jar包中)来加载一些预先配置的类,而这个核心机制来源于SpringFactoriesLoader。spring.factories文件必须采用 properties 格式,其中key是接口或抽象类的全限定名,而value是用逗号分隔的实现类的全限定类名列表。

1// 我们先来看下上面讲解@Import注解讲到的AutoConfigurationImportSelector,我们说通过它就可以导入SpringBoot提供那些 2// 自动配置类. 下面是Import注解讲到的AutoConfigurationImportSelector的selectImport方法 3@Override 4public String[] selectImports(AnnotationMetadata annotationMetadata) { 5 // 忽略一些其他代码 6 // 这里的含义就是通过SpringFactoriesLoader去加载META-INF/spring.factories中配置的那些xxxAutoConfiguration 7 // 并放入String数组返回 8 AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata, 9 annotationMetadata); 10 return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); 11} 12 13// 忽略中间过程调用,后面会分析,接下来就走到这里通过SpringFactoriesLoader去加载自动配置类 14protected List<string> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { 15 // 具体调用看下面分析 16 List<string> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), 17 getBeanClassLoader()); 18 return configurations; 19} 20 21protected Class<?> getSpringFactoriesLoaderFactoryClass() { 22 return EnableAutoConfiguration.class; 23} 24 25public static List<string> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) { 26 // org.springframework.boot.autoconfigure.EnableAutoConfiguration 27 String factoryClassName = factoryClass.getName(); 28 // 获得一个Map,map形如<key,List<String>&gt;形式,后面getOrDefault是获取 29 // org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的实现类集合 30 // 注意这里面不仅仅是加载自动配置类,也会加载监听器、初始化器那些容器配置的实现类,这里统一一次加载之后放到缓存中 31 return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); 32} 33 34// 带缓存功能,从各个jar包的META-INF/spring.factories文件中加载实现类,一个key可能包含多个实现 35private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) { 36 MultiValueMap<String, String> result = cache.get(classLoader); 37 if (result != null) { 38 // 缓存中有直接返回 39 return result; 40 } 41 42 try { 43 // FACTORIES_RESOURCE_LOCATION值是META-INF/spring.factories 44 Enumeration<url> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : 45 ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); 46 result = new LinkedMultiValueMap<>(); 47 while (urls.hasMoreElements()) { 48 URL url = urls.nextElement(); 49 UrlResource resource = new UrlResource(url); 50 Properties properties = PropertiesLoaderUtils.loadProperties(resource); 51 for (Map.Entry<?, ?> entry : properties.entrySet()) { 52 // 获取key 53 String factoryClassName = ((String) entry.getKey()).trim(); 54 // 将value逗号分隔,获得各个具体的实现类 55 for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { 56 // 放入result中 57 result.add(factoryClassName, factoryName.trim()); 58 } 59 } 60 } 61 cache.put(classLoader, result); 62 // 返回的result见下图 63 return result; 64 } 65}

可以看到返回了很多自动配置类,有AopAutoConfiguration、RabbitAutoConfiguration,也有MybatisAutoConfiguraion等(图中没有截全)

6、BeanFactoryPostProcessor

  1. BeanFactoryPostProcessor,BeanFactory后置处理器,是针对于BeanFactory的扩展点,即Spring会在BeanFactory初始化之后,beanDefinition都已经loaded,但是bean还未创建前进行调用,可以修改或增加BeanDefinition。
  2. BeanDefinitionRegistryPostProcessor,是BeanFactoryPostProcessor的子接口,是针对于BeanFactory的扩展点,即Spring会在调用BeanFactoryPostProcessor之前调用它。我们下面要重点分析的
  3. ConfigurationClassPostProcessor就是该接口的实现类,SpringBoot就是通过它去解析配置类,封装成一个个BeanDefinition注入到容器中。
  4. BeanPostProcessor,是针对Bean的扩展点,即Spring会在Bean初始化前后调用方法对Bean进行处理,AOP、依赖注入就是通过BeanPostProcessor实现的。

下面是自定义的一个BeanFactoryPostProcessor和BeanPostProcessor,发现通过BeanFactoryPostProcessor可以往容器中增加新的Bean或者修改原有的Bean定义,通过BeanPostProcessor可以修改已经创建好的Bean的属性值。

1@Component 2public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { 3 4 @Override 5 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { 6 BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; 7 8 // 往容器中新增BeanDefinition 9 GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); 10 beanDefinition.setBeanClass(Chicken.class); 11 registry.registerBeanDefinition("beanFactoryPostProcessor-Chicken", beanDefinition); 12 13 // 修改容器中原有的BeanDefinition 14 BeanDefinition snake = registry.getBeanDefinition("snake"); 15 snake.setLazyInit(true); 16 } 17} 18 19@Component 20public class CatBeanPostProcessor implements BeanPostProcessor { 21 22 @Override 23 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 24 return bean; 25 } 26 27 @Override 28 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 29 if (bean instanceof Cat){ 30 Cat cat = (Cat) bean; 31 cat.setName("changeNameCat"); 32 } 33 return bean; 34 } 35}

二、SpringBoot启动流程概述 

第一节是SpringBoot解析自动配置类会用到的一些知识点,下面我们来看SpringBoot解析配置类的具体过程。上图是SpringBoot启动流程图,其中在refreshContext的第五步会调用容器的BeanFactoryPostProcessor的postProcessBeanDefinitionRegistry方法。其中有一个是ConfigurationClassPostProcessor,它是在创建ConfigurableApplicationContext时设置到容器中的,如下所示。

1// 图中说的创建ConfigurableApplicationContext,默认创建的是普通的Servlet Web容器,就是下面这个 2// 通过反射创建会走到其默认的构造函数 3public AnnotationConfigServletWebServerApplicationContext() { 4 // 这里面进去会走到下面代码 5 this.reader = new AnnotatedBeanDefinitionReader(this); 6 this.scanner = new ClassPathBeanDefinitionScanner(this); 7} 8 9// 走到这里 10registerAnnotationConfigProcessors(registry, null); 11 12// 走到这里 13// 向容器中注入一个ConfigurationClassPostProcessor,它是BeanFactoryPostProcessor 14if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) { 15 RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class); 16 def.setSource(source); 17 beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)); 18} 19 20// 向容器中注入一个AutowiredAnnotationBeanPostProcessor,它是BeanPostProcessor,用于解决依赖注入的 21if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) { 22 RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class); 23 def.setSource(source); 24 beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)); 25}

三、配置类解析

上面说到在refreshContex中的第五步时,会调用容器中的BeanFactoryPostProcessor

的postProcessBeanDefinitionRegistry方法。其中有一个是ConfigurationClassPostProcessor,这是我们解析自动配置类的入口,下面分析其postProcessBeanDefinitionRegistry方法。

1、配置类解析流程概述

1@Override 2public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { 3 // 删掉一些非关键代码 4 processConfigBeanDefinitions(registry); 5}

下面的processConfigBeanDefinitions方法就是对应上图中的步骤1、2、3、4、5,其中步骤4和步骤5比较长,单独拆出来分析。

1public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { 2 List<beandefinitionholder> configCandidates = new ArrayList<>(); 3 // 获取容器中已注册的bean名字,见下图,注意,这里容器中这些BeanDefinition都是容器初始化过程中容器添加进去的 4 // 不是我们业务代码的beanDefinition,这段代码其实是连贯的,为了注释图片方便才分开 5 String[] candidateNames = registry.getBeanDefinitionNames(); 6 7 for (String beanName : candidateNames) { 8 // 获取BeanDefinition 9 BeanDefinition beanDef = registry.getBeanDefinition(beanName); 10 // 判断这个BeanDefinition的configurationClass属性是不是full或者lite,如果是认为已经处理过了,第一次时默认为空, 11 // 走下面分支 12 if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) || 13 ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) { 14 // 打印日志记录下 15 } else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { 16 // 1) 下面先分析下这个checkConfigurationClassCandidate方法,这边看方法名也可以猜到是检测该类是不是配置类 17 // 是配置类的意思就是它会给容器中引入bean,这个方法判断主要就是看这个类的元信息中有没有@Configuration注解 18 // 有没有@Component注解、有没有@ComponentScan、@Import、@ImportResource注解,有没有@Bean方法 19 // 构造一个BeanDefinitionHolder,放入configCandidates中 20 configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); 21 } 22 } 23 24 // Return immediately if no @Configuration classes were found 25 // 上图中容易默认已经引入了7个BeanDefinition,经过上面检测发现默认就一个符合条件的配置类,即我们的主配置类 26 // 这里面configCandidates就一个,就是SpringBootApplication 27 if (configCandidates.isEmpty()) { 28 return; 29 } 30 31 // Sort by previously determined @Order value, if applicable 32 // 排序 33 configCandidates.sort((bd1, bd2) -> { 34 int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); 35 int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); 36 return Integer.compare(i1, i2); 37 }); 38 39 // 删掉部分代码 40 41 // Parse each @Configuration class 42 // 配置类解析工具 43 ConfigurationClassParser parser = new ConfigurationClassParser( 44 this.metadataReaderFactory, this.problemReporter, this.environment, 45 this.resourceLoader, this.componentScanBeanNameGenerator, registry); 46 // 待处理集合 47 Set<beandefinitionholder> candidates = new LinkedHashSet<>(configCandidates); 48 // 已处理集合 49 Set<configurationclass> alreadyParsed = new HashSet<>(configCandidates.size()); 50 // 循环处理直到candidates.isEmpty() 51 do { 52 // 这边开始解析,对应步骤4 53 parser.parse(candidates); 54 parser.validate(); 55 // 取出第四步解析得到的一些configurationClasses集合 56 Set<configurationclass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); 57 configClasses.removeAll(alreadyParsed); 58 59 // 删除一部分代码 60 // 这边也会去加载BeanDefinition,对应图中步骤五 61 this.reader.loadBeanDefinitions(configClasses); 62 alreadyParsed.addAll(configClasses); 63 } 64 while (!candidates.isEmpty()); 65}

2、检测是否是配置类

在配置类解析流程图中,第二步,会获取容器中已经注册的BeanDefinition,放入candidateNames中,然后依次遍历这些BeanDefinition,判断它有没有被处理过,如果处理过就不管,否则通过checkConfigurationClassCandidate方法去判断它是不是配置类,判断方法如下。通过阅读这段代码,发现如果一个类上面有@Configuration注解、或者有@Component、@ComponentScan、@Import、@ImportResource注解、或者有@Bean标注的方法,则认为它是一个配置类。默认情况下,走到这里时最终只有一个candidateName符合,它是我们的主配置类,也就是SpringbootApplication这个Bean。

1ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory); 2 3public static boolean checkConfigurationClassCandidate(BeanDefinition beanDef, 4 MetadataReaderFactory metadataReaderFactory) { 5 6 String className = beanDef.getBeanClassName(); 7 // 获取下类名,如果类名为空或者该类为工厂类 8 if (className == null || beanDef.getFactoryMethodName() != null) { 9 return false; 10 } 11 12 // 获取类的元数据信息 13 AnnotationMetadata metadata; 14 // 上图的7个candidateNames中只有一个springbootApplication是AnnotatedBeanDefinition,其余全返回false 15 if (beanDef instanceof AnnotatedBeanDefinition && 16 className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) { 17 // Can reuse the pre-parsed metadata from the given BeanDefinition... 18 // springbootApplication走到这里 19 metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata(); 20 } 21 else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) { 22 // Check already loaded Class if present... 23 // since we possibly can't even load the class file for this Class. 24 Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass(); 25 metadata = new StandardAnnotationMetadata(beanClass, true); 26 } 27 else { 28 try { 29 MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className); 30 // 读取类的元数据信息,这里面包括注解等信息 31 metadata = metadataReader.getAnnotationMetadata(); 32 } 33 catch (IOException ex) { 34 return false; 35 } 36 } 37 38 // metadata.isAnnotated(Configuration.class.getName()),这个就是判断类上面有没有@Configuration注解 39 if (isFullConfigurationCandidate(metadata)) { 40 // 如果true的话设置下这个属性,那么就标记为处理过了 41 beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL); 42 } else if (isLiteConfigurationCandidate(metadata)) { 43 beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE); 44 } 45 else { 46 // 其余6个返回false 47 return false; 48 } 49 50 // It's a full or lite configuration candidate... Let's determine the order value, if any. 51 Integer order = getOrder(metadata); 52 if (order != null) { 53 // 获取下类上的@Order信息 54 beanDef.setAttribute(ORDER_ATTRIBUTE, order); 55 } 56 return true; 57} 58 59public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) { 60 // Do not consider an interface or an annotation... 61 if (metadata.isInterface()) { 62 return false; 63 } 64 65 // Any of the typical annotations found? 66 for (String indicator : candidateIndicators) { 67 // 判断下类上面有没有这几个注解 68 if (metadata.isAnnotated(indicator)) { 69 return true; 70 } 71 } 72 73 // Finally, let's look for @Bean methods... 74 try { 75 // 判断有没有@Bean的方法 76 return metadata.hasAnnotatedMethods(Bean.class.getName()); 77 } 78 return false; 79 } 80} 81 82private static final Set<string> candidateIndicators = new HashSet<>(); 83 84static { 85 candidateIndicators.add(Component.class.getName()); 86 candidateIndicators.add(ComponentScan.class.getName()); 87 candidateIndicators.add(Import.class.getName()); 88 candidateIndicators.add(ImportResource.class.getName()); 89}

3、步骤四解析

1public void parse(Set<beandefinitionholder> configCandidates) { 2 // 删除部分代码,实际执行时这里的configCandidates就一个springBootApplication代表的主配置类 3 for (BeanDefinitionHolder holder : configCandidates) { 4 // 获取BeanDefinition 5 BeanDefinition bd = holder.getBeanDefinition(); 6 // 我们的SpringBootApplication会走到这边,下面先分析这边 7 parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName()); 8 } 9 // (**)处这边也要留意下,这边会处理DeferredImportSelector,我们前面说的AutoConfigurationImportSelector就是在这边处理 10 // 给容器中导入xxxAutoConfiguration 11 this.deferredImportSelectorHandler.process(); 12} 13 14protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException { 15 processConfigurationClass(new ConfigurationClass(metadata, beanName)); 16} 17 18protected void processConfigurationClass(ConfigurationClass configClass) throws IOException { 19 // 根据当前类上面的@Conditional注解标注的条件判断是否要解析这个配置类 20 if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { 21 return; 22 } 23 // 以configClass作为key去获取,第一次来肯定是获取不到的,走下面逻辑 24 ConfigurationClass existingClass = this.configurationClasses.get(configClass); 25 if (existingClass != null) { 26 if (configClass.isImported()) { 27 if (existingClass.isImported()) { 28 existingClass.mergeImportedBy(configClass); 29 } 30 // Otherwise ignore new imported config class; existing non-imported class overrides it. 31 return; 32 } 33 else { 34 // Explicit bean definition found, probably replacing an import. 35 // Let's remove the old one and go with the new one. this.configurationClasses.remove(configClass); 36 this.knownSuperclasses.values().removeIf(configClass::equals); 37 } 38 } 39 40 // Recursively process the configuration class and its superclass hierarchy. 41 // 这一步其实没有做啥,重点还是看下一步骤 42 SourceClass sourceClass = asSourceClass(configClass); 43 do { 44 // 这里是重点,里面具体分为8大步骤,单独拿一小节分析 45 // b) doProcessConfigurationClass 46 sourceClass = doProcessConfigurationClass(configClass, sourceClass); 47 } 48 while (sourceClass != null); 49 // 放入configurationClasses中 50 this.configurationClasses.put(configClass, configClass); 51} 52 53// 上面的asSourceClass最终其实就是封装了一个SourceClass对象 54public SourceClass(Object source) { 55 this.source = source; 56 if (source instanceof Class) { 57 this.metadata = new StandardAnnotationMetadata((Class<?>) source, true); 58 } 59 else { 60 this.metadata = ((MetadataReader) source).getAnnotationMetadata(); 61 } 62}

下面这个doProcessConfigurationClass具体分为8个小步骤去解析,对应步骤四种的A-H步骤

1protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) 2 throws IOException { 3 // 判断这个类上面有没有@Component注解 4 if (configClass.getMetadata().isAnnotated(Component.class.getName())) { 5 // Recursively process any member (nested) classes first 6 // 如果有的话,遍历其内部类,然后也是调用doProcessConfigurationClass递归处理 7 processMemberClasses(configClass, sourceClass); 8 } 9 10 // Process any @PropertySource annotations 11 // 处理PropertySource注解,之前讲解属性配置也分析过,就是将该注解对应的属性文件加载到Environment中 12 for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( 13 sourceClass.getMetadata(), PropertySources.class, 14 org.springframework.context.annotation.PropertySource.class)) { 15 if (this.environment instanceof ConfigurableEnvironment) { 16 processPropertySource(propertySource); 17 } 18 } 19 20 // Process any @ComponentScan annotations 21 // 处理@ComponentScan注解,将其指定的包下的bean注册到框架中 22 Set<annotationattributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( 23 sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); 24 if (!componentScans.isEmpty() && 25 !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { 26 for (AnnotationAttributes componentScan : componentScans) { 27 // The config class is annotated with @ComponentScan -&gt; perform the scan immediately 28 Set<beandefinitionholder> scannedBeanDefinitions = 29 this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); 30 // Check the set of scanned definitions for any further config classes and parse recursively if needed 31 for (BeanDefinitionHolder holder : scannedBeanDefinitions) { 32 BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); 33 if (bdCand == null) { 34 bdCand = holder.getBeanDefinition(); 35 } 36 if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { 37 parse(bdCand.getBeanClassName(), holder.getBeanName()); 38 } 39 } 40 } 41 } 42 43 // Process any @Import annotations 44 // 处理Import注解 45 processImports(configClass, sourceClass, getImports(sourceClass), true); 46 47 // Process any @ImportResource annotations 48 // 处理@ImportResource注解,可以通过它来指定xml文件,BeanFactory就会读取这个xml文件将bean注册进去 49 AnnotationAttributes importResource = 50 AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); 51 if (importResource != null) { 52 String[] resources = importResource.getStringArray("locations"); 53 Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader"); 54 for (String resource : resources) { 55 String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); 56 configClass.addImportedResource(resolvedResource, readerClass); 57 } 58 } 59 60 // Process individual @Bean methods 61 // 处理我们的类中使用@Bean注解的方法,添加到configClass的beanMethod中 62 Set<methodmetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass); 63 for (MethodMetadata methodMetadata : beanMethods) { 64 configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); 65 } 66 67 // Process default methods on interfaces 68 // 处理接口的默认方法,遍历这个类的接口,判断有没有使用@Bean注解的非抽象方法,添加到configClass的beanMethod中 69 processInterfaces(configClass, sourceClass); 70 71 // Process superclass, if any 72 // 递归处理父类,这边返回父类上层方法会递归处理 73 if (sourceClass.getMetadata().hasSuperClass()) { 74 // 判断父类不为null且不在knownSuperclasses中且不以Java开头 75 String superclass = sourceClass.getMetadata().getSuperClassName(); 76 if (superclass != null && !superclass.startsWith("java") && 77 !this.knownSuperclasses.containsKey(superclass)) { 78 this.knownSuperclasses.put(superclass, configClass); 79 // Superclass found, return its annotation metadata and recurse 80 return sourceClass.getSuperClass(); 81 } 82 } 83 84 // No superclass -&gt; processing is complete 85 return null; 86}

(1)处理内部类

1if (configClass.getMetadata().isAnnotated(Component.class.getName())) { 2 // Recursively process any member (nested) classes first 3 processMemberClasses(configClass, sourceClass); 4} 5 6 7 // Register member (nested) classes that happen to be configuration classes themselves. 8 private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { 9 Collection<SourceClass> memberClasses = sourceClass.getMemberClasses(); 10 // 判断是否有内部类,没有的话直接不处理 11 if (!memberClasses.isEmpty()) { 12 List<SourceClass> candidates = new ArrayList<>(memberClasses.size()); 13 for (SourceClass memberClass : memberClasses) { 14 // 判断是否是配置类,判断也很简单,之前分析过,判断类上面有没有@Configuration注解、@Import、@ImportResource 15 // @Component、@ComponentScan以及@Bean标注的方法 16 if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) && 17 !memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) { 18 // 加入到candidates中然后排个序 19 candidates.add(memberClass); 20 } 21 } 22 OrderComparator.sort(candidates); 23 for (SourceClass candidate : candidates) { 24 // 防止A引入防止A引入B,B引入A 25 if (this.importStack.contains(configClass)) { 26 this.problemReporter.error(new CircularImportProblem(configClass, this.importStack)); 27 } 28 else { 29 this.importStack.push(configClass); 30 try { 31 // 放入栈中并遍历处理这些配置类,也是递归处理,调用之前的doProcessConfigurationClass处理这个配置类 32 processConfigurationClass(candidate.asConfigClass(configClass)); 33 } 34 finally { 35 this.importStack.pop(); 36 } 37 } 38 } 39 } 40}

(2)处理@PropertySource注解

1@SpringBootApplication 2@PropertySource({"demo.properties"}) 3public class Springboot2Application { 4 5for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( 6 sourceClass.getMetadata(), PropertySources.class, 7 org.springframework.context.annotation.PropertySource.class)) { 8 if (this.environment instanceof ConfigurableEnvironment) { 9 // 这边就不进去看了,主要是读取@PropertySource注解指定的文件,将其封装成一个属性集放入到环境中 10 processPropertySource(propertySource); 11 } 12}

(3)处理@ComponentScan注解

1Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( 2 sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); 3if (!componentScans.isEmpty() && 4 !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { 5 for (AnnotationAttributes componentScan : componentScans) { 6 // The config class is annotated with @ComponentScan -> perform the scan immediately 7 // 下面先分析这个parse方法 8 Set<BeanDefinitionHolder> scannedBeanDefinitions = 9 this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); 10 // Check the set of scanned definitions for any further config classes and parse recursively if needed 11 for (BeanDefinitionHolder holder : scannedBeanDefinitions) { 12 BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); 13 if (bdCand == null) { 14 bdCand = holder.getBeanDefinition(); 15 } 16 // 如果是配置类,再递归处理 17 if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { 18 parse(bdCand.getBeanClassName(), holder.getBeanName()); 19 } 20 } 21 } 22} 23 24 25public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) { 26 ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry, 27 componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader); 28 29 Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator"); 30 boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass); 31 // 设置一个bean名字生成器,默认就是使用org.springframework.beans.factory.support.BeanNameGenerator 32 scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator : 33 BeanUtils.instantiateClass(generatorClass)); 34 35 // 就是默认的 36 ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy"); 37 if (scopedProxyMode != ScopedProxyMode.DEFAULT) { 38 scanner.setScopedProxyMode(scopedProxyMode); 39 } 40 else { 41 Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver"); 42 // 理解是元数据解析器 43 scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass)); 44 } 45 46 // 设置下扫描的资源模式,是**/*.class 47 scanner.setResourcePattern(componentScan.getString("resourcePattern")); 48 49 // 添加IncludeFilter和ExcludeFilter 50 for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) { 51 for (TypeFilter typeFilter : typeFiltersFor(filter)) { 52 scanner.addIncludeFilter(typeFilter); 53 } 54 } 55 for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) { 56 for (TypeFilter typeFilter : typeFiltersFor(filter)) { 57 scanner.addExcludeFilter(typeFilter); 58 } 59 } 60 // 设置是否懒加载 61 boolean lazyInit = componentScan.getBoolean("lazyInit"); 62 if (lazyInit) { 63 scanner.getBeanDefinitionDefaults().setLazyInit(true); 64 } 65 66 // 解析扫描的包路径加入到basePackages中 67 Set<String> basePackages = new LinkedHashSet<>(); 68 String[] basePackagesArray = componentScan.getStringArray("basePackages"); 69 for (String pkg : basePackagesArray) { 70 String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg), 71 ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); 72 Collections.addAll(basePackages, tokenized); 73 } 74 for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) { 75 // 解析basePackageClasses所在的包并加入到basePackages 76 basePackages.add(ClassUtils.getPackageName(clazz)); 77 } 78 // 如果是空的,将声明该注解所在的类的包加入到basePackages 79 if (basePackages.isEmpty()) { 80 // 通常我们的主配置类是没有声明包扫描的路径的,所以这里会将主配置类所在的包加到这里面 81 basePackages.add(ClassUtils.getPackageName(declaringClass)); 82 } 83 84 // 添加一个ExcludeFilter,跳过声明该注解的类 85 scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) { 86 @Override 87 protected boolean matchClassName(String className) { 88 return declaringClass.equals(className); 89 } 90 }); 91 return scanner.doScan(StringUtils.toStringArray(basePackages)); 92} 93 94 95protected Set<BeanDefinitionHolder> doScan(String... basePackages) { 96 Assert.notEmpty(basePackages, "At least one base package must be specified"); 97 Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>(); 98 // 遍历所有的包路径 99 for (String basePackage : basePackages) { 100 // 获取该包下面所有符合条件的BeanDefinition,然后遍历处理,下面会分析 101 Set<BeanDefinition> candidates = findCandidateComponents(basePackage); 102 for (BeanDefinition candidate : candidates) { 103 ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); 104 candidate.setScope(scopeMetadata.getScopeName()); 105 // 通过beanNameGenerator生成beanName 106 String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); 107 // 这两个if判断逻辑比较简单,就是设置一些Lazy、DependsOn属性 108 if (candidate instanceof AbstractBeanDefinition) { 109 postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); 110 } 111 if (candidate instanceof AnnotatedBeanDefinition) { 112 AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); 113 } 114 // 这边是检查下有没有之前定义过这个BeanDefinition 115 if (checkCandidate(beanName, candidate)) { 116 BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); 117 definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, 118 definitionHolder, this.registry); 119 // 将该beanDefinition加入到集合中,并注册到容器中 120 beanDefinitions.add(definitionHolder); 121 registerBeanDefinition(definitionHolder, this.registry); 122 } 123 } 124 } 125 return beanDefinitions; 126} 127 128 129private Set<BeanDefinition> scanCandidateComponents(String basePackage) { 130 Set<BeanDefinition> candidates = new LinkedHashSet<>(); 131 try { 132 // 扫描指定包路径及其子包下面的class文件,将其封装成Resource对象 133 // classpath*:com/lwh/springboot/**/*.class 134 String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + 135 resolveBasePackage(basePackage) + '/' + this.resourcePattern; 136 Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); 137 138 for (Resource resource : resources) { 139 if (resource.isReadable()) { 140 try { 141 MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); 142 if (isCandidateComponent(metadataReader)) { 143 ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); 144 sbd.setResource(resource); 145 sbd.setSource(resource); 146 if (isCandidateComponent(sbd)) { 147 candidates.add(sbd); 148 } 149 } 150 } 151 } 152 } 153 } 154 return candidates; 155} 156 157// 通过之前设置的几个filter进行过滤 158protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { 159 for (TypeFilter tf : this.excludeFilters) { 160 if (tf.match(metadataReader, getMetadataReaderFactory())) { 161 return false; 162 } 163 } 164 for (TypeFilter tf : this.includeFilters) { 165 if (tf.match(metadataReader, getMetadataReaderFactory())) { 166 return isConditionMatch(metadataReader); 167 } 168 } 169 return false; 170}

(4)处理@Import注解

1// Process any @Import annotations 2// getImports方法就是去递归扫描configClass上面所有的注解,将@Import注解标注的值放入importCandidates中,见下图 3processImports(configClass, sourceClass, getImports(sourceClass), true); 4 5private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, 6 Collection<SourceClass> importCandidates, boolean checkForCircularImports) { 7 8 if (importCandidates.isEmpty()) { 9 return; 10 } 11 12 if (checkForCircularImports && isChainedImportOnStack(configClass)) { 13 this.problemReporter.error(new CircularImportProblem(configClass, this.importStack)); 14 } 15 else { 16 this.importStack.push(configClass); 17 try { 18 for (SourceClass candidate : importCandidates) { 19 // 依次遍历判断类型 20 // 其中有一个是这个类型,@Import(AutoConfigurationImportSelector.class) 21 // 这个就是自动配置原理,导入xxxAutoConfiguration这些类 22 if (candidate.isAssignable(ImportSelector.class)) { 23 // Candidate class is an ImportSelector -> delegate to it to determine imports 24 Class<?> candidateClass = candidate.loadClass(); 25 // 实例化并调用xxxAware的方法并注入相关属性 26 ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class); 27 ParserStrategyUtils.invokeAwareMethods( 28 selector, this.environment, this.resourceLoader, this.registry); 29 // 它是DeferredImportSelector类型的 30 if (selector instanceof DeferredImportSelector) { 31 // deferredImportSelectors = new ArrayList<>() 32 // 这边会将两个参数封装下加入到deferredImportSelectors中,后面处理 33 // 加入到deferredImportSelectors中后,具体的处理是this.deferredImportSelectorHandler.process(); 34 this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector); 35 } 36 else { 37 // 不是的话获取@Import导入的类名数组 38 String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); 39 Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames); 40 // 然后再递归处理 41 processImports(configClass, currentSourceClass, importSourceClasses, false); 42 } 43 } 44 // @Import(AutoConfigurationPackages.Registrar.class),我们的主配置类上面的注解就是这个类型 45 // 这个是用于导入主配置类所在包及其子包下的BeanDefinition 46 else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) { 47 // Candidate class is an ImportBeanDefinitionRegistrar -> 48 // delegate to it to register additional bean definitions 49 Class<?> candidateClass = candidate.loadClass(); 50 ImportBeanDefinitionRegistrar registrar = 51 BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class); 52 ParserStrategyUtils.invokeAwareMethods( 53 registrar, this.environment, this.resourceLoader, this.registry); 54 // 这边就是将这两个参数作为key,value放入了一个map中 55 // this.importBeanDefinitionRegistrars.put(registrar, importingClassMetadata); 56 configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata()); 57 } 58 else { 59 // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar -> 60 // process it as an @Configuration class 61 this.importStack.registerImport( 62 currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); 63 // 当做一个普通类处理,判断是不是配置类,递归处理 64 processConfigurationClass(candidate.asConfigClass(configClass)); 65 } 66 } 67 } 68 finally { 69 this.importStack.pop(); 70 } 71 } 72}

(5)处理@ImportSource注解

1// 这种就是Spring中常用的通过XML形式注入的方式 2@SpringBootApplication 3@ImportResource("test.xml") 4public class Springboot2Application { 5 6// Process any @ImportResource annotations 7// 可以使用@ImportResource注解指定xml文件,导入BeanDefinition 8AnnotationAttributes importResource = 9 AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); 10if (importResource != null) { 11 String[] resources = importResource.getStringArray("locations"); 12 Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader"); 13 for (String resource : resources) { 14 // 就是test.xml 15 String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); 16 configClass.addImportedResource(resolvedResource, readerClass); 17 } 18} 19 20public void addImportedResource(String importedResource, Class<? extends BeanDefinitionReader> readerClass) { 21 // 这边就是放入到了map中,这边是先统一存放起来,在步骤五的4)在真正进行导入BeanDefinition 22 this.importedResources.put(importedResource, readerClass); 23} 24 25private final Map<String, Class<? extends BeanDefinitionReader>> importedResources = new LinkedHashMap<>();

(6)处理@Bean标注的方法

1// Process individual @Bean methods 2// 获取当前类中的Bean方法 3Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass); 4for (MethodMetadata methodMetadata : beanMethods) { 5 // 这边也是加入到set中,见下面代码,也是在步骤五的3)中进行处理 6 configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); 7} 8 9private final Set<BeanMethod> beanMethods = new LinkedHashSet<>();

(7)处理默认方法

1// 默认方法举例,主配置类实现这个接口就可以 2public interface ConfigurationInterface { 3 4 @Bean 5 default Pig pig(){ 6 return new Pig(); 7 } 8} 9 10 // Process default methods on interfaces 11 processInterfaces(configClass, sourceClass); 12 13 // Register default methods on interfaces implemented by the configuration class. 14 // 这边也是递归处理其父接口,判断父接口中默认方法是不是@Bean方法 15 private void processInterfaces(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { 16 for (SourceClass ifc : sourceClass.getInterfaces()) { 17 Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(ifc); 18 for (MethodMetadata methodMetadata : beanMethods) { 19 if (!methodMetadata.isAbstract()) { 20 // 也是在步骤五的3)中进行处理 21 configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); 22 } 23 } 24 processInterfaces(configClass, ifc); 25 } 26}

(8)递归处理父类

1// Process superclass, if any 2if (sourceClass.getMetadata().hasSuperClass()) { 3 String superclass = sourceClass.getMetadata().getSuperClassName(); 4 if (superclass != null && !superclass.startsWith("java") && 5 !this.knownSuperclasses.containsKey(superclass)) { 6 this.knownSuperclasses.put(superclass, configClass); 7 // Superclass found, return its annotation metadata and recurse 8 return sourceClass.getSuperClass(); 9 } 10}

讲到这边步骤四中的大部分方法已经分析完了,但是还有一个重要步骤没有分析,就是导入xxxAutoConfiguration这些自动配置类部分。下面分析:

1// 在解析@Import注解时,我们的AutoConfigurationImportSelector就是DeferredImportSelector类型的,它的意思是延迟导 2// 入. 为啥要延迟导入? 因为AutoConfigurationImportSelector是给容器中导入一些默认的组件,如果容器中已经有这种类型 3// 的组件了,那么就不再导入. 因此,它是故意先等SpringBoot容器去解析那些用户自定义的Bean,最后发现没有才来导入这 4// 些默认的组件 5if (selector instanceof DeferredImportSelector) { 6 // deferredImportSelectors = new ArrayList<>() 7 // 这边会将两个参数封装下加入到deferredImportSelectors中,后面处理 8 // 加入到deferredImportSelectors中后,具体的处理是this.deferredImportSelectorHandler.process(); 9 this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector); 10} 11 12// 步骤四就是从这里开始分析的 13public void parse(Set<beandefinitionholder> configCandidates) { 14 // 删除部分代码,实际执行时这里的configCandidates就一个springBootApplication代表的主配置类 15 for (BeanDefinitionHolder holder : configCandidates) { 16 // 获取BeanDefinition 17 BeanDefinition bd = holder.getBeanDefinition(); 18 // 我们的SpringBootApplication会走到这边,下面先分析这边 19 parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName()); 20 } 21 22 // 给容器中导入xxxAutoConfiguration,我们现在分析这边 23 this.deferredImportSelectorHandler.process(); 24} 25 26 27// 上面方法会走到这里 28public void processGroupImports() { 29 for (DeferredImportSelectorGrouping grouping : this.groupings.values()) { 30 // 下面先分析这个getImports方法 31 grouping.getImports().forEach(entry -> { 32 ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata()); 33 try { 34 processImports(configurationClass, asSourceClass(configurationClass), 35 asSourceClasses(entry.getImportClassName()), false); 36 } 37 }); 38 } 39} 40 41public Iterable<group.entry> getImports() { 42 // 这边的deferredImports是通过grouping.add(deferredImport)添加进去的 43 for (DeferredImportSelectorHolder deferredImport : this.deferredImports) { 44 this.group.process(deferredImport.getConfigurationClass().getMetadata(), 45 deferredImport.getImportSelector()); 46 } 47 return this.group.selectImports(); 48} 49 50public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) { 51 // 下面先分析下这个getAutoConfigurationEntry方法 52 AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector) 53 .getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata); 54 // 加入到这个autoConfigurationEntries中 55 this.autoConfigurationEntries.add(autoConfigurationEntry); 56 for (String importClassName : autoConfigurationEntry.getConfigurations()) { 57 // 接着依次遍历放入到这个map中,Map<string, annotationmetadata> entries = new LinkedHashMap<>() 58 this.entries.putIfAbsent(importClassName, annotationMetadata); 59 } 60} 61 62// 这边就到了AutoConfigurationImportSelector类的方法中 63protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, 64 AnnotationMetadata annotationMetadata) { 65 // 判断是否支持自动配置 66 if (!isEnabled(annotationMetadata)) { 67 return EMPTY_ENTRY; 68 } 69 // 这个attributes属性就是上图中显示的,用来过滤自动配置类的 70 AnnotationAttributes attributes = getAttributes(annotationMetadata); 71 // 下面分析这个方法,这个就是加载容器中的自动配置类 72 List<string> configurations = getCandidateConfigurations(annotationMetadata, attributes); 73 // 去除重复的,方法就是放入set再放入list中 74 configurations = removeDuplicates(configurations); 75 // 去除掉应该被排除的 76 Set<string> exclusions = getExclusions(annotationMetadata, attributes); 77 checkExcludedClasses(configurations, exclusions); 78 configurations.removeAll(exclusions); 79 80 // 通过filter过滤,下面分析,过滤完发现只有22个了 81 configurations = filter(configurations, autoConfigurationMetadata); 82 // 发布一个事件,好像没有做啥关键的 83 fireAutoConfigurationImportEvents(configurations, exclusions); 84 // 将configurations封装成AutoConfigurationEntry返回 85 return new AutoConfigurationEntry(configurations, exclusions); 86} 87 88protected boolean isEnabled(AnnotationMetadata metadata) { 89 if (getClass() == AutoConfigurationImportSelector.class) { 90 // 判断有没有配置这个属性,没有的话默认为true, 91 // String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; 92 return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true); 93 } 94 return true; 95} 96 97protected List<string> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { 98 // getSpringFactoriesLoaderFactoryClass()返回EnableAutoConfiguration.class,那么这边就是获取容器中所有的字段配置类 99 List<string> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), 100 getBeanClassLoader()); 101 return configurations; 102} 103 104protected List<autoconfigurationimportfilter> getAutoConfigurationImportFilters() { 105 return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader); 106}

4、步骤五解析

1// 上面分析的是配置类解析的步骤四 2parser.parse(candidates); 3 4// 下面来看第五步,步骤四中解析得到的配置类会放在configurationClasses中 5Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); 6configClasses.removeAll(alreadyParsed); 7this.reader.loadBeanDefinitions(configClasses); 8 9public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) { 10 TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator(); 11 for (ConfigurationClass configClass : configurationModel) { 12 loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator); 13 } 14} 15 16private void loadBeanDefinitionsForConfigurationClass( 17 ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) { 18 // 判断是否应该跳过 19 if (trackedConditionEvaluator.shouldSkip(configClass)) { 20 return; 21 } 22 23 // 向容器中注入这个配置类所代表的的BeanDefinition 24 if (configClass.isImported()) { 25 registerBeanDefinitionForImportedConfigurationClass(configClass); 26 } 27 // 遍历这个配置类的所有的Bean方法,注入这些@Bean标注的方法要引入的BeanDefinition 28 for (BeanMethod beanMethod : configClass.getBeanMethods()) { 29 loadBeanDefinitionsForBeanMethod(beanMethod); 30 } 31 32 // 处理该配置类上的@ImportResource指定的配置文件,就是Spring中常用的XML配置方式 33 // 这边会通过解析该XML文件给容器中注入BeanDefinition,SpringBoot中不推荐这种方式,具体就不往下分析了 34 loadBeanDefinitionsFromImportedResources(configClass.getImportedResources()); 35 36 // 处理步骤四中D解析@Import注解时获得的importBeanDefinitionRegistrars,调用其registerBeanDefinitions 37 // 方法给容器中注入BeanDefinition,这在前面讲解@Import方式时已经讲过了 38 loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()); 39} 40 41private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) { 42 registrars.forEach((registrar, metadata) -> registrar.registerBeanDefinitions(metadata, this.registry)); 43}

四、总结

至此本文对SpringBoot的配置类解析就基本讲完了,简单回顾一下:

  1. 第一部分介绍了SpringBoot解析配置类时会用到的一些类和注解,讲解了它们的使用及原理。
  2. 第二部分介绍了SpringBoot启动流程,引出了SpringBoot在哪一步解析配置类。
  3. 第三部分介绍了SpringBoot解析配置类的8大步骤。

更多内容敬请关注vivo 互联网技术微信公众号

注:转载文章请先与微信号:Labs2020联系。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )