- 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的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
题: 是否有一种简单的方法可以获取正在运行的应用程序中泄漏的资源类型列表? IOW 通过连接到应用程序? 我知道 memproof 可以做到,但它会减慢速度,以至于应用程序甚至无法持续一分钟。大多数任
正确地说下面的代码会将自定义日志发送到.net核心中的Docker容器的stdout和stderr吗? console.Writeline(...) console.error(..) 最佳答案 如果
我想将一个任务多次重复,放入 for 循环中。我必须将时间序列对象存储为 IExchangeItem , openDA 中的一个特殊类(数据同化软件)。 这是任务之一(有效): HashMap ite
我需要从文件中读取一个数组。该数组在文件中不是连续排序的,必须跳转“偏移”字节才能获得下一个元素。假设我读取一个非常大的文件,什么更有效率。 1) 使用增量相对位置。 2)使用绝对位置。 选项 1:
我有一个安装程序(使用 Advanced Installer 制作)。我有一个必须与之交互的应用程序,但我不知道如何找到该安装的 MSIHANDLE。我查看了 Microsoft 引用资料,但没有发现
我在替换正则表达式中的“joe.”等内容时遇到问题。这是代码 var objects = new Array("joe","sam"); code = "joe.id was here so was
我有 A 类。A 类负责管理 B 对象的生命周期,它包含 B 对象的容器,即 map。 ,每个 B 对象都包含 C 对象的容器,即 map .我有一个全局 A 对象用于整个应用程序。 我有以下问题:我
任何人都可以告诉我在哪里可以找到 freeImage.so 吗?我一直在努力寻找相同的东西但没有成功..任何帮助将不胜感激。我已经尝试将 freeimage.a 转换为 freeImage .so 并
在单元测试期间,我想将生成的 URL 与测试中定义的静态 URL 进行比较。对于此比较,最好有一个 TestCase.assertURLEqual 或类似的,它可以让您比较两个字符串格式的 URL,如
'find ./ -name *.jpg' 我正在尝试优化上述语句的“查找”命令。 在查找实现中处理“-name”谓词的方法。 static boolean pred__name __common (
请原谅我在这里的困惑,但我已经阅读了关于 python 中的 seek() 函数的文档(在不得不使用它之后),虽然它帮助了我,但我仍然对它的实际含义有点困惑,任何非常感谢您的解释,谢谢。 最佳答案 关
我在我正在使用的库中找到了这个语句。它应该检查集群中的当前节点是否是领导者。这是语句:(!(cluster.Leader?.IsRemote ?? true)) 为什么不直接使用 (cluster.L
我发现 JsonParser 在 javax.json.stream 中,但我不知道在哪里可以找到它。谁能帮帮我? https://docs.oracle.com/javaee/7/api/javax
关闭。这个问题需要更多focused .它目前不接受答案。 想改善这个问题吗?更新问题,使其仅关注一个问题 editing this post . 6年前关闭。 Improve this questi
如果 git 存储库中有新的更改可用,我有一个多分支管道作业设置为每分钟由 Jenkinsfile 构建。如果分支名称是某种格式,我有一个将工件部署到环境的步骤。我希望能够在每个分支的基础上配置环境,
关闭。这个问题不满足Stack Overflow guidelines .它目前不接受答案。 想改善这个问题吗?更新问题,使其成为 on-topic对于堆栈溢出。 6年前关闭。 Improve thi
我想我刚刚意识到当他们不让我使用 cfdump 时我的网络主机是多么的限制。这其实有点让我生气,真的,dump 有什么害处?无论如何,我的问题是是否有人编写了一个 cfdump 替代方案来剔除复杂类型
任务:我有多个资源需要在一个 HTTP 调用中更新。 要更新的资源类型、字段和值对于所有资源都是相同的。 示例:通过 ID 设置了一组汽车,需要将所有汽车的“状态”更新为“已售出”。 经典 RESTF
场景:表中有 2 列,数据如下例所示。对于“a”列的相同值,该表可能有多个行。 在示例中,考虑到“a”列,“1”有三行,“2”有一行。 示例表“t1”: |a|b ||1|1.1||1|1.2||1
我有一个数据框: Date Price 2021-01-01 29344.67 2021-01-02 32072.08 2021-01-03 33048.03 2021-01-04 32084.
我是一名优秀的程序员,十分优秀!