前言

前面的文章描述了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. // Tell the subclass to refresh the internal bean factory.
  6. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
  7. // Prepare the bean factory for use in this context.
  8. prepareBeanFactory(beanFactory);
  9. try {
  10. // Allows post-processing of the bean factory in context subclasses.
  11. postProcessBeanFactory(beanFactory);
  12. // Invoke factory processors registered as beans in the context.
  13. invokeBeanFactoryPostProcessors(beanFactory);
  14. // Register bean processors that intercept bean creation.
  15. registerBeanPostProcessors(beanFactory);
  16. // Initialize message source for this context.
  17. initMessageSource();
  18. // Initialize event multicaster for this context.
  19. initApplicationEventMulticaster();
  20. // Initialize other special beans in specific context subclasses.
  21. onRefresh();
  22. // Check for listener beans and register them.
  23. registerListeners();
  24. // Instantiate all remaining (non-lazy-init) singletons.
  25. finishBeanFactoryInitialization(beanFactory);
  26. // Last step: publish corresponding event.
  27. finishRefresh();
  28. }
  29. catch (BeansException ex) {
  30. logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex);
  31. // Destroy already created singletons to avoid dangling resources.
  32. destroyBeans();
  33. // Reset 'active' flag.
  34. cancelRefresh(ex);
  35. // Propagate exception to caller.
  36. throw ex;
  37. }
  38. }
  39. }

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

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

[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. for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
  4. postProcessor.postProcessBeanDefinitionRegistry(registry);
  5. }
  6. }

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

又是一段很长的代码

[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. for (BeanDefinitionHolder holder : configCandidates) {
  4. BeanDefinition bd = holder.getBeanDefinition();
  5. try {
  6. if (bd instanceof AnnotatedBeanDefinition) {   //执行该部分代码
  7. parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
  8. }
  9. else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
  10. parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
  11. }
  12. else {
  13. parse(bd.getBeanClassName(), holder.getBeanName());
  14. }
  15. }
  16. catch (BeanDefinitionStoreException ex) {
  17. throw ex;
  18. }
  19. catch (Exception ex) {
  20. throw new BeanDefinitionStoreException(
  21. "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
  22. }
  23. }
  24. processDeferredImportSelectors();
  25. }

调用

[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. ConfigurationClass existingClass = this.configurationClasses.get(configClass);
  6. if (existingClass != null) {
  7. if (configClass.isImported()) {
  8. if (existingClass.isImported()) {
  9. existingClass.mergeImportedBy(configClass);
  10. }
  11. // Otherwise ignore new imported config class; existing non-imported class overrides it.
  12. return;
  13. }
  14. else {
  15. // Explicit bean definition found, probably replacing an import.
  16. // Let's remove the old one and go with the new one.
  17. this.configurationClasses.remove(configClass);
  18. for (Iterator<ConfigurationClass> it = this.knownSuperclasses.values().iterator(); it.hasNext(); ) {
  19. if (configClass.equals(it.next())) {
  20. it.remove();
  21. }
  22. }
  23. }
  24. }
  25. // Recursively process the configuration class and its superclass hierarchy.
  26. SourceClass sourceClass = asSourceClass(configClass);
  27. do {
  28. sourceClass = doProcessConfigurationClass(configClass, sourceClass);
  29. }
  30. while (sourceClass != null);
  31. this.configurationClasses.put(configClass, configClass);
  32. }

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

到这里就看到了如何去解析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

 

其核心操作:

[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. }

通过注解中的信息获取资源信息,然后添加到MutablePropertySourcespropertySources = ((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. processImports(configClass, sourceClass, getImports(sourceClass), true);

处理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. this.importStack.push(configClass);
  5. try {
  6. for (SourceClass candidate : importCandidates) {
  7. if (candidate.isAssignable(ImportSelector.class)) {
  8. // Candidate class is an ImportSelector -> delegate to it to determine imports
  9. Class<?> candidateClass = candidate.loadClass();
  10. ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
  11. invokeAwareMethods(selector);
  12. if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
  13. this.deferredImportSelectors.add(
  14. new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
  15. }
  16. else {
  17. String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
  18. Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
  19. processImports(configClass, currentSourceClass, importSourceClasses, false);
  20. }
  21. }
  22. else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
  23. // Candidate class is an ImportBeanDefinitionRegistrar ->
  24. // delegate to it to register additional bean definitions
  25. Class<?> candidateClass = candidate.loadClass();
  26. ImportBeanDefinitionRegistrar registrar =
  27. BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
  28. invokeAwareMethods(registrar);
  29. configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
  30. }
  31. else {
  32. // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
  33. // process it as an @Configuration class
  34. this.importStack.registerImport(
  35. currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
  36. processConfigurationClass(candidate.asConfigClass(configClass));
  37. }
  38. }
  39. }
  40. catch (BeanDefinitionStoreException ex) {
  41. throw ex;
  42. }
  43. catch (Exception ex) {
  44. throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" +
  45. configClass.getMetadata().getClassName() + "]", ex);
  46. }
  47. finally {
  48. this.importStack.pop();
  49. }
  50. }
  51. }

如果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的解析完成,这里面涉及到多重递归,首先理清楚一条线才能把代码看明白。

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

  1. bytebuf池_Netty篇:ByteBuf之内存池源码分析

    Netty的内存池的实现有些复杂,使用了大量的位运算,晦涩难懂,不过万能的博客上好多大神已经介绍的非常详细,推荐四篇很详细很棒的源码分析的文章链接,本文根据自己的理解顺一下思路,内容主要也是出自以下四 ...

  2. 【Spring框架】 ☞ 项目启动时执行特定处理及ApplicationListener源码分析

    1.背景 在一些业务场景中,在容器启动完成后,需要处理一些诸如:kafka业务注册,数据处理,初始化缓存等的操作. 本文重点介绍如何在服务启动中,或启动完成时执行相关处理. 2.针对上述场景,有如下实 ...

  3. 【Spring】Bean生命周期源码分析 总结

    [Spring]Bean生命周期源码总结 1.案例验证 定义两个bean A,B 以及实现MyBeanFactoryProcess,MyBeanProcessor,MyInstantiationAwa ...

  4. mybatis第十话 - mybaits整个事务流程的源码分析

    1.故事前因 在分析mybatis源码时一直带的疑问,一直以为事务是在SqlSessionTemplate#SqlSessionInterceptor#invoke完成的,直到断点才发现并不简单! 在 ...

  5. Vue + Spring Boot 项目实战(十五):动态加载后台菜单

    重要链接: 「系列文章目录」 「项目源码(GitHub)」 本篇目录 前言 一.后端实现 1.表设计 2.pojo 3.菜单查询接口(树结构查询) 二.前端实现 1.后台页面设计 2.数据处理 3.添 ...

  6. Spring Cloud实战小贴士:Ribbon的饥饿加载(eager-load)模式

    我们在使用Spring Cloud的Ribbon或Feign来实现服务调用的时候,如果我们的机器或网络环境等原因不是很好的话,有时候会发现这样一个问题:我们服务消费方调用服务提供方接口的时候,第一次请 ...

  7. Spring Cloud实战小贴士:Zuul的饥饿加载(eager-load)使用

    上一篇 我们介绍了如何使用Ribbon的 earger-load配置加速Spring Cloud中对服务接口的第一次调用.可是这样只是解决了内部服务间的调用,另外一个问题依然经常困扰我们,那就是网关到 ...

  8. java.lang.object源码_第三篇:java.lang.Object 类源码分析

    Object所包含的方法如下: ① public Object(); 构造函数: 大部分情况下,类对象的声明,都是通过构造函数完成的(Java中规定:在类定义过程中,对于未定义构造函数的类,默认会有一 ...

  9. kotlin 添加第一个 集合_Flutter开发必学Dart语法篇之集合操作符函数与源码分析...

    简述: 在上一篇文章中,我们全面地分析了常用集合的使用以及集合部分源码的分析.那么这一节讲点更实用的内容,绝对可以提高你的Flutter开发效率的函数,那就是集合中常用的操作符函数.这次说的内容的比较 ...

最新文章

  1. 文件操作2-Day3
  2. AI理论知识整理(16)-线性方程组有解
  3. Xcode6的新特性、iPhone6和iPhone6Plus的适配,xcode6iphone6
  4. producer send源码_RocketMq系列之Producer顺序消息发送源码分析(四)
  5. c#获取文件夹路径(转载)
  6. kafka中生产者和消费者的分区问题
  7. 利用BioPerl将DNA序列翻译成蛋白序列
  8. POJ 1066 Treasure Hunt(计算几何)
  9. 使用Movavi Video Editor如何做局部的影片放大特效
  10. ubuntu下安装2个mysql_Ubuntu下MySQL的手工安装
  11. 洛谷P2342 叠积木
  12. 戴德金--连续性和无理数--我自己做的中文翻译第11页
  13. 安装itunes需要管理员身份_iTunes安装失败 iTunes安装出错解决方法
  14. 世界观 - 经验事实和哲学性/概念性事实
  15. 360、腾讯、迅雷Windows编程、网络编程面试题及答案
  16. web学习笔记6 - TCP/IP五层协议簇
  17. linux数据库管理工具
  18. 什么是MyBatis
  19. GD32F130FXP6学习笔记六:cortex-m3系列的ADC初识
  20. 打码指南-由猫眼线下扫码1分购谈起 | 掘金直播 小程序专场分享总结

热门文章

  1. Facebook 宣布开源无线设备 打造全新5G世界
  2. 从JVM指令层面看try-catch-finally返回值问题
  3. JVM内存结构与GC
  4. 2016网易实习生编程题:n个骰子的和等于m
  5. 我用的 cordova 插件
  6. struct2(二) struct2的hello world 程序
  7. WCF 第十二章 对等网
  8. Windows XP with SP3 All Edtion Download
  9. C++17中那些值得关注的特性(上)
  10. golang基础-chan的select操作、定时器操作、超时控制、goroutine中使用recover