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

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】
免费领取 2000+ 道 Java 面试题

前言

前面的文章描述了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名称,其信息为:

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

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


来源:[]()

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » spring boot实战(第十篇)Spring boot Bean加载源码分析

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏