spring boot实战(第十篇)Spring boot Bean加载源码分析

前言

前面的文章描述了Application对应Bean的创建,本篇将阐述spring boot中bean的创建过程

refresh


首先来看SpringApplication#run方法中refresh()方法
[html]  view plain  copy
  1. // Refresh the context  
  2.             refresh(context);  

调用的是 AbstractApplicationContext中的refresh方法
[html]  view plain  copy
  1. protected void refresh(ApplicationContext applicationContext) {  
  2.         Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);  
  3.         ((AbstractApplicationContext) applicationContext).refresh();  
  4.     }  
方法定义如下:
[html]  view plain  copy
  1. public void refresh() throws BeansException, IllegalStateException {  
  2.         synchronized (this.startupShutdownMonitor) {  
  3.             // Prepare this context for refreshing.  
  4.             prepareRefresh();  
  5.   
  6.             // Tell the subclass to refresh the internal bean factory.  
  7.             ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();  
  8.   
  9.             // Prepare the bean factory for use in this context.  
  10.             prepareBeanFactory(beanFactory);  
  11.   
  12.             try {  
  13.                 // Allows post-processing of the bean factory in context subclasses.  
  14.                 postProcessBeanFactory(beanFactory);  
  15.   
  16.                 // Invoke factory processors registered as beans in the context.  
  17.                 invokeBeanFactoryPostProcessors(beanFactory);  
  18.   
  19.                 // Register bean processors that intercept bean creation.  
  20.                 registerBeanPostProcessors(beanFactory);  
  21.   
  22.                 // Initialize message source for this context.  
  23.                 initMessageSource();  
  24.   
  25.                 // Initialize event multicaster for this context.  
  26.                 initApplicationEventMulticaster();  
  27.   
  28.                 // Initialize other special beans in specific context subclasses.  
  29.                 onRefresh();  
  30.   
  31.                 // Check for listener beans and register them.  
  32.                 registerListeners();  
  33.   
  34.                 // Instantiate all remaining (non-lazy-init) singletons.  
  35.                 finishBeanFactoryInitialization(beanFactory);  
  36.   
  37.                 // Last step: publish corresponding event.  
  38.                 finishRefresh();  
  39.             }  
  40.   
  41.             catch (BeansException ex) {  
  42.                 logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex);  
  43.   
  44.                 // Destroy already created singletons to avoid dangling resources.  
  45.                 destroyBeans();  
  46.   
  47.                 // Reset 'active' flag.  
  48.                 cancelRefresh(ex);  
  49.   
  50.                 // Propagate exception to caller.  
  51.                 throw ex;  
  52.             }  
  53.         }  
  54.     }  

该方法中涉及的过程非常多,需要一步步来分析

获取BeanFactory
[html]  view plain  copy
  1. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();  
通过前面的文章应该知道对应的BeanFactory为DefaultListableBeanFactory
 
直奔主题来看如下方法
[html]  view plain  copy
  1. invokeBeanFactoryPostProcessors(beanFactory);  

[html]  view plain  copy
  1. protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {  
  2.         PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());  
  3.     }  
首先来看getBeanFactoryPostProcessors(),其对应值为:ConfigurationWarningsApplicationContextInitializer$ConfigurationWarningsPostProcessor、PropertySourceOrderingPostProcessor

ConfigurationWarningsApplicationContextInitializer是在ConfigurationWarningsApplicationContextInitializer中执行
[html]  view plain  copy
  1. public void initialize(ConfigurableApplicationContext context) {  
  2.         context.addBeanFactoryPostProcessor(new ConfigurationWarningsPostProcessor(  
  3.                 getChecks()));  
  4.     }  
添加

PropertySourceOrderingPostProcessor是在ConfigFileApplicationListener执行
[html]  view plain  copy
  1. protected void addPostProcessors(ConfigurableApplicationContext context) {  
  2.         context.addBeanFactoryPostProcessor(new PropertySourceOrderingPostProcessor(  
  3.                 context));  
  4.     }  
添加

来看invokeBeanFactoryPostProcessors方法

[html]  view plain  copy
  1. public static void invokeBeanFactoryPostProcessors(  
  2.             ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {  
  3.   
  4.         // Invoke BeanDefinitionRegistryPostProcessors first, if any.  
  5.         Set<String> processedBeans = new HashSet<String>();  
  6.   
  7.         if (beanFactory instanceof BeanDefinitionRegistry) {  
  8.             ...//处理后处理器  
  9.                
  10.             String[] postProcessorNames =  
  11.                     beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);  
  12.   
  13.             // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.  
  14.             List<BeanDefinitionRegistryPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanDefinitionRegistryPostProcessor>();  
  15.             for (String ppName : postProcessorNames) {  
  16.                 if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {  
  17.                     priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));  
  18.                     processedBeans.add(ppName);  
  19.                 }  
  20.             }  
  21.             OrderComparator.sort(priorityOrderedPostProcessors);  
  22.             registryPostProcessors.addAll(priorityOrderedPostProcessors);  
  23.             invokeBeanDefinitionRegistryPostProcessors(priorityOrderedPostProcessors, registry);  
  24.   
  25.             // Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.  
  26.             postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);  
  27.             List<BeanDefinitionRegistryPostProcessor> orderedPostProcessors = new ArrayList<BeanDefinitionRegistryPostProcessor>();  
  28.             for (String ppName : postProcessorNames) {  
  29.                 if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {  
  30.                     orderedPostProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));  
  31.                     processedBeans.add(ppName);  
  32.                 }  
  33.             }  
  34.             OrderComparator.sort(orderedPostProcessors);  
  35.             registryPostProcessors.addAll(orderedPostProcessors);  
  36.             invokeBeanDefinitionRegistryPostProcessors(orderedPostProcessors, registry);  
  37.   
  38.             // Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.  
  39.             boolean reiterate = true;  
  40.             while (reiterate) {  
  41.                 reiterate = false;  
  42.                 postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);  
  43.                 for (String ppName : postProcessorNames) {  
  44.                     if (!processedBeans.contains(ppName)) {  
  45.                         BeanDefinitionRegistryPostProcessor pp = beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class);  
  46.                         registryPostProcessors.add(pp);  
  47.                         processedBeans.add(ppName);  
  48.                         pp.postProcessBeanDefinitionRegistry(registry);  
  49.                         reiterate = true;  
  50.                     }  
  51.                 }  
  52.             }  
  53.   
  54.             // Now, invoke the postProcessBeanFactory callback of all processors handled so far 执行后处理器  
  55.             invokeBeanFactoryPostProcessors(registryPostProcessors, beanFactory);  
  56.             invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);  
  57.         }  

[html]  view plain  copy
  1. String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);  
按照bean的类型获取类型为BeanDefinitionRegistryPostProcessor的bean,这里获取到的bean名称为:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor;对应的Class为ConfigurationClassPostProcessor

在前面文章中创建上下文的时候beanfactory创建了该bean。

经过排序后执行如下方法

[html]  view plain  copy
  1. invokeBeanDefinitionRegistryPostProcessors(priorityOrderedPostProcessors, registry);  

[html]  view plain  copy
  1. private static void invokeBeanDefinitionRegistryPostProcessors(  
  2.             Collection extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {  
  3.   
  4.         for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {  
  5.             postProcessor.postProcessBeanDefinitionRegistry(registry);  
  6.         }  
  7.     }  

实际调用 ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry

[html]  view plain  copy
  1. public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {  
  2.         ...//注册若干bean  
  3.         processConfigBeanDefinitions(registry);  
  4.     }  

processConfigBeanDefinitions( registry)如下:

[html]  view plain  copy
  1. public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {  
  2.         Set<BeanDefinitionHolder> configCandidates = new LinkedHashSet<BeanDefinitionHolder>();  
  3.         String[] candidateNames = registry.getBeanDefinitionNames();  
  4.   
  5.         for (String beanName : candidateNames) {  
  6.             BeanDefinition beanDef = registry.getBeanDefinition(beanName);  
  7.             if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||  
  8.                     ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {  
  9.                 if (logger.isDebugEnabled()) {  
  10.                     logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);  
  11.                 }  
  12.             }  
  13.             else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {  
  14.                 configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));  
  15.             }  
  16.         }  
  17.   
  18.         // Return immediately if no @Configuration classes were found  
  19.         if (configCandidates.isEmpty()) {  
  20.             return;  
  21.         }  
  22.   
  23.         // Detect any custom bean name generation strategy supplied through the enclosing application context  
  24.         SingletonBeanRegistry singletonRegistry = null;  
  25.         if (registry instanceof SingletonBeanRegistry) {  
  26.             singletonRegistry = (SingletonBeanRegistry) registry;  
  27.             if (!this.localBeanNameGeneratorSet && singletonRegistry.containsSingleton(CONFIGURATION_BEAN_NAME_GENERATOR)) {  
  28.                 BeanNameGenerator generator = (BeanNameGenerator) singletonRegistry.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);  
  29.                 this.componentScanBeanNameGenerator = generator;  
  30.                 this.importBeanNameGenerator = generator;  
  31.             }  
  32.         }  
  33.   
  34.         // Parse each @Configuration class  
  35.         ConfigurationClassParser parser = new ConfigurationClassParser(  
  36.                 this.metadataReaderFactory, this.problemReporter, this.environment,  
  37.                 this.resourceLoader, this.componentScanBeanNameGenerator, registry);  
  38.   
  39.         Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());  
  40.         do {  
  41.             parser.parse(configCandidates);  
  42.             parser.validate();  
  43.   
  44.             Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(parser.getConfigurationClasses());  
  45.             configClasses.removeAll(alreadyParsed);  
  46.   
  47.             // Read the model and create bean definitions based on its content  
  48.             if (this.reader == null) {  
  49.                 this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor,  
  50.                         this.problemReporter, this.metadataReaderFactory, this.resourceLoader, this.environment,  
  51.                         this.importBeanNameGenerator, parser.getImportRegistry());  
  52.             }  
  53.             this.reader.loadBeanDefinitions(configClasses);  
  54.             alreadyParsed.addAll(configClasses);  
  55.   
  56.             configCandidates.clear();  
  57.             if (registry.getBeanDefinitionCount() > candidateNames.length) {  
  58.                 String[] newCandidateNames = registry.getBeanDefinitionNames();  
  59.                 Set<String> oldCandidateNames = new HashSet<String>(Arrays.asList(candidateNames));  
  60.                 Set<String> alreadyParsedClasses = new HashSet<String>();  
  61.                 for (ConfigurationClass configurationClass : alreadyParsed) {  
  62.                     alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());  
  63.                 }  
  64.                 for (String candidateName : newCandidateNames) {  
  65.                     if (!oldCandidateNames.contains(candidateName)) {  
  66.                         BeanDefinition beanDef = registry.getBeanDefinition(candidateName);  
  67.                         if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory) &&  
  68.                                 !alreadyParsedClasses.contains(beanDef.getBeanClassName())) {  
  69.                             configCandidates.add(new BeanDefinitionHolder(beanDef, candidateName));  
  70.                         }  
  71.                     }  
  72.                 }  
  73.                 candidateNames = newCandidateNames;  
  74.             }  
  75.         }  
  76.         while (!configCandidates.isEmpty());  
  77.   
  78.         // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes  
  79.         if (singletonRegistry != null) {  
  80.             if (!singletonRegistry.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {  
  81.                 singletonRegistry.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());  
  82.             }  
  83.         }  
  84.   
  85.         if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {  
  86.             ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();  
  87.         }  
  88.     }  

又是一段很长的代码

[html]  view plain  copy
  1. String[] candidateNames = registry.getBeanDefinitionNames();  
获取已经注册的bean名称,其信息为:


这里看到上一篇中创建的Application对应bean

[html]  view plain  copy
  1. for (String beanName : candidateNames) {  
  2.             BeanDefinition beanDef = registry.getBeanDefinition(beanName);  
  3.             if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||  
  4.                     ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {  
  5.                 if (logger.isDebugEnabled()) {  
  6.                     logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);  
  7.                 }  
  8.             }  
  9.             else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {  
  10.                 configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));  
  11.             }  
  12.         }  

判断对应bean是否为配置文件bean(包含Configuration注解),经过筛选只有Application对应bean满足条件

[html]  view plain  copy
  1. ConfigurationClassParser parser = new ConfigurationClassParser(  
  2.             this.metadataReaderFactory, this.problemReporter, this.environment,  
  3.             this.resourceLoader, this.componentScanBeanNameGenerator, registry);  
该代码构造了 Configuration类解析器

执行

[html]  view plain  copy
  1. parser.parse(configCandidates);  

[html]  view plain  copy
  1. public void parse(Set<BeanDefinitionHolder> configCandidates) {  
  2.         this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>();  
  3.   
  4.         for (BeanDefinitionHolder holder : configCandidates) {  
  5.             BeanDefinition bd = holder.getBeanDefinition();  
  6.             try {  
  7.                 if (bd instanceof AnnotatedBeanDefinition) {   //执行该部分代码  
  8.                     parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());  
  9.                 }  
  10.                 else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {  
  11.                     parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());  
  12.                 }  
  13.                 else {  
  14.                     parse(bd.getBeanClassName(), holder.getBeanName());  
  15.                 }  
  16.             }  
  17.             catch (BeanDefinitionStoreException ex) {  
  18.                 throw ex;  
  19.             }  
  20.             catch (Exception ex) {  
  21.                 throw new BeanDefinitionStoreException(  
  22.                         "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);  
  23.             }  
  24.         }  
  25.   
  26.         processDeferredImportSelectors();  
  27.     }  

调用

[html]  view plain  copy
  1. parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());  

最终调用

[html]  view plain  copy
  1. protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {  
  2.         if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {  
  3.             return;  
  4.         }  
  5.   
  6.         ConfigurationClass existingClass = this.configurationClasses.get(configClass);  
  7.         if (existingClass != null) {  
  8.             if (configClass.isImported()) {  
  9.                 if (existingClass.isImported()) {  
  10.                     existingClass.mergeImportedBy(configClass);  
  11.                 }  
  12.                 // Otherwise ignore new imported config class; existing non-imported class overrides it.  
  13.                 return;  
  14.             }  
  15.             else {  
  16.                 // Explicit bean definition found, probably replacing an import.  
  17.                 // Let's remove the old one and go with the new one.  
  18.                 this.configurationClasses.remove(configClass);  
  19.                 for (Iterator<ConfigurationClass> it = this.knownSuperclasses.values().iterator(); it.hasNext(); ) {  
  20.                     if (configClass.equals(it.next())) {  
  21.                         it.remove();  
  22.                     }  
  23.                 }  
  24.             }  
  25.         }  
  26.   
  27.         // Recursively process the configuration class and its superclass hierarchy.  
  28.         SourceClass sourceClass = asSourceClass(configClass);  
  29.         do {  
  30.             sourceClass = doProcessConfigurationClass(configClass, sourceClass);  
  31.         }  
  32.         while (sourceClass != null);  
  33.   
  34.         this.configurationClasses.put(configClass, configClass);  
  35.     }  

首先判断该bean是否被跳过(该部分代码上一篇已说明),然后对Class进行包装,调用 sourceClass = doProcessConfigurationClass( configClass, sourceClass)处理Application类

解析Configuration注解

[html]  view plain  copy
  1. protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {  
  2.         // Recursively process any member (nested) classes first  
  3.         processMemberClasses(configClass, sourceClass);  
  4.   
  5.         // Process any @PropertySource annotations  
  6.         for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(  
  7.                 sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) {  
  8.             if (this.environment instanceof ConfigurableEnvironment) {  
  9.                 processPropertySource(propertySource);  
  10.             }  
  11.             else {  
  12.                 logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +  
  13.                         "]. Reason: Environment must implement ConfigurableEnvironment");  
  14.             }  
  15.         }  
  16.   
  17.         // Process any @ComponentScan annotations  
  18.         AnnotationAttributes componentScan = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ComponentScan.class);  
  19.         if (componentScan != null && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {  
  20.             // The config class is annotated with @ComponentScan -> perform the scan immediately  
  21.             Set<BeanDefinitionHolder> scannedBeanDefinitions =  
  22.                     this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());  
  23.             // Check the set of scanned definitions for any further config classes and parse recursively if necessary  
  24.             for (BeanDefinitionHolder holder : scannedBeanDefinitions) {  
  25.                 if (ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(), this.metadataReaderFactory)) {  
  26.                     parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());  
  27.                 }  
  28.             }  
  29.         }  
  30.   
  31.         // Process any @Import annotations  
  32.         processImports(configClass, sourceClass, getImports(sourceClass), true);  
  33.   
  34.         // Process any @ImportResource annotations  
  35.         if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {  
  36.             AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);  
  37.             String[] resources = importResource.getStringArray("value");  
  38.             Class extends BeanDefinitionReader> readerClass = importResource.getClass("reader");  
  39.             for (String resource : resources) {  
  40.                 String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);  
  41.                 configClass.addImportedResource(resolvedResource, readerClass);  
  42.             }  
  43.         }  
  44.   
  45.         // Process individual @Bean methods  
  46.         Set<MethodMetadata> beanMethods = sourceClass.getMetadata().getAnnotatedMethods(Bean.class.getName());  
  47.         for (MethodMetadata methodMetadata : beanMethods) {  
  48.             configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));  
  49.         }  
  50.   
  51.         // Process superclass, if any  
  52.         if (sourceClass.getMetadata().hasSuperClass()) {  
  53.             String superclass = sourceClass.getMetadata().getSuperClassName();  
  54.             if (!superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) {  
  55.                 this.knownSuperclasses.put(superclass, configClass);  
  56.                 // Superclass found, return its annotation metadata and recurse  
  57.                 return sourceClass.getSuperClass();  
  58.             }  
  59.         }  
  60.   
  61.         // No superclass -> processing is complete  
  62.         return null;  
  63.     }  

到这里就看到了如何去解析Application类

[html]  view plain  copy
  1. processMemberClasses(configClass, sourceClass);  
处理其中内部类,解析内部类的过程和外部类相似,因此继续看下面的代码

处理PropertySource注解

[html]  view plain  copy
  1. // Process any @PropertySource annotations  
  2.         for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(  
  3.                 sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) {  
  4.             if (this.environment instanceof ConfigurableEnvironment) {  
  5.                 processPropertySource(propertySource);  
  6.             }  
  7.             else {  
  8.                 logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +  
  9.                         "]. Reason: Environment must implement ConfigurableEnvironment");  
  10.             }  
  11.         }<pre name="code" class="html">  
[html]  view plain  copy
  1.   

 
   

其核心操作:

[html]  view plain  copy
  1. private void processPropertySource(AnnotationAttributes propertySource) throws IOException {  
  2.         String name = propertySource.getString("name");  
  3.         String[] locations = propertySource.getStringArray("value");  
  4.         boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");  
  5.         Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");  
  6.         for (String location : locations) {  
  7.             try {  
  8.                 String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);  
  9.                 Resource resource = this.resourceLoader.getResource(resolvedLocation);  
  10.                 ResourcePropertySource rps = (StringUtils.hasText(name) ?  
  11.                         new ResourcePropertySource(name, resource) : new ResourcePropertySource(resource));  
  12.                 addPropertySource(rps);  
  13.             }  
  14.             catch (IllegalArgumentException ex) {  
  15.                 // from resolveRequiredPlaceholders  
  16.                 if (!ignoreResourceNotFound) {  
  17.                     throw ex;  
  18.                 }  
  19.             }  
  20.             catch (FileNotFoundException ex) {  
  21.                 // from ResourcePropertySource constructor  
  22.                 if (!ignoreResourceNotFound) {  
  23.                     throw ex;  
  24.                 }  
  25.             }  
  26.         }  
  27.     }  
通过注解中的信息获取资源信息,然后添加到MutablePropertySources propertySources = ((ConfigurableEnvironment) this. environment).getPropertySources()中,该内容前面已有讲述

解析ComponentScan注解

[html]  view plain  copy
  1. // Process any @ComponentScan annotations  
  2.         AnnotationAttributes componentScan = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ComponentScan.class);  
  3.         if (componentScan != null && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {  
  4.             // The config class is annotated with @ComponentScan -> perform the scan immediately  
  5.             Set<BeanDefinitionHolder> scannedBeanDefinitions =  
  6.                     this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());  
  7.             // Check the set of scanned definitions for any further config classes and parse recursively if necessary  
  8.             for (BeanDefinitionHolder holder : scannedBeanDefinitions) {  
  9.                 if (ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(), this.metadataReaderFactory)) {  
  10.                     parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());  
  11.                 }  
  12.             }  
  13.         }  
ComponentScan注解的作用大家都明白,扫描执行路径下bean信息,那么具体是如何实现的?需要跟进去看代码,调用

[html]  view plain  copy
  1. Set<BeanDefinitionHolder> scannedBeanDefinitions =  
  2.                     this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());  

[html]  view plain  copy
  1. public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {  
  2.          ...//通过注解中的信息设置扫描器的参数信息  
  3.         return scanner.doScan(StringUtils.toStringArray(basePackages));  
  4.     }  

代码中忽略了扫描器对应的参数信息,直接看doScan方法

[html]  view plain  copy
  1. protected Set<BeanDefinitionHolder> doScan(String... basePackages) {  
  2.     Assert.notEmpty(basePackages, "At least one base package must be specified");  
  3.     Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();  
  4.     for (String basePackage : basePackages) {  
  5.         Set<BeanDefinition> candidates = findCandidateComponents(basePackage);  
  6.         for (BeanDefinition candidate : candidates) {  
  7.             ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);  
  8.             candidate.setScope(scopeMetadata.getScopeName());  
  9.             String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);  
  10.             if (candidate instanceof AbstractBeanDefinition) {  
  11.                 postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);  
  12.             }  
  13.             if (candidate instanceof AnnotatedBeanDefinition) {  
  14.                 AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);  
  15.             }  
  16.             if (checkCandidate(beanName, candidate)) {  
  17.                 BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);  
  18.                 definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);  
  19.                 beanDefinitions.add(definitionHolder);  
  20.                 registerBeanDefinition(definitionHolder, this.registry);  
  21.             }  
  22.         }  
  23.     }  
  24.     return beanDefinitions;  
  25. }  
遍历basePackages信息,
[html]  view plain  copy
  1. Set<BeanDefinition> candidates = findCandidateComponents(basePackage);  
查询类路径下申明的组件信息,
[html]  view plain  copy
  1. public Set<BeanDefinition> findCandidateComponents(String basePackage) {  
  2.         Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();  
  3.         try {  
  4.             String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +  
  5.                     resolveBasePackage(basePackage) + "/" + this.resourcePattern;  
  6.             Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);  
  7.             boolean traceEnabled = logger.isTraceEnabled();  
  8.             boolean debugEnabled = logger.isDebugEnabled();  
  9.             for (Resource resource : resources) {  
  10.                 if (traceEnabled) {  
  11.                     logger.trace("Scanning " + resource);  
  12.                 }  
  13.                 if (resource.isReadable()) {  
  14.                     try {  
  15.                         MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);  
  16.                         if (isCandidateComponent(metadataReader)) {  
  17.                             ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);  
  18.                             sbd.setResource(resource);  
  19.                             sbd.setSource(resource);  
  20.                             if (isCandidateComponent(sbd)) {  
  21.                                 if (debugEnabled) {  
  22.                                     logger.debug("Identified candidate component class: " + resource);  
  23.                                 }  
  24.                                 candidates.add(sbd);  
  25.                             }  
  26.                             else {  
  27.                                 if (debugEnabled) {  
  28.                                     logger.debug("Ignored because not a concrete top-level class: " + resource);  
  29.                                 }  
  30.                             }  
  31.                         }  
  32.                         else {  
  33.                             if (traceEnabled) {  
  34.                                 logger.trace("Ignored because not matching any filter: " + resource);  
  35.                             }  
  36.                         }  
  37.                     }  
  38.                     catch (Throwable ex) {  
  39.                         throw new BeanDefinitionStoreException(  
  40.                                 "Failed to read candidate component class: " + resource, ex);  
  41.                     }  
  42.                 }  
  43.                 else {  
  44.                     if (traceEnabled) {  
  45.                         logger.trace("Ignored because not readable: " + resource);  
  46.                     }  
  47.                 }  
  48.             }  
  49.         }  
  50.         catch (IOException ex) {  
  51.             throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);  
  52.         }  
  53.         return candidates;  
  54.     }  


[html]  view plain  copy
  1. Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);  

[html]  view plain  copy
  1. public Resource[] getResources(String locationPattern) throws IOException {  
  2.         Assert.notNull(locationPattern, "Location pattern must not be null");  
  3.         if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {  
  4.             // a class path resource (multiple resources for same name possible)  
  5.             if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {  
  6.                 // a class path resource pattern  
  7.                 return findPathMatchingResources(locationPattern);  
  8.             }  
  9.             else {  
  10.                 // all class path resources with the given name  
  11.                 return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));  
  12.             }  
  13.         }  
  14.         else {  
  15.             // Only look for a pattern after a prefix here  
  16.             // (to not get fooled by a pattern symbol in a strange prefix).  
  17.             int prefixEnd = locationPattern.indexOf(":") + 1;  
  18.             if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {  
  19.                 // a file pattern  
  20.                 return findPathMatchingResources(locationPattern);  
  21.             }  
  22.             else {  
  23.                 // a single resource with the given name  
  24.                 return new Resource[] {getResourceLoader().getResource(locationPattern)};  
  25.             }  
  26.         }  
  27.     }  

解析路径信息,这里spring有自己的一套继续规则,通过findPathMatchingResources()检索到指定类路径下所有的*.class文件,然后调用findAllClassPathResources解析Class文件

[html]  view plain  copy
  1. protected Resource[] findAllClassPathResources(String location) throws IOException {  
  2.         String path = location;  
  3.         if (path.startsWith("/")) {  
  4.             path = path.substring(1);  
  5.         }  
  6.         Set<Resource> result = doFindAllClassPathResources(path);  
  7.         return result.toArray(new Resource[result.size()]);  
  8.     }  

[html]  view plain  copy
  1. protected Set<Resource> doFindAllClassPathResources(String path) throws IOException {  
  2.         Set<Resource> result = new LinkedHashSet<Resource>(16);  
  3.         ClassLoader cl = getClassLoader();  
  4.         Enumeration<URL> resourceUrls = (cl != null ? cl.getResources(path) : ClassLoader.getSystemResources(path));  
  5.         while (resourceUrls.hasMoreElements()) {  
  6.             URL url = resourceUrls.nextElement();  
  7.             result.add(convertClassLoaderURL(url));  
  8.         }  
  9.         if ("".equals(path)) {  
  10.             // The above result is likely to be incomplete, i.e. only containing file system references.  
  11.             // We need to have pointers to each of the jar files on the classpath as well...  
  12.             addAllClassLoaderJarRoots(cl, result);  
  13.         }  
  14.         return result;  
  15.     }  

通过上面的代码可以发现,在获取到path路径以后spring采用类加载器获取指定Class文件对应的资源信息


获取完资源信息后调用

[html]  view plain  copy
  1. MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);  
解析资源信息对应的元数据


[html]  view plain  copy
  1. if (isCandidateComponent(metadataReader)) {  
  2.                             ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);  
  3.                             sbd.setResource(resource);  
  4.                             sbd.setSource(resource);  
  5.                             if (isCandidateComponent(sbd)) {  
  6.                                 if (debugEnabled) {  
  7.                                     logger.debug("Identified candidate component class: " + resource);  
  8.                                 }  
  9.                                 candidates.add(sbd);  
  10.                             }  
  11.                             else {  
  12.                                 if (debugEnabled) {  
  13.                                     logger.debug("Ignored because not a concrete top-level class: " + resource);  
  14.                                 }  
  15.                             }  
  16.                         }  
  17.                         else {  
  18.                             if (traceEnabled) {  
  19.                                 logger.trace("Ignored because not matching any filter: " + resource);  
  20.                             }  
  21.                         }  
如果存在Componment注解修饰的Class文件则加入到BeanDefinition集合中返回。


回到调用扫描bean处

[html]  view plain  copy
  1. for (BeanDefinitionHolder holder : scannedBeanDefinitions) {  
  2.                 if (ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(), this.metadataReaderFactory)) {  
  3.                     parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());  
  4.                 }  
  5.             }  

遍历扫描到的bean信息,如果为配置bean,则执行parse方法,该方法调用processConfigurationClass,形成一个递归的操作。

解析Import注解

[html]  view plain  copy
  1. <span style="font-family:Arial, Helvetica, sans-serif;"> span><span style="font-family: Arial, Helvetica, sans-serif;">processImports(span><span class="s1" style="font-family: Arial, Helvetica, sans-serif;">configClassspan><span style="font-family: Arial, Helvetica, sans-serif;">span><span class="s1" style="font-family: Arial, Helvetica, sans-serif;">sourceClassspan><span style="font-family: Arial, Helvetica, sans-serif;">, getImports(span><span class="s1" style="font-family: Arial, Helvetica, sans-serif;">sourceClassspan><span style="font-family: Arial, Helvetica, sans-serif;">), span><span class="s2" style="font-family: Arial, Helvetica, sans-serif;">truespan><span style="font-family: Arial, Helvetica, sans-serif;">);span>  

处理import注解,该注解在spring boot中使用非常频繁

[html]  view plain  copy
  1. private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,  
  2.             Collection<SourceClass> importCandidates, boolean checkForCircularImports) throws IOException {  
  3.   
  4.          ...  
  5.            
  6.             this.importStack.push(configClass);  
  7.             try {  
  8.                 for (SourceClass candidate : importCandidates) {  
  9.                     if (candidate.isAssignable(ImportSelector.class)) {  
  10.                         // Candidate class is an ImportSelector -> delegate to it to determine imports  
  11.                         Class> candidateClass = candidate.loadClass();  
  12.                         ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);  
  13.                         invokeAwareMethods(selector);  
  14.                         if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {  
  15.                             this.deferredImportSelectors.add(  
  16.                                     new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));  
  17.                         }  
  18.                         else {  
  19.                             String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());  
  20.                             Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);  
  21.                             processImports(configClass, currentSourceClass, importSourceClasses, false);  
  22.                         }  
  23.                     }  
  24.                     else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {  
  25.                         // Candidate class is an ImportBeanDefinitionRegistrar ->  
  26.                         // delegate to it to register additional bean definitions  
  27.                         Class> candidateClass = candidate.loadClass();  
  28.                         ImportBeanDefinitionRegistrar registrar =  
  29.                                 BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);  
  30.                         invokeAwareMethods(registrar);  
  31.                         configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());  
  32.                     }  
  33.                     else {  
  34.                         // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->  
  35.                         // process it as an @Configuration class  
  36.                         this.importStack.registerImport(  
  37.                                 currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());  
  38.                         processConfigurationClass(candidate.asConfigClass(configClass));  
  39.                     }  
  40.                 }  
  41.             }  
  42.             catch (BeanDefinitionStoreException ex) {  
  43.                 throw ex;  
  44.             }  
  45.             catch (Exception ex) {  
  46.                 throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" +  
  47.                         configClass.getMetadata().getClassName() + "]", ex);  
  48.             }  
  49.             finally {  
  50.                 this.importStack.pop();  
  51.             }  
  52.         }  
  53.     }  
如果Import注解中Class为ImportSelector子类,通过invokeAwareMethods( selector)设置aware值,如果类型为DeferredImportSelector则添加到deferredImportSelectors集合中,待前面的 parser .parse( configCandidates )

方法中processDeferredImportSelectors()处理;如果不是,则执行selectImports方法,将获取到的结果递归调用processImports,解析selectImports得到的结果

如果Import注解中Class为ImportBeanDefinitionRegistrar子类,则添加到importBeanDefinitionRegistrars中,注意该部分的数据在执行完parser.parse(configCandidates)后调用this.reader.loadBeanDefinitions(configClasses)解析

否则执行配置信息的解析操作。


解析ImportResource注解

[html]  view plain  copy
  1. // Process any @ImportResource annotations  
  2.         if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {  
  3.             AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);  
  4.             String[] resources = importResource.getStringArray("value");  
  5.             Class extends BeanDefinitionReader> readerClass = importResource.getClass("reader");  
  6.             for (String resource : resources) {  
  7.                 String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);  
  8.                 configClass.addImportedResource(resolvedResource, readerClass);  
  9.             }  
  10.         }  

解析Bean注解

[html]  view plain  copy
  1. Set<MethodMetadata> beanMethods = sourceClass.getMetadata().getAnnotatedMethods(Bean.class.getName());  
  2.         for (MethodMetadata methodMetadata : beanMethods) {  
  3.             configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));  
  4.         }  


上面这两个注解相对来讲要简单一些,至此bean的解析完成,这里面涉及到多重递归,首先理清楚一条线才能把代码看明白。



本文转自http://blog.csdn.net/liaokailin/article/details/49107209

你可能感兴趣的:(spring,boot)