- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
<? xml version="1.0" encoding="UTF-8" ?> < project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > < modelVersion > 4.0.0 </ modelVersion > < groupId > org.example </ groupId > < artifactId > SpringBootStudy </ artifactId > < version > 1.0-SNAPSHOT </ version > < properties > < maven.compiler.source > 8 </ maven.compiler.source > < maven.compiler.target > 8 </ maven.compiler.target > < project.build.sourceEncoding > UTF-8 </ project.build.sourceEncoding > </ properties > < dependencies > < dependency > < groupId > org.springframework.boot </ groupId > < artifactId > spring-boot-starter-web </ artifactId > < version > 2.7.14 </ version > </ dependency > </ dependencies > </ project >
package com.springboot.study; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Main { public static void main(String[] args) { SpringApplication springApplication = new SpringApplication(Main. class ); springApplication.run(args); } }
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?> [] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); // Use names and ensure unique to protect against duplicates Set<String> names = new LinkedHashSet<> (SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List <T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { ClassLoader classLoaderToUse = classLoader; if (classLoaderToUse == null ) { classLoaderToUse = SpringFactoriesLoader. class .getClassLoader(); } String factoryTypeName = factoryType.getName(); return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList()); }
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) { Map <String, List<String>> result = cache.get(classLoader); if (result != null ) { return result; } result = new HashMap<> (); try { Enumeration <URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION); while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<?, ?> entry : properties.entrySet()) { String factoryTypeName = ((String) entry.getKey()).trim(); String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String) entry.getValue()); for (String factoryImplementationName : factoryImplementationNames) { result.computeIfAbsent(factoryTypeName, key -> new ArrayList<> ()) .add(factoryImplementationName.trim()); } } } // Replace all lists with unmodifiable lists containing unique elements result.replaceAll((factoryType, implementations) -> implementations.stream().distinct() .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList))); cache.put(classLoader, result); } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]" , ex); } return result; }
这个地方会扫描项目中 resource 目录下的 META-INF/spring.factories 文件,默认如果不添加其他依赖,会扫描到如下项目:
最终会扫描到如下对象:
public ConfigurableApplicationContext run(String... args) { long startTime = System.nanoTime(); DefaultBootstrapContext bootstrapContext = createBootstrapContext(); ConfigurableApplicationContext context = null ; configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(bootstrapContext, this .mainApplicationClass); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); context = createApplicationContext(); context.setApplicationStartup( this .applicationStartup); prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); afterRefresh(context, applicationArguments); Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime); if ( this .logStartupInfo) { new StartupInfoLogger( this .mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup); } listeners.started(context, timeTakenToStartup); callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, listeners); throw new IllegalStateException(ex); } try { Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime); listeners.ready(context, timeTakenToReady); } catch (Throwable ex) { handleRunFailure(context, ex, null ); throw new IllegalStateException(ex); } return context; }
1、首先是 createBootstrapContext 方法,该方法会调用 第一个扩展点 : BootstrapRegistryInitializer->initializer ,但是项目中没有该接口的实现类.
2、其次就是 listeners.starting 方法, 该方法中会调用 第二个扩展点 SpringApplicationRunListener->starting ,这个 SpringApplicationRunListener 项目中只存在一个实现类:EventPublishingRunListener,它会触发所有的 ApplicationListener 监听 ApplicationStartingEvent 的事件,后文就不特别声明这个实现类了.
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) { MutablePropertySources sources = environment.getPropertySources(); if (!CollectionUtils.isEmpty( this .defaultProperties)) { DefaultPropertiesPropertySource.addOrMerge( this .defaultProperties, sources); } if ( this .addCommandLineProperties && args.length > 0 ) { String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME; if (sources.contains(name)) { PropertySource <?> source = sources.get(name); CompositePropertySource composite = new CompositePropertySource(name); composite .addPropertySource( new SimpleCommandLinePropertySource("springApplicationCommandLineArgs" , args)); composite.addPropertySource(source); sources.replace(name, composite); } else { sources.addFirst( new SimpleCommandLinePropertySource(args)); } } }
3、然后就是 prepareEnvironment 方法,该方法会调用 第三个扩展点:SpringApplicationRunListener->environmentPrepared ,它会触发所有的 ApplicationListener 监听 ApplicationEnvironmentPreparedEvent 的事件.
private Banner getBanner(Environment environment) { Banners banners = new Banners(); banners.addIfNotNull(getImageBanner(environment)); banners.addIfNotNull(getTextBanner(environment)); if (banners.hasAtLeastOneBanner()) { return banners; } if ( this .fallbackBanner != null ) { return this .fallbackBanner; } return DEFAULT_BANNER; }
4、 第四个扩展点 在 prepareContext->applyInitializers 方法里, ApplicationContextInitializer->initialize .
5、 第五个扩展点 在 prepareContext->listeners.contextPrepared 方法里, SpringApplicationRunListeners->contextPrepared ,它会触发所有的 ApplicationListener 监听 ApplicationContextInitializedEvent 的事件.
6、 第六个扩展点 在 prepareContext->bootstrapContext.close 方法里,它会触发所有的 ApplicationListener 监听 BootstrapContextClosedEvent 的事件.
public Set<Object> getAllSources() { Set <Object> allSources = new LinkedHashSet<> (); if (!CollectionUtils.isEmpty( this .primarySources)) { allSources.addAll( this .primarySources); } if (!CollectionUtils.isEmpty( this .sources)) { allSources.addAll( this .sources); } return Collections.unmodifiableSet(allSources); }
private void load(Object source) { Assert.notNull(source, "Source must not be null" ); if (source instanceof Class<?> ) { load((Class <?> ) source); return ; } if (source instanceof Resource) { load((Resource) source); return ; } if (source instanceof Package) { load((Package) source); return ; } if (source instanceof CharSequence) { load((CharSequence) source); return ; } throw new IllegalArgumentException("Invalid source type " + source.getClass()); }
这个地方会从 SpringApplication 中的 primarySources、sources 加载源,然后注册为 Bean.
7、 第七个扩展点 在 prepareContext->listeners.contextLoaded 方法里, SpringApplicationRunListeners ->contextLoaded ,会触发 ApplicationListener 监听 ApplicationPreparedEvent 的事件.
@Override public void refresh() throws BeansException, IllegalStateException { synchronized ( this .startupShutdownMonitor) { StartupStep contextRefresh = this .applicationStartup.start("spring.context.refresh" ); // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. 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. postProcessBeanFactory(beanFactory); StartupStep beanPostProcess = this .applicationStartup.start("spring.context.beans.post-process" ); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); beanPostProcess.end(); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. 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(); contextRefresh.end(); } } }
这里我们直接看到 invokeBeanFactoryPostProcessors 方法,不过这个方法蛮长的,就先看一部分:
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) { if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) { BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor) postProcessor; registryProcessor.postProcessBeanDefinitionRegistry(registry); registryProcessors.add(registryProcessor); } else { regularPostProcessors.add(postProcessor); } }
这个地方可以猜到这个 postProcessBeanDefinitionRegistry 也是一个扩展点,但是这个 beanFactoryPostProcessors 的值是从哪里来的呢?
打断点这里是有三个值的:
这里直接说答案,还记得这段代码吗:
context = createApplicationContext(); context.setApplicationStartup( this .applicationStartup); prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
这里获取了 context 之后,就调用了 prepareContext 方法,prepareContext 方法里曾提到过有 第4、第5、第6、第七个扩展点 .
举个例子,就拿第四个扩展点说:
ConfigurationWarningsApplicationContextInitializer 实现了 ApplicationContextInitializer 接口,然后添加了一个值:
@Override public void initialize(ConfigurableApplicationContext context) { context.addBeanFactoryPostProcessor( new ConfigurationWarningsPostProcessor(getChecks())); }
相同的还有 SharedMetadataReaderFactoryContextInitializer,至于第三个值则是直接在 prepareContext 方法里添加的.
8、 第八个扩展点 在 refresh->invokeBeanFactoryPostProcessors 方法里,会调用 invokeBeanDefinitionRegistryPostProcessors .
紧接着,就是如下代码:
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor. class , true , false ); for (String ppName : postProcessorNames) { if (beanFactory.isTypeMatch(ppName, PriorityOrdered. class )) { currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor. class )); processedBeans.add(ppName); } }
我们看看这个 postProcessorNames 是怎么来的,追踪到 beanFactory.getBeanNamesForType 方法,进去可以看到 doGetBeanNamesForType 方法,总而言之,是从 beanDefinitionNames 和 manualSingletonNames 的值来的,那这些值又是怎么来的呢,仍然是如下一段代码:
context = createApplicationContext(); context.setApplicationStartup( this .applicationStartup); prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
在创建 context 的时候,实际上是调用 AnnotationConfigApplicationContext 的构造方法,里面会调用 registerAnnotationConfigProcessors 方法,这个方法会初始化 5 个 beanDefinitionName.
然后就是 prepareContext 方法里,在第四个扩展点会初始化两个 manualSingletonName,prepareContext 方法里的 registerSingleton 方法也添加了两个 manualSingletonName,并且后面的 load 方法也添加了一个 main 的 beanDefinitionName,在最后的第七个扩展点里又添加了 3 个 manualSingletonName,总之在执行 refreshContext 之前,context 的 beanFactory 里包含 6 个 beanDefinitionName 7 个 manualSingletonName,后面的就不分析了,毕竟能扩展的就在这里.
9、 第九个扩展点 也在 refresh->invokeBeanFactoryPostProcessors 方法里,会调用 invokeBeanFactoryPostProcessors .
不过有一点要注意的是,虽说第八个、第九个也算是扩展点,但其只有在第四到第七个扩展点里面配置了才能进行扩展.
@Override protected void onRefresh() { super .onRefresh(); try { createWebServer(); } catch (Throwable ex) { throw new ApplicationContextException("Unable to start web server" , ex); } }
该方法会调用 createWebServer 创建一个 webserver.
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // Initialize conversion service for this context. if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) && beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService. class )) { beanFactory.setConversionService( beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService. class )); } // Register a default embedded value resolver if no BeanFactoryPostProcessor // (such as a PropertySourcesPlaceholderConfigurer bean) registered any before: // at this point, primarily for resolution in annotation attribute values. if (! beanFactory.hasEmbeddedValueResolver()) { beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal)); } // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early. String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware. class , false , false ); for (String weaverAwareName : weaverAwareNames) { getBean(weaverAwareName); } // Stop using the temporary ClassLoader for type matching. beanFactory.setTempClassLoader( null ); // Allow for caching all bean definition metadata, not expecting further changes. beanFactory.freezeConfiguration(); // Instantiate all remaining (non-lazy-init) singletons. beanFactory.preInstantiateSingletons(); }
这个方法会调用 beanFactory,实例化所有的 bean.
10、 第十个扩展点 在 refresh->finishRefresh 方法里,会调用 getLifecycleProcessor().onRefresh(),会调用 SmartLifecycle->start 方法.
11、 第十一个扩展点也 在 refresh->finishRefresh 方法里,会调用 publishEvent 然后触发 ApplicationListener 监听 ContextRefreshedEvent 的事件.
12、 第十二个扩展点 在 run 方法里,会调用 listeners.started 方法, SpringApplicationRunListener->started .
13、 第十三个扩展点 也在 run 方法里,会调用 callRunners 方法, ApplicationRunner 或 CommandLineRunner 的 run 方法 .
14、 第十四个扩展点也 在 run 方法里,会调用 listeners.ready方法, SpringApplicationRunListener->ready .
第一篇分析 SpringBoot 启动源码到这里就结束了,我们这次的目标是找到 SpringBoot 有哪些地方可以自己进行代码扩展的,其中不免有些遗漏的,欢迎各位补充.
目前看完还是有很多问题,比如 SpringBootApplication 注解的作用是什么、Bean 的实例化流程是什么、以及我们 Web 的 URL 请求是如何到方法上的...等等.
最后,这篇分析完的这些扩展点能干些什么呢?
最后此篇关于SpringBoot启动流程分析(寻找扩展点)的文章就讲到这里了,如果你想了解更多关于SpringBoot启动流程分析(寻找扩展点)的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
每当我运行命令以将 Virtualbox 驱动程序启动 Minishift 到操作系统主机时,它都需要一段疯狂的时间,而且它永远不会结束。有时我什至收到有关达到存储限制的错误消息。 不知道是不是描述h
您好,我正在使用 npm 运行一个基本的 React 项目,我正尝试在 docker 容器中启动它。但是我实际上无法让项目运行。我的 dockerfile 看起来像这样: FROM node:7.8.
所以我想从我的 SSH 终端开始游戏。 这真的很奇怪,当我直接从 Linux GUI 执行此操作时,它可以工作。但是当我使用 SSH 客户端进行远程连接时,它就崩溃了。似乎与我的显示驱动程序有关。 U
我有一个显示图像的动态壁纸。我在 Activity 中更改了该图像。然后我需要通知动态壁纸,以便它知道重新加载资源。 Intent 似乎是完美、简单的解决方案: Intent intent = new
我有一个似乎无法解决的问题。我在 Boot Dashboard 中使用 STS 3.9.2 从 Eclipse (Oxygen) 启动 Spring Boot 应用程序没有任何问题: 但是,当我尝试从
全新的 Python,在我开始摆弄东西之前先设置和安装东西。我的理解是 Python 2.7 和 Python 3.3 之间存在一些显着差异/不兼容,尽管这两个版本都得到了很好的使用,所以我认为最好安
在使用了很长时间的 jQuery 之后,我有一个问题,我正在使用 jQuery 模式(样式)编写一个简单的代码, (function(window, undefined) { var jQu
我正在尝试在 spring boot 应用程序下的非 spring 托管类中配置 Autowired。我在 tomcat 服务器下部署的 Web 应用程序下成功运行了这个。但是当我想在 spring
我对 xmonad 完全陌生,但我想开始使用它来提高我的工作效率。 这是我一直在使用的指南(我使用的是 Apple OS X Snow Leopard) http://xmonad.org/tour.
我试图将Spring Boot指南中的Managing Transactions示例扩展到两个数据源,但是@Transaction注释似乎仅对其中一个数据源有效。 在“Application.java
conEmu 有没有办法默认打开多个不同的选项卡? 我看到这个页面解释了如何使用 splits , 我意识到我可以按 Ctrl + T, 1, Enter,但我希望有一种方法可以自动执行此操作! "%
我正在寻找快速而肮脏的答案。我当时脑子一片空白,盯着屏幕看了 12 个小时以上,我想我中枪了。 我想做一个简单的 SignalR 应用程序作为教程。我找到了这个example ,但我不断收到票证未定义
我正在使用 Azure Powershell cmdlet 来启动/停止 VM。 Start-AzureVM [-ServiceName] [-Name] [ ] Stop-AzureVM [-S
我想使用Powershell脚本代码启动/停止iis和mssql 意味着当我运行ps脚本时,我想启动/停止iis和mssql 我在网上搜索了它,发现了一些代码,但按照我的要求无法正常工作 码: $ii
我在 liferay 工作。我们在我们的项目中使用一个模块来创建 liferay 主题。我使用命令 ant -Ddeploy.war=true 将它部署在服务器中。 war 文件在 liferay 部
我想在已安装 Python 2.7 的 Windows XP 计算机上运行 IPython(版本 0.12)。 我通过 Windows 二进制安装程序安装,但安装后 IPython 没有显示在菜单中,
我从创建了自己的简单图片。 FROM python:2.7.11 RUN mkdir /extra/later/ \ && mkdir /yyy 现在,我可以执行以下步骤: docker run
$(document).ready(function () { setTimeout(function() { window.location.reload(); }, 2000); // 2
我刚刚创建了一个帐户 OpenWeatherMap 我想通过城市 ID API 调用获取当前位置的天气: http://api.openweathermap.org/data/2.5/weather?
我注意到,如果我更改 xcasset 中的图像,启动 Storyboard不会更新。 例如,假设您的启动 Storyboard中有一个 UIImage View ,其中包含一个名为“logo”的蓝色图
我是一名优秀的程序员,十分优秀!