Spring Boot 启动流程详解(二)
来源:互联网 发布:网络大学 日语专业 编辑:程序博客网 时间:2024/05/28 19:24
Spring Boot 启动流程详解(二)
使用SpringBoot已经有好几个月了,一直对框架当中Bean的实例化过程有很感兴趣,比如Spring是如何去加载.class文件,并解析其中我们通过注解@Configuration, @PropertySource,@ComponentScan,@Import,@Bean等产生我们自定义的Bean的。下面我们就来看看,框架当中是如何实现Bean的加载与创建的。
下面是SpringApplication函数的run函数:
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; FailureAnalyzers analyzers = null; configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); Banner printedBanner = printBanner(environment); context = createApplicationContext(); analyzers = new FailureAnalyzers(context); prepareContext(context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); afterRefresh(context, applicationArguments); listeners.finished(context, null); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } return context; } catch (Throwable ex) { handleRunFailure(context, listeners, analyzers, ex); throw new IllegalStateException(ex); } }
注意,在完成了context的创建之后,会调用refreshContext函数,refreshContext函数中会调用refresh函数,在该函数中又会先调用祖先类AbstractApplicationContext类的refresh函数。该函数如下:
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. // 初始化BeanFactory ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. // 准备工厂后处理器的使用 prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. //在子类中注册BeanFactory后处理器 postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. //调用工厂后处理器 invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. //注册Bean后处理器 registerBeanPostProcessors(beanFactory); // Initialize message source for this context. // 初始化信息源 initMessageSource(); // Initialize event multicaster for this context. // 初始化应用上下文时间广播 initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. // 初始化其他特殊的Bean,有具体的子类来实现 onRefresh(); // Check for listener beans and register them. // 注册事件监听器 registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. // 初始化所有非懒加载的单例Bean finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. // 完成刷新并发布容器刷新事件 finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
在函数调用过程当中,调用了函数invokeBeanFactoryPostProcessors,在该函数的调用流程当中,调用链为PostProcessorRegistrationDelegate ->ConfigurationClassPostProcessor -> ConfigurationClassParser,在类ConfigurationClassParser类的doProcessConfigurationClass函数中,将启动类(即@SpringBootApplication标注的类,主要是@SpringBootApplication注解中的@Configuration注解的作用)做为入口,读取我们所定义的Bean,将其转化为一个个的BeanDefinition。下面,我们来看一下该函数的定义:
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { // Recursively process any member (nested) classes first processMemberClasses(configClass, sourceClass); // Process any @PropertySource annotations for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) { if (this.environment instanceof ConfigurableEnvironment) { processPropertySource(propertySource); } else { logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment"); } } // Process any @ComponentScan annotations Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { for (AnnotationAttributes componentScan : componentScans) { // The config class is annotated with @ComponentScan -> perform the scan immediately Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // Check the set of scanned definitions for any further config classes and parse recursively if needed for (BeanDefinitionHolder holder : scannedBeanDefinitions) { if (ConfigurationClassUtils.checkConfigurationClassCandidate( holder.getBeanDefinition(), this.metadataReaderFactory)) { parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName()); } } } } // Process any @Import annotations processImports(configClass, sourceClass, getImports(sourceClass), true); // Process any @ImportResource annotations if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) { AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); String[] resources = importResource.getStringArray("locations"); Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader"); for (String resource : resources) { String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); configClass.addImportedResource(resolvedResource, readerClass); } } // Process individual @Bean methods Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass); for (MethodMetadata methodMetadata : beanMethods) { configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } // Process default methods on interfaces processInterfaces(configClass, sourceClass); // Process superclass, if any if (sourceClass.getMetadata().hasSuperClass()) { String superclass = sourceClass.getMetadata().getSuperClassName(); if (!superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) { this.knownSuperclasses.put(superclass, configClass); // Superclass found, return its annotation metadata and recurse return sourceClass.getSuperClass(); } } // No superclass -> processing is complete return null; }
在该代码块中我们可以看到,函数首先处理由注解@PropertySource,该注解用于将我们自定义的属性文件加载到上下文环境当中。然后对@ComponentScan注解定义的包结构进行扫描,将启动类所在包下的子包中Bean文件(不包括子文件夹中的Bean文件)定义转化为BeanDefinition,如果该Bean定义的文件中也使用了@Configuration对其进行了注解,那么将会进行递归。之后,解析由注解@Import和@ImportResource注解导入的Bean定义文件,最后是直接由@Bean注解的成员函数。之后所有需要实例化的Bean都已经完成了解析,都转化为BeanDefinition,将由refresh函数中的finishBeanFactoryInitialization根据这些信息对Bean进行实例化。
- Spring Boot 启动流程详解(二)
- Spring Boot启动流程详解(一)
- Spring Boot 启动流程详解(一)
- Spring Boot启动流程
- Spring boot启动运行流程
- Spring Boot Web启动流程
- 【Spring Boot】SpringBoot-启动流程分析
- U-BOOT启动流程之二
- Spring Boot学习(二):Spring Boot的启动器Starter详解
- spring boot用到的注解详解(二)
- 98. Spring Boot启动流程分析第二篇
- 1.spring-boot启动流程以及IOC容器创建
- (二)u-boot启动流程分析(汇编部分)
- u-boot启动流程
- u-boot启动流程
- u-boot启动流程
- u-boot启动流程
- u-boot启动流程
- POJ2104 K-th Number —— 划分树(模板题)
- Oracle内存全面分析 1
- 编解码器框架
- 从零开始,手把手教你如何在Ubuntu下编译VLC-Android源码
- node.js中的exports
- Spring Boot 启动流程详解(二)
- rtsp协议总结
- Oracle内存全面分析 2
- RAID、LVM(逻辑卷)的相关用法
- 阿里巴巴2017年秋招(测试开发工程师)编程题:组队问题
- [欧拉路] Codeforces Round #407 (Div. 1) 788B. Weird journey
- c++容器迭代器失效问题
- .NET Core 2.0迁移技巧之MemoryCache问题修复
- Oracle内存全面分析 3