【SpringBoot源码分析】Bean的加载过程
转自:https://www.cnblogs.com/lukama/p/14604894.html
-- 以下内容均基于2.1.8.RELEASE版本
在《SpringBoot启动过程的分析》系列文章中简要的对SpringBoot整体的启动流程作了梳理,但并未针对诸多细节进行分析。前面的篇章中介绍了从SpringBoot应用程序入口开始执行,一直到上下文刷新完成。期间它加载了所有的类,但是并未直接指出它是在哪个环节加载的类,在加载的过程中如何处理的,以及我们在程序入口所使用的各种注解是如何解析的。本文将对这一疑惑进行解答。
要分析SpringBoot加载类的过程,就必须清晰的知道我们的类到底在哪个环节被加载的。也就是需要定位到加载类的入口,如何来确定这个入口呢?通过阅读spring-framework的官方文档可以得知我们可以从ApplicationContext中来通过getBean()方法来获取Bean。那么通过这个入口就能找到存放Bean的地方,找到存放Bean的地方就可以通过调试得知它在什么时候被加载进来,进而确定Bean加载的入口。
找到Bean存放位置
这里通过一个简单示例来展示如何寻找Bean存放位置
1 public static void main(String[] args) { 2 SpringApplication application = new SpringApplication(Example.class); 3 ConfigurableApplicationContext context = application.run(args); 4 // 从容器中获取一个Bean 5 Example2 example2 = context.getBean(Example2.class); 6 7 }
上述代码是一个非常常见的获取Bean的代码,跟踪context.getBean()方法就能找到它存放的位置。
1 // AbstractApplicationContext.class 2 public <T> T getBean(Class<T> requiredType) throws BeansException { 3 assertBeanFactoryActive(); 4 // 从BeanFactory获取Bean 5 return getBeanFactory().getBean(requiredType); 6 } 7 8 // DefaultListableBeanFactory.class 9 public <T> T getBean(Class<T> requiredType) throws BeansException { 10 return getBean(requiredType, (Object[]) null); 11 } 12 13 public <T> T getBean(Class<T> requiredType, @Nullable Object... args) throws BeansException { 14 Assert.notNull(requiredType, "Required type must not be null"); 15 // 可以看到Object对象是这里获取的 16 Object resolved = resolveBean(ResolvableType.forRawClass(requiredType), args, false); 17 if (resolved == null) { 18 throw new NoSuchBeanDefinitionException(requiredType); 19 } 20 return (T) resolved; 21 } 22 23 // ...中间省略部分代码 24 25 private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) { 26 List<String> result = new ArrayList<>(); 27 28 // ① Check all bean definitions. 29 for (String beanName : this.beanDefinitionNames) { 30 // ...中间省略部分代码 31 } 32 return StringUtils.toStringArray(result); 33 }
① - 一路跟踪下来,可以看到所有的Bean都是在BeanFactory的beanDefinitionNames里面存放。因此关注这个属性何时被赋值就可以找到Bean加载的入口。
确定Bean在哪个环节被加载
当得知Bean存放于BeanFactory的beanDefinitionNames属性中,在启动阶段关注这个属性值的变化即可确定它在哪个阶段被赋值,可以肯定的是,它一定是在上下文容器创建完毕之后才会加载,因为容器都没有怎么存放。下图就展示了在创建完毕之后的上下文中Bean的初始化数量。
图: 创建完毕上下文容器
图中所展示的几个Bean是SpringBoot内置的处理器,在SpringBoot启动过程的分析-创建应用程序上下文一文中已经介绍过此处不再次解读。在创建完毕上下文之后有两个重要操作:预处理上下文、刷新上下文。那么初始化类必然就在这两个步骤中间了。首先在刷新上下文处打断点,看看在预处理上下文时是否初始化了其他的Bean。
图:预处理上下文完毕
查看刷新方法,它明显的调用了AbstractApplicationContext.refresh()。
1 // SpringApplication.class 2 3 private void refreshContext(ConfigurableApplicationContext context) { 4 refresh(context); 5 if (this.registerShutdownHook) { 6 try { 7 context.registerShutdownHook(); 8 } 9 catch (AccessControlException ex) { 10 // Not allowed in some environments. 11 } 12 } 13 } 14 15 protected void refresh(ApplicationContext applicationContext) { 16 Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext); 17 ((AbstractApplicationContext) applicationContext).refresh(); 18 }
此处可以发现它多了一个"Example"的类,单并未出现其他新的类,Example类是笔者调试程序的入口,在前面文章中也已经介绍过。因此可以断定,其他的类肯定在刷新上下文容器的时候被加载。快速确定方法就是在刷新上下文容器下方打断点,查看beanDefinitionNames的内容变化。在确定了是刷新容器时加载所有类之后,进入刷新容器的代码,可以看到它也清晰的划分了多个步骤,和上面一样,以每个方法为界,观察bean的加载情况。
在refresh()方法中,通过调式可以得知它在invokeBeanFactoryPostProcessors()方法会加载所有的Bean
1 public void refresh() throws BeansException, IllegalStateException { 2 synchronized (this.startupShutdownMonitor) { 3 4 // Prepare this context for refreshing. 5 prepareRefresh(); 6 7 // Tell the subclass to refresh the internal bean factory. 8 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); 9 10 // Prepare the bean factory for use in this context. 11 prepareBeanFactory(beanFactory); 12 13 try { 14 // Allows post-processing of the bean factory in context subclasses. 15 postProcessBeanFactory(beanFactory); 16 17 // Invoke factory processors registered as beans in the context. 18 invokeBeanFactoryPostProcessors(beanFactory); 19 20 // Register bean processors that intercept bean creation. 21 registerBeanPostProcessors(beanFactory); 22 23 // Initialize message source for this context. 24 initMessageSource(); 25 26 // Initialize event multicaster for this context. 27 initApplicationEventMulticaster(); 28 29 // Initialize other special beans in specific context subclasses. 30 onRefresh(); 31 32 // Check for listener beans and register them. 33 registerListeners(); 34 35 // Instantiate all remaining (non-lazy-init) singletons. 36 finishBeanFactoryInitialization(beanFactory); 37 38 // Last step: publish corresponding event. 39 finishRefresh(); 40 } 41 //...省略部分代码 42 } 43 }
通过BeanFactoryPostProcessor加载所有的类
提示:
下文中所提到的BeanFactoryPostProcessor,均表示类型为BeanFactoryPostProcessor的接口统称,包括了它的扩展接口BeanDefinitionRegistryPostProcessor以及他们的实现类。
invokeBeanFactoryPostProcessors()方法是加载并调用所有的BeanFactoryProcessor,我们在前面的文章中已经详细介绍了它的执行流程和业务细节,不明白的同学可以再去回顾一下SpringBoot启动过程的分析-刷新ApplicationContext。这里再重申一下有关于BeanFactoryPostProcessor的相关概念。加强了这些概念,后续对其他代码的理解也会更容易。
BeanFactoryPostProcessor,是Spring内部诸多PostProcessor中的一种,它是一个接口。注意它的前缀名称为BeanFactory,Bean工厂意味着它可以创建Bean,根据它的注释描述可以得知它可以修改Bean的定义,也可以修改Bean的属性值,但是它只能用于处理BeanDefinition,而不能处理Bean的实例。简单说就是,它可以在类实例化之前去修改它。另外一个BeanDefinitionRegistryPostProcessor它继承了BeanFactoryPostProcessor,对其进行了扩展,主要用于修改上下文中的Bean定义,加载所有常规的Bean,添加Bean。简单点理解就是它要比BeanFactoryPostProcessor更先执行。主要用于注册Bean。
在调用AbstractApplicationContext.invokeBeanFactoryPostProcessors()方法的时候需要注意它传入的BeanFactoryPostProcessor参数,具体可以看代码:
1 // AbstractApplicationContext.java 2 3 protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { 4 // ① 5 PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); 6 7 // ...省略部分代码 8 } 9 10 public List<BeanFactoryPostProcessor> getBeanFactoryPostProcessors() { 11 return this.beanFactoryPostProcessors; 12 }
① - 这里需要注意传入的参数,它由getBeanFactoryPostProcessors()方法提供,而这个方法返回的是AbstractApplicationContext.beanFactoryPostProcessor这个属性值。这个属性内部所定义的BeanFactoryPostProcessor都是在ApplicationContextInitializer的扩展中添加进来的,而创建上下文容器时添加的内部的处理器则存放在DefaultListableBeanFactory.beanDefinitionNames这个属性中。所以在处理BeanFactoryPostProcessor的时候首先处理的是ApplicationContextInitializer中的内容。也就意味着通过ApplicationContextInitializer添加的BeanFactoryPostProcessor将会优先执行。
通过两处不同的BeanFactoryPostProcessor可以反推出BeanFactoryPostProcessor初始化的两种方式:
- 通过ConfigurableApplicationContext.addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor)来添加
- 通过BeanDefinitionRegistry.registerBeanDefinition(String beanName, BeanDefinition beanDefinition)来注册一个Bean
本文的重点是分析Bean加载,此处对于BeanFactoryPostProcessor的植入涉及到另外一个课题:Spring框架的扩展点。对扩展点的分析此处先占位,有兴趣可以查看SpringFramework的扩展点
实际上对于Bean加载不光包括我们自己编写的业务代码,也包括SpringBoot自己的其他组件。因为BeanFactoryPostProcessor本身也是一个类。他们在ApplicationContextInitializer接口中被添加,或是在BeanDefinitionRegistry中被注册。区别就是一个是直接添加实例,一个是注册BeanDefinition。言归正传回到Bean的加载中来;此处对于通过ApplicationContextInitializer接口中被添加的BeanFactoryPostProcessor不作分析,因为代码比较简洁,他们本身也没涉及过多的操作,感兴趣的可以自己debug。这里重点分析通过BeanDefinitionRegistry注册的BeanFactoryPostProcessor即DefaultListableBeanFactory.beanDefinitionNames这个属性中注册的类。内置的BeanFactoryPostProcessor是在容器创建的时候加入的,可参考之前的分析注册内定的BeanFactoryPostProcessor。
org.springframework.context.annotation.ConfigurationClassPostProcessor
org.springframework.context.event.DefaultEventListenerFactory
org.springframework.context.event.EventListenerMethodProcessor
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
经过查看这些内置的类,只有ConfigurationClassPostProcessor和类加载相关。
小结
前面章节部分通过一种简要(笨拙)的方式来定位到我们需要分析的代码点,在**容器刷新阶段**中的**invokeBeanFactoryPostProcessors()**方法中由调用BeanFactoryPostProcessor接口开始,执行了具有配置解析、BeanDefinition加载的实现类。通过对BeanFactoryPostProcessor接口的实现进行分析结合源码执行流程 | |
得知它的执行顺序。这一点尤其重要,它是后面整个流程的入口。 |
ConfigurationClassPostProcessor
主要用于对@Configuration注解进行处理。在SpringBoot应用中,往往都是从一个Main函数开始,启动类上面也必须使用@SpringBootApplication注解来标明它的身份;因此@Configuration注解也代表着应用的起点;因在BeanFactoryPostProcessor被调用的时候是按照优先级来的,首先被调用的是BeanDefinitionRegistryPostProcessor,所以首先执行postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)方法。
1 // ConfigurationClassPostProcessor.java 2 3 public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { 4 // ① 5 int registryId = System.identityHashCode(registry); 6 if (this.registriesPostProcessed.contains(registryId)) { 7 throw new IllegalStateException("postProcessBeanDefinitionRegistry already called on this post-processor against " + registry); 8 } 9 // ② 10 if (this.factoriesPostProcessed.contains(registryId)) { 11 throw new IllegalStateException("postProcessBeanFactory already called on this post-processor against " + registry); 12 } 13 // ③ 14 this.registriesPostProcessed.add(registryId); 15 // ④ 16 processConfigBeanDefinitions(registry); 17 }
① - 获取注册ID
② - 判断当前Processor是否被执行过
③ - 添加到已处理列表
④ - 处理配置相关的BeanDefinition
1 public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { 2 3 List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); 4 // 获取所有已注册的BeanDefinition,寻找具有@Configuration注解的类 5 String[] candidateNames = registry.getBeanDefinitionNames(); 6 7 // 遍历所有的beanName 8 for (String beanName : candidateNames) { 9 // 根据名称找到BeanDefinition 10 BeanDefinition beanDef = registry.getBeanDefinition(beanName); 11 // 判断当前类是Full模式还是Lite模式,就为了打印个日志?No,查看日志内容,打印的是:当前类已经被当做一个configuration类被处理过。 12 // 那就意味着默认情况下它是没有设置ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE 这个属性的。 13 if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) || ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) { 14 if (logger.isDebugEnabled()) { 15 logger.debug("Bean definition has already been processed as a configuration class: " + beanDef); 16 } 17 } 18 // 判断当前处理的类是不是完整的配置类,也就是是否被@Configuration注解修饰,若被修饰则会给当前的BeanDefinition设置一个attribute(org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass = full) 19 else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { 20 // 加入配置候选列表 21 configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); 22 } 23 } 24 25 // 若没有被@Configuration注解的类,直接返回 26 // Return immediately if no @Configuration classes were found 27 if (configCandidates.isEmpty()) { 28 return; 29 } 30 31 // 根据优先级排序 32 // Sort by previously determined @Order value, if applicable 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 // 设置BeanNameGenerator用于生成稍后检测到的Bean的名称 40 // Detect any custom bean name generation strategy supplied through the enclosing application context 41 SingletonBeanRegistry sbr = null; 42 if (registry instanceof SingletonBeanRegistry) { 43 sbr = (SingletonBeanRegistry) registry; 44 if (!this.localBeanNameGeneratorSet) { 45 BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR); 46 if (generator != null) { 47 this.componentScanBeanNameGenerator = generator; 48 this.importBeanNameGenerator = generator; 49 } 50 } 51 } 52 53 if (this.environment == null) { 54 this.environment = new StandardEnvironment(); 55 } 56 57 // 新建一个配置类解析器,用于解析所有@Configuration注解的类 58 // Parse each @Configuration class 59 ConfigurationClassParser parser = new ConfigurationClassParser( 60 this.metadataReaderFactory, this.problemReporter, this.environment, 61 this.resourceLoader, this.componentScanBeanNameGenerator, registry); 62 63 // 将要被解析的配置类 64 Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); 65 // 已经被解析的配置类 66 Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); 67 do { 68 // ① 开始解析配置类 69 parser.parse(candidates); 70 parser.validate(); 71 72 // ② 获取解析到的配置类 73 Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); 74 configClasses.removeAll(alreadyParsed); 75 76 // ③ 构建Reader对象,为加载Bean做准备 Read the model and create bean definitions based on its content 77 if (this.reader == null) { 78 this.reader = new ConfigurationClassBeanDefinitionReader( 79 registry, this.sourceExtractor, this.resourceLoader, this.environment, 80 this.importBeanNameGenerator, parser.getImportRegistry()); 81 } 82 83 // ④ 从配置类开始加载Bean 84 this.reader.loadBeanDefinitions(configClasses); 85 alreadyParsed.addAll(configClasses); 86 87 candidates.clear(); 88 if (registry.getBeanDefinitionCount() > candidateNames.length) { 89 String[] newCandidateNames = registry.getBeanDefinitionNames(); 90 Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames)); 91 Set<String> alreadyParsedClasses = new HashSet<>(); 92 for (ConfigurationClass configurationClass : alreadyParsed) { 93 alreadyParsedClasses.add(configurationClass.getMetadata().getClassName()); 94 } 95 for (String candidateName : newCandidateNames) { 96 if (!oldCandidateNames.contains(candidateName)) { 97 BeanDefinition bd = registry.getBeanDefinition(candidateName); 98 if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && 99 !alreadyParsedClasses.contains(bd.getBeanClassName())) { 100 candidates.add(new BeanDefinitionHolder(bd, candidateName)); 101 } 102 } 103 } 104 candidateNames = newCandidateNames; 105 } 106 } 107 while (!candidates.isEmpty()); 108 109 // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes 110 if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) { 111 sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry()); 112 } 113 114 if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) { 115 // Clear cache in externally provided MetadataReaderFactory; this is a no-op 116 // for a shared cache since it'll be cleared by the ApplicationContext. 117 ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache(); 118 } 119 }
根据上面的void processConfigBeanDefinitions(BeanDefinitionRegistry registry)方法中的do-while循环可以看到,它内部是按照解析@Configuration->取出@Configuration->构建BeanDefinitionReader->从@Configuration读取BeanDefinition的流程来完成类的加载。
开始解析@Configuration类
@Configuration注解是Spring中基于Java配置容器中的一个注解,属于类级别的注解,它主要用于标明一组@Bean定义的来源。通常@Configuration和@Bean同时使用。
1 // ConfigurationClassParser.java 2 3 public void parse(Set<BeanDefinitionHolder> configCandidates) { 4 // 遍历所有具有@Configuration注解的类 5 for (BeanDefinitionHolder holder : configCandidates) { 6 BeanDefinition bd = holder.getBeanDefinition(); 7 try { 8 if (bd instanceof AnnotatedBeanDefinition) { 9 // 解析AnnotatedBeanDefinition类型的配置类(可以获取类的注解元数据) 10 parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName()); 11 } 12 else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) { 13 parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName()); 14 } 15 else { 16 parse(bd.getBeanClassName(), holder.getBeanName()); 17 } 18 } 19 catch (BeanDefinitionStoreException ex) { 20 throw ex; 21 } 22 catch (Throwable ex) { 23 throw new BeanDefinitionStoreException( 24 "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex); 25 } 26 } 27 28 this.deferredImportSelectorHandler.process(); 29 } 30 31 // 包装为ConfigurationClass对象 32 protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException { 33 processConfigurationClass(new ConfigurationClass(metadata, beanName)); 34 } 35 36 // 判断是否已经执行过 37 protected void processConfigurationClass(ConfigurationClass configClass) throws IOException { 38 if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { 39 return; 40 } 41 42 ConfigurationClass existingClass = this.configurationClasses.get(configClass); 43 if (existingClass != null) { 44 if (configClass.isImported()) { 45 if (existingClass.isImported()) { 46 existingClass.mergeImportedBy(configClass); 47 } 48 // Otherwise ignore new imported config class; existing non-imported class overrides it. 49 return; 50 } 51 else { 52 // Explicit bean definition found, probably replacing an import. 53 // Let's remove the old one and go with the new one. 54 this.configurationClasses.remove(configClass); 55 this.knownSuperclasses.values().removeIf(configClass::equals); 56 } 57 } 58 59 // 包装为SourceClass便于统一处理 60 // Recursively process the configuration class and its superclass hierarchy. 61 SourceClass sourceClass = asSourceClass(configClass); 62 do { 63 // 这里才开始处理配置类 64 sourceClass = doProcessConfigurationClass(configClass, sourceClass); 65 } 66 while (sourceClass != null); 67 68 this.configurationClasses.put(configClass, configClass); 69 } 70 71 // 真正开始处理配置的方法 72 protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) 73 throws IOException { 74 75 // 优先处理@Component注解 76 if (configClass.getMetadata().isAnnotated(Component.class.getName())) { 77 // 递归处理 78 // Recursively process any member (nested) classes first 79 processMemberClasses(configClass, sourceClass); 80 } 81 82 // 处理@PropertySource注解 83 // Process any @PropertySource annotations 84 for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( 85 sourceClass.getMetadata(), PropertySources.class, 86 org.springframework.context.annotation.PropertySource.class)) { 87 if (this.environment instanceof ConfigurableEnvironment) { 88 processPropertySource(propertySource); 89 } 90 else { 91 logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + 92 "]. Reason: Environment must implement ConfigurableEnvironment"); 93 } 94 } 95 96 // 处理@ComponentScan注解 97 // Process any @ComponentScan annotations 98 Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( 99 sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); 100 if (!componentScans.isEmpty() && 101 !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { 102 for (AnnotationAttributes componentScan : componentScans) { 103 // The config class is annotated with @ComponentScan -> perform the scan immediately 104 Set<BeanDefinitionHolder> scannedBeanDefinitions = 105 this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); 106 // Check the set of scanned definitions for any further config classes and parse recursively if needed 107 for (BeanDefinitionHolder holder : scannedBeanDefinitions) { 108 BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); 109 if (bdCand == null) { 110 bdCand = holder.getBeanDefinition(); 111 } 112 if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { 113 parse(bdCand.getBeanClassName(), holder.getBeanName()); 114 } 115 } 116 } 117 } 118 119 // 处理@Import注解 120 // Process any @Import annotations 121 processImports(configClass, sourceClass, getImports(sourceClass), true); 122 123 // 处理@ImportResource注解 124 // Process any @ImportResource annotations 125 AnnotationAttributes importResource = 126 AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); 127 if (importResource != null) { 128 String[] resources = importResource.getStringArray("locations"); 129 Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader"); 130 for (String resource : resources) { 131 String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); 132 configClass.addImportedResource(resolvedResource, readerClass); 133 } 134 } 135 136 // 处理单个@Bean方法 137 // Process individual @Bean methods 138 Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass); 139 for (MethodMetadata methodMetadata : beanMethods) { 140 configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); 141 } 142 143 // 处理接口默认方法 144 // Process default methods on interfaces 145 processInterfaces(configClass, sourceClass); 146 147 // 处理父类的方法 148 // Process superclass, if any 149 if (sourceClass.getMetadata().hasSuperClass()) { 150 String superclass = sourceClass.getMetadata().getSuperClassName(); 151 if (superclass != null && !superclass.startsWith("java") && 152 !this.knownSuperclasses.containsKey(superclass)) { 153 this.knownSuperclasses.put(superclass, configClass); 154 // Superclass found, return its annotation metadata and recurse 155 return sourceClass.getSuperClass(); 156 } 157 } 158 159 // No superclass -> processing is complete 160 return null; 161 }
@Configuration配置类同时也和其他注解有关联,这里说的关联是其他注解的行为会影响配置类本身的状态。例如方法中提到的@Component、@ComponentScan、@PropertySource、@Import、@Bean、@ImportResource。
处理@Component注解
@Component注解表示被修饰的类将会被识别为受Spring管理的类。将会被注册到Bean容器中。
1 private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { 2 // 获取成员类 3 Collection<SourceClass> memberClasses = sourceClass.getMemberClasses(); 4 if (!memberClasses.isEmpty()) { 5 List<SourceClass> candidates = new ArrayList<>(memberClasses.size()); 6 for (SourceClass memberClass : memberClasses) { 7 // 判断是否有@Configuration或者@Component注解 8 if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) && !memberClass.getMetadata().getC 9 lassName().equals(configClass.getMetadata().getClassName())) { 10 candidates.add(memberClass); 11 } 12 } 13 // 排序 14 OrderComparator.sort(candidates); 15 // 检测是否有循环导入(@Import)的问题 16 for (SourceClass candidate : candidates) { 17 if (this.importStack.contains(configClass)) { 18 this.problemReporter.error(new CircularImportProblem(configClass, this.importStack)); 19 } 20 else { 21 this.importStack.push(configClass); 22 try { 23 // 递归调用处理 24 processConfigurationClass(candidate.asConfigClass(configClass)); 25 } 26 finally { 27 this.importStack.pop(); 28 } 29 } 30 } 31 } 32 }
在该方法中,主要用于处理当前传入的configClass内部的嵌套类、成员类中是否有@Configuration、@Component注解。一般来说@Configuratio
n都是单独使用的一个类。
处理@PropertySource注解
@PropertySource注解用于加载指定的properties配置文件到Spring的Environment中
1 // 内部实现比较简单,自行debug即可 2 for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) { 3 // 判断当前环境对象是否是可配置的 4 if (this.environment instanceof ConfigurableEnvironment) { 5 processPropertySource(propertySource); 6 } 7 else { 8 logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment"); 9 } 10 }
处理@ComponentScan注解
@ComponentScan注解用于指明当前应用将扫描哪些包下的具有@Component注解的类。这个注解必须添加到@Configuration类中
1 // 根据ComponentScans配置的包路径查找带@Component注解的类 2 Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); 3 if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { 4 // 获取所有带@Component注解的类 5 for (AnnotationAttributes componentScan : componentScans) { 6 // The config class is annotated with @ComponentScan -> perform the scan immediately 7 Set<BeanDefinitionHolder> scannedBeanDefinitions = 8 this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); 9 // Check the set of scanned definitions for any further config classes and parse recursively if needed 10 for (BeanDefinitionHolder holder : scannedBeanDefinitions) { 11 BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); 12 if (bdCand == null) { 13 bdCand = holder.getBeanDefinition(); 14 } 15 if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { 16 parse(bdCand.getBeanClassName(), holder.getBeanName()); 17 } 18 } 19 } 20 }
这里重点分析它是如何查找@ComponentScans注解的,跳过无用的调用链,查看真正开始查找的方法。
1 // AnnotationConfigUtils.java 2 // 需要注意的是参数:metadata代表从哪里获取,containerClassName表示包含注解(例如@ComponentScans,它本身的值可以包含多个@ComponentScan),annotationClassName是当前要获取的目标注解也就是@ComponentScan 3 static Set<AnnotationAttributes> attributesForRepeatable(AnnotationMetadata metadata, String containerClassName, String annotationClassName) { 4 5 Set<AnnotationAttributes> result = new LinkedHashSet<>(); 6 7 // ① 8 // Direct annotation present? 9 addAttributesIfNotNull(result, metadata.getAnnotationAttributes(annotationClassName, false)); 10 11 // ② 12 // Container annotation present? 13 Map<String, Object> container = metadata.getAnnotationAttributes(containerClassName, false); 14 if (container != null && container.containsKey("value")) { 15 for (Map<String, Object> containedAttributes : (Map<String, Object>[]) container.get("value")) { 16 addAttributesIfNotNull(result, containedAttributes); 17 } 18 } 19 20 // Return merged result 21 return Collections.unmodifiableSet(result); 22 }
提示:
在这个方法内部分了两个操作,第一个是直接获取@ComponentScan这个注解,第二个是获取@ComponentScans注解。这两者是有区别的。
① - 此处为@ComponentScan,称之为直接注解”,即在类上面直接声明的注解。
1 // StandardAnnotationMetadata.class 2 public Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString) { 3 return (this.annotations.length > 0 ? AnnotatedElementUtils.getMergedAnnotationAttributes(getIntrospectedClass(), annotationName, classValuesAsString, this.nestedAnnotationsAsMap) : null); 4 } 5 6 // AnnotatedElementUtils.class 7 public static AnnotationAttributes getMergedAnnotationAttributes(AnnotatedElement element, 8 String annotationName, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { 9 10 AnnotationAttributes attributes = searchWithGetSemantics(element, null, annotationName, new MergedAnnotationAttributesProcessor(classValuesAsString, nestedAnnotationsAsMap)); 11 AnnotationUtils.postProcessAnnotationAttributes(element, attributes, classValuesAsString, nestedAnnotationsAsMap); 12 return attributes; 13 } 14 15 // 通过一系列的重载方法,最终调用此方法 16 private static <T> T searchWithGetSemantics(AnnotatedElement element, 17 Set<Class<? extends Annotation>> annotationTypes, @Nullable String annotationName, 18 @Nullable Class<? extends Annotation> containerType, Processor<T> processor, 19 Set<AnnotatedElement> visited, int metaDepth) { 20 21 if (visited.add(element)) { 22 try { 23 // Start searching within locally declared annotations 24 // 获取当前元素声明的所有注解 25 List<Annotation> declaredAnnotations = Arrays.asList(AnnotationUtils.getDeclaredAnnotations(element)); 26 // 获取这些注解中指定的类型 27 T result = searchWithGetSemanticsInAnnotations(element, declaredAnnotations, annotationTypes, annotationName, containerType, processor, visited, metaDepth); 28 if (result != null) { 29 return result; 30 } 31 32 if (element instanceof Class) { // otherwise getAnnotations doesn't return anything new 33 Class<?> superclass = ((Class<?>) element).getSuperclass(); 34 if (superclass != null && superclass != Object.class) { 35 List<Annotation> inheritedAnnotations = new LinkedList<>(); 36 for (Annotation annotation : element.getAnnotations()) { 37 if (!declaredAnnotations.contains(annotation)) { 38 inheritedAnnotations.add(annotation); 39 } 40 } 41 // Continue searching within inherited annotations 42 result = searchWithGetSemanticsInAnnotations(element, inheritedAnnotations, 43 annotationTypes, annotationName, containerType, processor, visited, metaDepth); 44 if (result != null) { 45 return result; 46 } 47 } 48 } 49 } 50 catch (Throwable ex) { 51 AnnotationUtils.handleIntrospectionFailure(element, ex); 52 } 53 } 54 55 return null; 56 } 57 58 private static <T> T searchWithGetSemanticsInAnnotations(@Nullable AnnotatedElement element, 59 List<Annotation> annotations, Set<Class<? extends Annotation>> annotationTypes, 60 @Nullable String annotationName, @Nullable Class<? extends Annotation> containerType, 61 Processor<T> processor, Set<AnnotatedElement> visited, int metaDepth) { 62 63 // 遍历所有注解 64 // Search in annotations 65 for (Annotation annotation : annotations) { 66 // 获取当前注解类型 67 Class<? extends Annotation> currentAnnotationType = annotation.annotationType(); 68 if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) { 69 // 判断是否处理过、当前注解的名称是否和将要查找的一致、是否默认处理(默认为true,请debug的时候注意传入的参数) 70 if (annotationTypes.contains(currentAnnotationType) || currentAnnotationType.getName().equals(annotationName) || processor.alwaysProcesses()) { 71 // 若匹配,则查找内部的注解属性 72 T result = processor.process(element, annotation, metaDepth); 73 if (result != null) { 74 if (processor.aggregates() && metaDepth == 0) { 75 processor.getAggregatedResults().add(result); 76 } 77 else { 78 return result; 79 } 80 } 81 } 82 // 如果没有找到 83 // Repeatable annotations in container? 84 else if (currentAnnotationType == containerType) { 85 for (Annotation contained : getRawAnnotationsFromContainer(element, annotation)) { 86 T result = processor.process(element, contained, metaDepth); 87 if (result != null) { 88 // No need to post-process since repeatable annotations within a 89 // container cannot be composed annotations. 90 processor.getAggregatedResults().add(result); 91 } 92 } 93 } 94 } 95 } 96 97 // Recursively search in meta-annotations 98 for (Annotation annotation : annotations) { 99 Class<? extends Annotation> currentAnnotationType = annotation.annotationType(); 100 if (!AnnotationUtils.hasPlainJavaAnnotationsOnly(currentAnnotationType)) { 101 T result = searchWithGetSemantics(currentAnnotationType, annotationTypes, annotationName, containerType, processor, visited, metaDepth + 1); 102 if (result != null) { 103 processor.postProcess(element, annotation, result); 104 if (processor.aggregates() && metaDepth == 0) { 105 processor.getAggregatedResults().add(result); 106 } 107 else { 108 return result; 109 } 110 } 111 } 112 } 113 114 return null; 115 } 116 117 // AnnotatedElementUtils.class内部类AnnotatedElementUtils.class 118 public AnnotationAttributes process(@Nullable AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) { 119 return AnnotationUtils.retrieveAnnotationAttributes(annotatedElement, annotation, this.classValuesAsString, this.nestedAnnotationsAsMap); 120 } 121 122 // AnnotationUtils.class 123 static AnnotationAttributes retrieveAnnotationAttributes(@Nullable Object annotatedElement, 124 Annotation annotation, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { 125 126 Class<? extends Annotation> annotationType = annotation.annotationType(); 127 AnnotationAttributes attributes = new AnnotationAttributes(annotationType); 128 129 // 遍历注解的方法,获取属性名称和属性值,填充至AnnotationAttributes对象内部(实际上就是个Map) 130 for (Method method : getAttributeMethods(annotationType)) { 131 try { 132 Object attributeValue = method.invoke(annotation); 133 Object defaultValue = method.getDefaultValue(); 134 if (defaultValue != null && ObjectUtils.nullSafeEquals(attributeValue, defaultValue)) { 135 attributeValue = new DefaultValueHolder(defaultValue); 136 } 137 attributes.put(method.getName(), adaptValue(annotatedElement, attributeValue, classValuesAsString, nestedAnnotationsAsMap)); 138 } 139 catch (Throwable ex) { 140 if (ex instanceof InvocationTargetException) { 141 Throwable targetException = ((InvocationTargetException) ex).getTargetException(); 142 rethrowAnnotationConfigurationException(targetException); 143 } 144 throw new IllegalStateException("Could not obtain annotation attribute value for " + method, ex); 145 } 146 } 147 148 return attributes; 149 }
② - 此处为@ComponentScans,称之为”容器注解”,它可以包含多个@ComponentScan,代码逻辑和获取直接注解@ComponentScan并没有太大差异,可自行debug
看到这里可以得出一个结论:有3种使用注解定义包扫描路径的方法
- @SpringBootApplication(scanBasePackages = "com.example")
- @ComponentScan(value = {"com.example"})
- @ComponentScans({@ComponentScan("com.abcd"), @ComponentScan("com.efgh")})
注意:其中1和2是可以同时存在,1和3可以同时存在,2和3不可以同时存在
1、2同时存在只取2的值
1、3同时存在取1、3的值
处理到这一步,仅仅是获取到了所有的@ComponentScan注解,接下来还需要解析注解里面配置的包路径
1 // ConfigurationClassParser.class 284行 2 for (AnnotationAttributes componentScan : componentScans) { 3 // 解析当前注解配置的包路径下的类 4 // The config class is annotated with @ComponentScan -> perform the scan immediately 5 Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); 6 // 检查返回的BeanDefinition里面是否有其他的配置类,如果需要的话会递归进行解析 7 // Check the set of scanned definitions for any further config classes and parse recursively if needed 8 for (BeanDefinitionHolder holder : scannedBeanDefinitions) { 9 BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); 10 if (bdCand == null) { 11 bdCand = holder.getBeanDefinition(); 12 } 13 // 若是配置类,执行配置类的解析操作 14 if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { 15 parse(bdCand.getBeanClassName(), holder.getBeanName()); 16 } 17 } 18 } 19 20 // 以下代码为获取BeanDefinition的具体流程 21 // ComponentScanAnnotationParser.class 22 public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) { 23 24 // 构建扫描器 25 ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry, componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader); 26 27 // 构建类名生成器 28 Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator"); 29 boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass); 30 scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator : BeanUtils.instantiateClass(generatorClass)); 31 32 // 获取类的作用域模型 33 ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy"); 34 if (scopedProxyMode != ScopedProxyMode.DEFAULT) { 35 scanner.setScopedProxyMode(scopedProxyMode); 36 } 37 else { 38 Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver"); 39 scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass)); 40 } 41 42 // 获取资源模式(注解配置扫描包都是扫描class因此此处为:**/*.class) 43 scanner.setResourcePattern(componentScan.getString("resourcePattern")); 44 45 // 获取@ComponentScan注解里面配置的include过滤器 46 for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) { 47 for (TypeFilter typeFilter : typeFiltersFor(filter)) { 48 scanner.addIncludeFilter(typeFilter); 49 } 50 } 51 // 获取@ComponentScan注解里面配置的exclude过滤器 52 for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) { 53 for (TypeFilter typeFilter : typeFiltersFor(filter)) { 54 scanner.addExcludeFilter(typeFilter); 55 } 56 } 57 58 // 获取@ComponentScan注解里面配置的lazyInit值 59 boolean lazyInit = componentScan.getBoolean("lazyInit"); 60 if (lazyInit) { 61 scanner.getBeanDefinitionDefaults().setLazyInit(true); 62 } 63 64 // 获取需要扫描的路径集合 65 Set<String> basePackages = new LinkedHashSet<>(); 66 String[] basePackagesArray = componentScan.getStringArray("basePackages"); 67 for (String pkg : basePackagesArray) { 68 String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg), ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); 69 Collections.addAll(basePackages, tokenized); 70 } 71 for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) { 72 basePackages.add(ClassUtils.getPackageName(clazz)); 73 } 74 if (basePackages.isEmpty()) { 75 basePackages.add(ClassUtils.getPackageName(declaringClass)); 76 } 77 78 scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) { 79 @Override 80 protected boolean matchClassName(String className) { 81 return declaringClass.equals(className); 82 } 83 }); 84 // 通过扫描器扫描指定路径下的具有@Component注解的类 85 return scanner.doScan(StringUtils.toStringArray(basePackages)); 86 } 87 88 // ClassPathBeanDefinitionScanner.class 89 90 protected Set<BeanDefinitionHolder> doScan(String... basePackages) { 91 Assert.notEmpty(basePackages, "At least one base package must be specified"); 92 Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>(); 93 for (String basePackage : basePackages) { 94 // 查找所有@Component注解标识的类 95 Set<BeanDefinition> candidates = findCandidateComponents(basePackage); 96 for (BeanDefinition candidate : candidates) { 97 // 获取作用域元数据 98 ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); 99 candidate.setScope(scopeMetadata.getScopeName()); 100 // 生成类名 101 String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); 102 // 对普通的Bean 进行处理 103 if (candidate instanceof AbstractBeanDefinition) { 104 postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); 105 } 106 // 对包含有注解的Bean进行处理,比如@Lazy、@Primary 107 if (candidate instanceof AnnotatedBeanDefinition) { 108 AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); 109 } 110 // 检查当前加载的Bean名称是否有冲突 111 if (checkCandidate(beanName, candidate)) { 112 BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); 113 definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); 114 beanDefinitions.add(definitionHolder); 115 registerBeanDefinition(definitionHolder, this.registry); 116 } 117 } 118 } 119 return beanDefinitions; 120 } 121 122 // ClassPathScanningCandidateComponentProvider.class 123 124 public Set<BeanDefinition> findCandidateComponents(String basePackage) { 125 if (this.componentsIndex != null && indexSupportsIncludeFilters()) { 126 return addCandidateComponentsFromIndex(this.componentsIndex, basePackage); 127 } 128 else { 129 // 真正开始搜索类的方法 130 return scanCandidateComponents(basePackage); 131 } 132 } 133 134 private Set<BeanDefinition> scanCandidateComponents(String basePackage) { 135 Set<BeanDefinition> candidates = new LinkedHashSet<>(); 136 try { 137 // 获取资源的路径 138 String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + this.resourcePattern; 139 // 获取所有资源 140 Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); 141 boolean traceEnabled = logger.isTraceEnabled(); 142 boolean debugEnabled = logger.isDebugEnabled(); 143 // 为每一个类生成BeanDefinition对象 144 for (Resource resource : resources) { 145 if (traceEnabled) { 146 logger.trace("Scanning " + resource); 147 } 148 if (resource.isReadable()) { 149 try { 150 MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); 151 // 判断是否具有@Component注解 152 if (isCandidateComponent(metadataReader)) { 153 ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); 154 sbd.setResource(resource); 155 sbd.setSource(resource); 156 if (isCandidateComponent(sbd)) { 157 if (debugEnabled) { 158 logger.debug("Identified candidate component class: " + resource); 159 } 160 candidates.add(sbd); 161 } 162 else { 163 if (debugEnabled) { 164 logger.debug("Ignored because not a concrete top-level class: " + resource); 165 } 166 } 167 } 168 else { 169 if (traceEnabled) { 170 logger.trace("Ignored because not matching any filter: " + resource); 171 } 172 } 173 } 174 catch (Throwable ex) { 175 throw new BeanDefinitionStoreException( 176 "Failed to read candidate component class: " + resource, ex); 177 } 178 } 179 else { 180 if (traceEnabled) { 181 logger.trace("Ignored because not readable: " + resource); 182 } 183 } 184 } 185 } 186 catch (IOException ex) { 187 throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex); 188 } 189 return candidates; 190 }
至此,通过@ComponentScan注解配置扫描指定的包,到获取到具体的带有@Component注解的类已经全部获取完毕。
处理@Import注解
@Import注解主要用于引入另外一个@Configuration。和Spring XML配置文件中的标签功能一样。
1 // ConfigurationClassParser.class 302行 2 // 处理@Import注解,请注意参数内部的getImports方法 3 processImports(configClass, sourceClass, getImports(sourceClass), true); 4 5 private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException { 6 Set<SourceClass> imports = new LinkedHashSet<>(); 7 Set<SourceClass> visited = new LinkedHashSet<>(); 8 collectImports(sourceClass, imports, visited); 9 return imports; 10 } 11 12 // 递归获取@Import注解 13 private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited) throws IOException { 14 // 添加已处理的避免无限递归 15 if (visited.add(sourceClass)) { 16 // 获取传入资源上的注解列表 17 for (SourceClass annotation : sourceClass.getAnnotations()) { 18 // 获取注解名称 19 String annName = annotation.getMetadata().getClassName(); 20 // 若当前注解不是@Import则递归查找 21 if (!annName.equals(Import.class.getName())) { 22 collectImports(annotation, imports, visited); 23 } 24 } 25 // 添加已经获取的所有注解 26 imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value")); 27 } 28 } 29 30 31 private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, boolean checkForCircularImports) { 32 33 if (importCandidates.isEmpty()) { 34 return; 35 } 36 37 if (checkForCircularImports && isChainedImportOnStack(configClass)) { 38 this.problemReporter.error(new CircularImportProblem(configClass, this.importStack)); 39 } 40 else { 41 this.importStack.push(configClass); 42 try { 43 for (SourceClass candidate : importCandidates) { 44 // 是否是ImportSelector的实现 45 if (candidate.isAssignable(ImportSelector.class)) { 46 // 获取SourceClass对象中所代表的真正对象,存储于SourceClass.source属性中 47 // Candidate class is an ImportSelector -> delegate to it to determine imports 48 Class<?> candidateClass = candidate.loadClass(); 49 // 获取ImportSelector实例 50 ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class); 51 // 调用Aware接口 52 ParserStrategyUtils.invokeAwareMethods(selector, this.environment, this.resourceLoader, this.registry); 53 // ① 54 if (selector instanceof DeferredImportSelector) { 55 // 1.1 56 this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector); 57 } else { 58 // 获取导入的类名称数组 59 String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); 60 // 将其转换为SourceClass, 目的是为了下一步的递归获取 61 Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames); 62 // 递归调用 63 processImports(configClass, currentSourceClass, importSourceClasses, false); 64 // ② 65 } 66 } 67 // ③ 68 else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) { 69 // Candidate class is an ImportBeanDefinitionRegistrar -> 70 // delegate to it to register additional bean definitions 71 // 获取SourceClass对象中所代表的真正对象,存储于SourceClass.source属性中 72 Class<?> candidateClass = candidate.loadClass(); 73 // 获取ImportBeanDefinitionRegistrar实例 74 ImportBeanDefinitionRegistrar registrar = BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class); 75 // 调用Aware接口 76 ParserStrategyUtils.invokeAwareMethods(registrar, this.environment, this.resourceLoader, this.registry); 77 // 将其存放于configClass的importBeanDefinitionRegistrars属性中 78 configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata()); 79 // 3.1 80 } 81 // 都不是的话把它当做@Configuration来处理 82 else { 83 // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar -> 84 // process it as an @Configuration class 85 this.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); 86 // 将其作为@Configuration处理 87 processConfigurationClass(candidate.asConfigClass(configClass)); 88 } 89 } 90 } 91 catch (BeanDefinitionStoreException ex) { 92 throw ex; 93 } 94 catch (Throwable ex) { 95 throw new BeanDefinitionStoreException( 96 "Failed to process import candidates for configuration class [" + configClass.getMetadata().getClassName() + "]", ex); 97 } 98 finally { 99 this.importStack.pop(); 100 } 101 } 102 }
提示:
ImporSelector主要用于导入@Configuration配置类,并且可以实现EnvironmentAware、BeanFactoryAware、BeanClassLoaderAware、ResourceLoaderAware接口,并在调用ImportSelector.selectImports方法之前调用它们。
ImportBeanDefinitionRegistrar主要用于导入导入@Configuration配置,并可以注册BeanDefinition
① - 是否是DeferredImportSelector, 它将会在所有的@Configuration执行完毕之后才执行。
1.1 将其放入DeferredImportSelectorHandler对象的deferredImportSelectors属性中,在当前方法内部直至执行完毕都未见这些DeferredImportSelector被执行,它是在哪里被执行的呢?向上翻到"开始解析@Configuration类"章节,查看parse()方法的最后一行,它是在所有的解析操作完成之后被执行。
② - 这里并未执行任何操作,仅仅是继续递归,因为当前条件分支处理的是ImportSelector,它的作用就是导入配置,因此继续递归查看是否有内嵌的其他配置,最终将会把配置类缓存,在解析完毕之后一并处理。
③ - 是否是ImportBeanDefinitionRegistrar的实现
3.1 此处并未执行获取的selector,仅仅是将其存放于当前解析的配置对象(ConfigClass)的importBeanDefinitionRegistrars属性中,那么它在哪里被执行呢?因其具有注册BeanDefinition的功能,所以此处并未处理,和DeferredImportSelector一样,此处也是先解析出来,等到该执行的时候再执行。
当前处理逻辑处于parse()方法中,对解析结果的调用在parse()方法的后面,因此可以查阅@Configuration读取BeanDefinition章节。
对于@Import的解析可以做一个简要的总结:
它用于导入配置类,配置类又分为可以延迟加载的、可以注册BeanDefinition的,无一例外他们都是在@Configuration类获取完毕之后才会被执行。需要注意的是解析配置类的对象是ConfigurationClassParser,它内部缓存了当前解析但并未被执行的类。在其执行完毕所有的解析动作之后会依次调用DeferredImportSelector和ImportBeanDefinitionRegistrar。
处理@ImportResource注解
用于引入Spring xml配置文件,类似于Spring XML中的标签,默认使用XmlBeanDefinitionReader来解析XML中的标签。
例如:
1 @Configuration 2 @ImportResource("classpath:/com/acme/properties-config.xml") 3 public class AppConfig { 4 5 @Value("${jdbc.url}") 6 private String url; 7 8 @Value("${jdbc.username}") 9 private String username; 10 11 @Value("${jdbc.password}") 12 private String password; 13 14 @Bean 15 public DataSource dataSource() { 16 return new DriverManagerDataSource(url, username, password); 17 } 18 }
源码解析:
1 AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); 2 if (importResource != null) { 3 // ① 4 String[] resources = importResource.getStringArray("locations"); 5 // ② 6 Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader"); 7 for (String resource : resources) { 8 // ③ 9 String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); 10 // ④ 11 configClass.addImportedResource(resolvedResource, readerClass); 12 } 13 }
① - 获取@ImportResource注解的value值
② - 获取BeanDefinitionReader
③ - 处理资源路径下的占位符
④ - 将资源和对应的解析器存放至当前配置类的importedResources属性中,它是一个LinkedHashMap
处理@Bean注解
此处处理的是在配置类中具有@Bean注解的方法
1 // ① 2 Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass); 3 for (MethodMetadata methodMetadata : beanMethods) { 4 // ② 5 configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); 6 } 7 8 // ④ 9 private void processInterfaces(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { 10 for (SourceClass ifc : sourceClass.getInterfaces()) { 11 Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(ifc); 12 for (MethodMetadata methodMetadata : beanMethods) { 13 if (!methodMetadata.isAbstract()) { 14 // A default method or other concrete method on a Java 8+ interface... 15 configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); 16 } 17 } 18 processInterfaces(configClass, ifc); 19 } 20 } 21 22 // ⑤ 23 if (sourceClass.getMetadata().hasSuperClass()) { 24 String superclass = sourceClass.getMetadata().getSuperClassName(); 25 if (superclass != null && !superclass.startsWith("java") && 26 !this.knownSuperclasses.containsKey(superclass)) { 27 this.knownSuperclasses.put(superclass, configClass); 28 // Superclass found, return its annotation metadata and recurse 29 return sourceClass.getSuperClass(); 30 } 31 }
① - 获取所有具有@Bean注解的方法
② - 将获取到的方法包装为BeanMethod对象(表示一个具有@Bean注解的@Configuration类的方法)保存到当前配置类的beanMethods属性中
④ - 处理接口中具有@Bean注解的方法
⑤ - 处理父类中具有@Bean注解的方法
取出@Configuration
直接从Parser中取出之前解析时缓存的配置类,因为这段代码在do-while循环中,因此首先移除已处理的。
1 Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); 2 configClasses.removeAll(alreadyParsed);
构建BeanDefinitionReader
若Reader为空,则创建,用于读取配置中的BeanDefinition
1 if (this.reader == null) { 2 this.reader = new ConfigurationClassBeanDefinitionReader( 3 registry, this.sourceExtractor, this.resourceLoader, this.environment, 4 this.importBeanNameGenerator, parser.getImportRegistry()); 5 }
@Configuration读取BeanDefinition
前面对各种注解的的解析最终并没有处理解析的结果,而是将其放在了ConfigurationClass对象的属性当中存储,在这里将通过Reader来处理这些不同来源的BeanDefinition。
1 this.reader.loadBeanDefinitions(configClasses); 2 alreadyParsed.addAll(configClasses); 3 4 5 public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) { 6 TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator(); 7 // ① 8 for (ConfigurationClass configClass : configurationModel) { 9 // ② 10 loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator); 11 } 12 }
① - 遍历所有的@Configuration
② - 从@Configuration读取BeanDefinition
1 private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) { 2 // ① 3 if (trackedConditionEvaluator.shouldSkip(configClass)) { 4 String beanName = configClass.getBeanName(); 5 if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) { 6 this.registry.removeBeanDefinition(beanName); 7 } 8 this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName()); 9 return; 10 } 11 12 // ② 13 if (configClass.isImported()) { 14 registerBeanDefinitionForImportedConfigurationClass(configClass); 15 } 16 17 // ③ 18 for (BeanMethod beanMethod : configClass.getBeanMethods()) { 19 loadBeanDefinitionsForBeanMethod(beanMethod); 20 } 21 22 // ④ 23 loadBeanDefinitionsFromImportedResources(configClass.getImportedResources()); 24 25 // ⑤ 26 loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()); 27 }
① - 是否需要跳过
② - 当前配置类本身是否通过@Import导入,若有则将自身注册为BeanDefinition
③ - 当前配置类中是否有@Bean注解修饰的方法,若有则处理
④ - 加载从@ImportResource导入的XML文件中定义的Bean
⑤ - 加载从@Import导入的BeanDefinition
合并已处理的BeanDefinition
1 // ① 2 String[] candidateNames = registry.getBeanDefinitionNames(); 3 4 if (registry.getBeanDefinitionCount() > candidateNames.length) { 5 // ② 6 String[] newCandidateNames = registry.getBeanDefinitionNames(); 7 // ③ 8 Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames)); 9 // ④ 10 Set<String> alreadyParsedClasses = new HashSet<>(); 11 // ⑤ 12 for (ConfigurationClass configurationClass : alreadyParsed) { 13 alreadyParsedClasses.add(configurationClass.getMetadata().getClassName()); 14 } 15 16 // ⑥ 17 for (String candidateName : newCandidateNames) { 18 // ⑦ 19 if (!oldCandidateNames.contains(candidateName)) { 20 // ⑧ 21 BeanDefinition bd = registry.getBeanDefinition(candidateName); 22 if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && 23 !alreadyParsedClasses.contains(bd.getBeanClassName())) { 24 candidates.add(new BeanDefinitionHolder(bd, candidateName)); 25 } 26 } 27 } 28 // ⑨ 29 candidateNames = newCandidateNames; 30 }
① - candidateNames 在方法的一开始就从registry中获取已经处理过的BeanDefinition名称
② - newCandidateNames 表示本次从配置类的解析中加载完毕BeanDefinition之后的BeanDefinition名称列表,它包含第一步里面的名称
③ - 将第一步的名称列表转换为Set集合
④ - 声明已解析的集合,此处用Set因为它可以保证元素不重复
⑤ - 遍历本次已经处理的所有类集合,将其加入第四步声明的Set集合内
⑥ - 遍历registry中现有的列表
⑦ - 当前的BeanDefinition必须是本次代码执行时加入的才进行处理
⑧ - 获取当前处理的类,判断是否为Configuration,并设置FULL模式和LITE模式
⑨ - 获取全部已经处理的类赋值给candidateNames
关于@SpringBootApplication注解
看到这里可能读者会有疑问:为什么入口处的@SpringBootApplication注解没有解析?每一个SpringBoot应用都能看到它的身影,它是在哪里被解析的呢?要弄明白它的功能还得从注解本身讲起。
1 /** 2 * 指明了它是一个配置类,可以声明一个或者多个@Bean方法,并且触发自动扫描和配置 3 * 等效于同时使用@Configuration、@ComponentScan、@EnableAutoConfiguration三个注解 4 * Indicates a {@link Configuration configuration} class that declares one or more 5 * {@link Bean @Bean} methods and also triggers {@link EnableAutoConfiguration 6 * auto-configuration} and {@link ComponentScan component scanning}. This is a convenience 7 * annotation that is equivalent to declaring {@code @Configuration}, 8 * {@code @EnableAutoConfiguration} and {@code @ComponentScan}. 9 * 10 * @author Phillip Webb 11 * @author Stephane Nicoll 12 * @since 1.2.0 13 */ 14 @Target(ElementType.TYPE) 15 @Retention(RetentionPolicy.RUNTIME) 16 @Documented 17 @Inherited 18 @SpringBootConfiguration 19 @EnableAutoConfiguration 20 @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) 21 public @interface SpringBootApplication { 22 23 /** 24 * 排除某些自动配置类 25 * Exclude specific auto-configuration classes such that they will never be applied. 26 * @return the classes to exclude 27 */ 28 @AliasFor(annotation = EnableAutoConfiguration.class) 29 Class<?>[] exclude() default {}; 30 31 /** 32 * 排除某些自动配置类名称 33 * Exclude specific auto-configguration class names such that they will never be 34 * applied. 35 * @return the class names to exclude 36 * @since 1.3.0 37 */ 38 @AliasFor(annotation = EnableAutoConfiguration.class) 39 String[] excludeName() default {}; 40 41 /** 42 * 用于扫描带@Component注解的基础包路径 43 * Base packages to scan for annotated components. Use {@link #scanBasePackageClasses} 44 * for a type-safe alternative to String-based package names. 45 * <p> 46 * <strong>Note:</strong> this setting is an alias for 47 * {@link ComponentScan @ComponentScan} only. It has no effect on {@code @Entity} 48 * scanning or Spring Data {@link Repository} scanning. For those you should add 49 * {@link org.springframework.boot.autoconfigure.domain.EntityScan @EntityScan} and 50 * {@code @Enable...Repositories} annotations. 51 * @return base packages to scan 52 * @since 1.3.0 53 */ 54 @AliasFor(annotation = ComponentScan.class, attribute = "basePackages") 55 String[] scanBasePackages() default {}; 56 57 /** 58 * basePackages的类型安全的替代方法,将扫描每个类的包路径 59 * Type-safe alternative to {@link #scanBasePackages} for specifying the packages to 60 * scan for annotated components. The package of each class specified will be scanned. 61 * <p> 62 * Consider creating a special no-op marker class or interface in each package that 63 * serves no purpose other than being referenced by this attribute. 64 * <p> 65 * <strong>Note:</strong> this setting is an alias for 66 * {@link ComponentScan @ComponentScan} only. It has no effect on {@code @Entity} 67 * scanning or Spring Data {@link Repository} scanning. For those you should add 68 * {@link org.springframework.boot.autoconfigure.domain.EntityScan @EntityScan} and 69 * {@code @Enable...Repositories} annotations. 70 * @return base packages to scan 71 * @since 1.3.0 72 */ 73 @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses") 74 Class<?>[] scanBasePackageClasses() default {}; 75 76 }
以上是这个注解的全部内容,通过注释可以得知它也是一个配置类,无非就多了自动扫描和自动配置的功能。当前注解类中并未看到@Configuration注解,其实它隐藏在@SpringBootConfiguration注解中
1 @Target(ElementType.TYPE) 2 @Retention(RetentionPolicy.RUNTIME) 3 @Documented 4 @Configuration 5 public @interface SpringBootConfiguration { 6 7 }
自动扫描是通过添加@ComponentScan注解实现,自动配置是通过添加@EnableAutoConfiguration注解来实现。同时又对这两个注解重新指定了显式的别名。
提示:
关于@AliasFor注解这里简单介绍一下,有助于理解代码含义。此处应用了在元注解属性中的显示别名。举例说明:
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
这个注解在@AliasFor内部填充了annotation和basePackages属性,并且声明在scanBasePackages方法上。表
示scanBasePackages在当前@SpringBootApplication注解中代表了@ComponentScan注解中的basePackages属性,是它的显式别名。
也可以说是是scanBasePackages覆盖了basePackages
也就是说@SpringBootApplication注解自身并没有任何方法,它仅仅是组合了多个注解,并通过覆盖别名的方法提供了对被组合注解的属性值设置。
再啰嗦点就是:
@SpringBootApplication注解的scanBasePackageClasses代表@ComponentScan中的basePackageClasses
1 // 写法对比 2 @SpringBootApplication(scanBasePackageClasses = {Test.class}) 3 @ComponentScan(basePackageClasses = {Test.class}) 4 // 这两种写法功能完全一致
再提一点:
我们可以看到,扫描包路径可以配置String类型的包名(通过scanBasePackages属性来配置)、配置Class类型的类(通过scanBasePackageClasses)Spring代码中的注释说道后者是前者类型安全的替代。怎么理解呢?其实也蛮直观的,当你使用String类型的包路径时它是一个字符串,在编译过程即便写错了,它也不会提示,只会在解析到这个包的时候因为包名错误而无法查找到想要加载的Bean,也不会报错。而使用类来配置的话若它不存在在编译的时候就不会通过。提早发现编写错误。那么当我们使用一个类来配置的时候,它会扫描什么内容呢?答案是它将会扫描此类所在的包下所有的类,包括子包中的类。
通过上面的解释,想必大家可以知道为何在前面篇章中没有提到@SpringBootApplication注解的解析了。
关于@EnableAutoConfiguration它启动了SpringBoot的自动配置,这块内容单独提取出来进行分析,此处只需要知道加入了这个注解,SpringBoot将会开启自动配置即可。
关于@EnableAutoConfiguration注解
这个注解是用于SpringBoot开启自动配置,通常使用@SpringBootApplication来启用它,在前一章节介绍@SpringBootApplication中可以看到是包含了@EnableAutoConfiguration注解的。因此当使用了@SpringBootApplication注解时无需再添加@EnableAutoConfiguration注解。
自动配置类是常规的Spring配置bean,它们是使用SpringFactoriesLoader机制来获取的。
1 @Target(ElementType.TYPE) 2 @Retention(RetentionPolicy.RUNTIME) 3 @Documented 4 @Inherited 5 @AutoConfigurationPackage 6 @Import(AutoConfigurationImportSelector.class) 7 public @interface EnableAutoConfiguration { 8 9 // 开启自动配置属性,默认为true 10 String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; 11 12 /** 13 * 排除不加载的配置类 14 * Exclude specific auto-configuration classes such that they will never be applied. 15 * @return the classes to exclude 16 */ 17 Class<?>[] exclude() default {}; 18 19 /** 20 * 排除不加载的配置类名称 21 * Exclude specific auto-configuration class names such that they will never be 22 * applied. 23 * @return the class names to exclude 24 * @since 1.3.0 25 */ 26 String[] excludeName() default {}; 27 28 }
首先看到这个注解上面使用了@AutoConfigurationPackage注解,它用于表示使用该注解的类所在的包将被作为加载配置的路径。
1 @Target(ElementType.TYPE) 2 @Retention(RetentionPolicy.RUNTIME) 3 @Documented 4 @Inherited 5 @Import(AutoConfigurationPackages.Registrar.class) 6 public @interface AutoConfigurationPackage { 7 8 }
注解的源码中又使用了@Import注解,并导入了AutoConfigurationPackages.Registrar.class,用于储存从导入配置类里面获取的基础包。究竟是什么包呢?还记得在前面学到的,当所有的@Configuration解析完毕之后才开始执行解析过程中获取的资源吗?在@Configuration读取BeanDefinition
章节的loadBeanDefinitionsForConfigurationClass()方法的第⑤步,调用了loadBeanDefinitionsFromRegistrars()方法
再看下EnableAutoConfiguration导入的AutoConfigurationImportSelector.class
1 /** 2 * {@link DeferredImportSelector} to handle {@link EnableAutoConfiguration 3 * auto-configuration}. This class can also be subclassed if a custom variant of 4 * {@link EnableAutoConfiguration @EnableAutoConfiguration} is needed. 5 * 6 * @author Phillip Webb 7 * @author Andy Wilkinson 8 * @author Stephane Nicoll 9 * @author Madhura Bhave 10 * @since 1.3.0 11 * @see EnableAutoConfiguration 12 */ 13 public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { 14 // 省略部分代码 15 }
从类注释上可以看出它是用于处理自动配置的。也就是说使用了@EnableAutoConfiguration注解就会导入这个自动处理配置的类,实现了一系列的Aware接口,表示它会被调用相关接口来设置Aware的对象,又实现了DeferredImportSelector接口,因此在所有@Configuration执行完毕之后将会被执行到selectImports()方法。
总结
SpringBoot中对于框架外的类加载从容器刷新阶段中的invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory)方法开始。用于调用BeanFactoryPostProcessor,位于org.springframework.context.annotation包下的ConfigurationClassPostProcessor.class实现了这个接口。
ConfigurationClassPostProcessor类并未直接实现BeanFactoryPostProcessor,而是实现了BeanDefinitionRegistryPostProcessor接口,BeanDefinitionRegistryPostProcessor继承了BeanFactoryPostProcessor。所以需要搞清楚他们各自方法的执行顺序。
在加载BeanDefinition之前,先获取所有的@Configuration,按照它的优先级排序。接着循环解析这些配置类,在解析的过程中又根据不同的注解来加载不同的内容。从而完成整个应用内部的BeanDefinition获取。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
2017-07-05 配置Yum源repo文件及搭建本地Yum服务器
2017-07-05 yum命令