皮皮网
皮皮网

【jdk的源码在】【源码分享网开源】【牛b支付源码】webapp启动源码_web应用源码

时间:2024-12-23 22:48:47 来源:网课源码开发

1.springboot启动类原理?
2.滴滴 Web 移动端组件库 cube-ui 开源

webapp启动源码_web应用源码

springboot启动类原理?

       SpringbootBatch的启动原理-Configuration

       Springboot整合了web和batch,但是他们肯定不是同一条路,在springboot中,会推断当前的运行环境。this.webApplicationType=WebApplicationType.deduceFromClasspath();

       ä»Žä¸Šæ–‡å¯ä»¥çœ‹å‡ºï¼ŒSpring尝试从classpath里找到特征类,来判断当前app是什么类型。当然这种判断是有局限性的,有可能是transitive带进来一个带有servlet的类被当成了WebApplicationType.SERVLET,实际上是个WebApplicationType.NONE;。如果不想以web运行就是想运行batch可以在application.properties强行指定WebApplicationType

       å…·ä½“发生作用的请看下面的stacktrace

       å½“一个batchapplication需要启动,需要配置JobRepository,Datasource等等,所有的开始都来自一个annotation@EnableBatchProcessing

       å½“加入@EnableBatchProcessing时,BatchConfigurationSelector开始启动,怎么启动的大家可以参考下面的stacktrace。

       import类主要是由ConfigurationClassPostProcessor来实现的。当BatchConfigurationSelector被调用的时候,我们可以看到他有两条支路。

       é‚£ä¹ˆè¿™ä¸¤æ¡è·¯æœ‰å•¥ä¸åŒå‘¢ã€‚主要是job定义的方式不同。

       modular=true的情况下,下面是一个例子

       å¯ä»¥æœ‰å¤šä¸ªå­ApplicationContextFactory,这样好处是在除了job大家不可以重复,因为是在不同的context里,其他的step,reader,writer,processor,mapper,以及所有的bean等等都可以重名。

       é‚£ä¸ºä»€ä¹ˆJob不可以重复,是因为虽然可以重复,但是如果job也重复,对用户来讲太不友好了。用户可能不知道自己配的是哪个context的job。具体为什么可以重名要看看GenericApplicationContextFactory的实现。

       å½“GenericApplicationContextFactory::createApplicationContext,启动会触发ApplicationContextHelper的构造函数从而调用loadConfiguration(config)把定义的bean加入到context里。那么有个问题,parent在哪里设置,createApplicationContext是什么时候调用的。

       æˆ‘们继续看ModularConfiguration

       ä»ŽModular的角度来看首先他可以外部注入一个Configurer,如果没有就选择DefaultBatchConfigurer,如果有多个选择则会抛出。

       å½“然Datasource也可以选择外部注入,或者由DefaultBatchConfigurer::initialize方法SimpleJobLauncher,JobRepository和JobExplorer都是由FactoryBean的方式实现的。

       åœ¨è¿™ä¸ªDefaultBatchConfigurer中可以看到JobLauncher的类型是

       initialize里MapJobRepositoryFactoryBean这个可以重点读一下。首先用FactoryBean的模式实现了一个ProxyBean,如果想了解FactoryBean的用法,这是个典型的例子。但是这个FactoryBean是以api行为直接调用的,并没有注册到Spring的context中。

       é…ç½®å¥½job需要的jobRepository,jobLauncher等那么重点来了,下面的AutomaticJobRegistrar就是来处理ApplicationContextFactory

       è¿™ä¸ªé€»è¾‘是把所有的ApplicationContextFactory的beaninstance放入到AutomaticJobRegistrar里去。这就回到了第一个问题,parentcontext是什么时候放进去的。就是在

       context.getBeansOfType(ApplicationContextFactory.class)请看下面的调用栈,在ApplicationContextAwareProcessor里会自动把parentcontext注入。

       ä½†æ˜¯æ”¾è¿›åŽ»å•¥æ—¶å€™ç”¨å‘¢ï¼Ÿæˆ‘们看一下AutomaticJobRegistrar

       åŽŸæ¥AutomaticJobRegistrar是个Smartlifecycle,从Smartlifecycle的细节可以从SpringbootSmartlifecycle来得知。它就是在所有bean都初始化结束后开始进行的一个阶段。在这个start方法中,开始遍历所有的ApplicationContextFactory,来进行加载。从上文这个jobLoader是DefaultJobLoader。

       é‚£ä¹ˆå¯ä»¥çœ‹çœ‹DefaultJobLoader::doLoad方法

       è¿™é‡Œæœ‰å‡ ä¸ªå…³é”®è°ƒç”¨ç¬¬ä¸€ä¸ªæ˜¯createApplicationContext,把context里定义的全部加载到Springcontext里去,这就满足了GenericApplicationContextFactory工作的两个条件。第二个是doRegister(context,job)里jobRegistry.register(jobFactory);

       æˆ‘们看一下JobRepository的实现MapJobRegistry::register方法,在这里就把jobname和jobFactory的键值对存储起来了。

       è¿™æ ·å°±å¯ä»¥é€šè¿‡JobRepository这个bean拿到所有注册的job了。

       å’±ä»¬å†å›žæ¥çœ‹@EnableBatchProcessing这个annotation,当没有设定modular的时候是比较简单的,只是实现了一个proxybased的Job的bean。

       åŒæ ·ä¹Ÿåªæœ‰BatchConfigurer来配置。这个逻辑和Modular是一样的。从这两个配置知道,Modular注册了在子context的配置,并且加载。但是当以正常bean的方式存在的,是怎么读进来的呢。这时候就要看JobRegistryBeanPostProcessor

       è¿™ä¸ªpostProcessAfterInitialization方法里,对每个job类型的bean,jobRegistry加入了ReferenceJobFactory。这样所有的以bean的方式定义的都可以通过jobRegistry获得。

SpringBootStater原理

       ä¸€.SpringBoot的好处

       1.依赖管理:可插拔式的组件管理,当需要某个组件时,只需要引入相关stater即可,不需要再手动引入各个jar包,避免了包遗漏、包冲突等不必要的问题。开发人员可以专注于业务开发,

       2.自动配置:遵从"约定优于配置"的原则,开发人员可以在少量配置或者不配置的情况下,使用某组件。

       å¤§å¤§é™ä½Žé¡¹ç›®æ­å»ºåŠç»„件引入的成本,开发人员可以专注于业务开发,避免繁杂的配置和大量的jar包管理。

       äºŒ.实现原理

       è¦å¼•å…¥æŸç»„件,无非要做两件事。一是引入jar包即pom文件引入stater;二就是编写配置文件,使用Java配置的情况下就是编写一系列@Configuration注解标注的类。那么SpringBoot是怎么来引入这些配置类的呢?就需要我们深入SpringBoot启动类一探究竟。

       SpringBoot启动类上面会有@SpringBootApplication注解,这是SpringBoot中最重要的一个注解,是实现自动配置的关键。@SpringBootApplication是一个租合注解,主要由@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan三部分组成。

@SpringBootConfiguration表明该类是一个配置类。

@EnableAutoConfiguration由@AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class)组成。@AutoConfigurationPackage由@Import(AutoconfigurationPackages.Registrar.class)组成,向Bean容器中注册一个AutoConfigurationPackages类,该类持有basePackage,目前我发现的作用是在MyBatis扫描注册Mapper时作为包扫描路径。

       @AutoConfigurationPackage的执行流程如下图:

@Import(AutoConfigurationImportSelector.class)是启动自动配置的核心。这里还有一个小插曲,一直在看AutoConfigurationImportSelector的selectImports方法,却发现没有被调用,以为是demo项目问题,去真实项目中debug断点,发现也没有进入,瞬间懵逼。。。继续跟踪发现执行流程如下:

       SpringBoot启动原理分析

       è‡ªåŠ¨é…ç½®æ ¸å¿ƒç±»SpringFactoriesLoader

       ä¸Šé¢åœ¨è¯´@EnableAutoConfiguration的时候有说META-INF下的spring.factories文件,那么这个文件是怎么被spring加载到的呢,其实就是SpringFactoriesLoader类。

       SpringFactoriesLoader是一个供Spring内部使用的通用工厂装载器,SpringFactoriesLoader里有两个方法,

       åœ¨è¿™ä¸ªSpringBoot应用启动过程中,SpringFactoriesLoader做了以下几件事:

       åŠ è½½æ‰€æœ‰META-INF/spring.factories中的Initializer

       åŠ è½½æ‰€æœ‰META-INF/spring.factories中的Listener

       åŠ è½½EnvironmentPostProcessor(允许在Spring应用构建之前定制环境配置)

       æŽ¥ä¸‹æ¥åŠ è½½Properties和YAML的PropertySourceLoader(针对SpringBoot的两种配置文件的加载器)

       å„种异常情况的FailureAnalyzer(异常解释器)

       åŠ è½½SpringBoot内部实现的各种AutoConfiguration

       æ¨¡æ¿å¼•æ“ŽTemplateAvailabilityProvider(如Freemarker、Thymeleaf、Jsp、Velocity等)

       æ€»å¾—来说,SpringFactoriesLoader和@EnableAutoConfiguration配合起来,整体功能就是查找spring.factories文件,加载自动配置类。

       æ•´ä½“启动流程

       åœ¨æˆ‘们执行入口类的main方法之后,运行SpringApplication.run,后面new了一个SpringApplication对象,然后执行它的run方法。

       åˆå§‹åŒ–SpringApplicationç±»

       åˆ›å»ºä¸€ä¸ªSpringApplication对象时,会调用它自己的initialize方法

       æ‰§è¡Œæ ¸å¿ƒrun方法

       åˆå§‹åŒ–initialize方法执行完之后,会调用run方法,开始启动SpringBoot。

       é¦–先遍历执行所有通过SpringFactoriesLoader,在当前classpath下的META-INF/spring.factories中查找所有可用的SpringApplicationRunListeners并实例化。调用它们的starting()方法,通知这些监听器SpringBoot应用启动。

       åˆ›å»ºå¹¶é…ç½®å½“前SpringBoot应用将要使用的Environment,包括当前有效的PropertySource以及Profile。

       éåŽ†è°ƒç”¨æ‰€æœ‰çš„SpringApplicationRunListeners的environmentPrepared()的方法,通知这些监听器SpringBoot应用的Environment已经完成初始化。

       æ‰“印SpringBoot应用的banner,SpringApplication的showBanner属性为true时,如果classpath下存在banner.txt文件,则打印其内容,否则打印默认banner。

       æ ¹æ®å¯åŠ¨æ—¶è®¾ç½®çš„applicationContextClass和在initialize方法设置的webEnvironment,创建对应的applicationContext。

       åˆ›å»ºå¼‚常解析器,用在启动中发生异常的时候进行异常处理(包括记录日志、释放资源等)。

       è®¾ç½®SpringBoot的Environment,注册SpringBean名称的序列化器BeanNameGenerator,并设置资源加载器ResourceLoader,通过SpringFactoriesLoader加载ApplicationContextInitializer初始化器,调用initialize方法,对创建的ApplicationContext进一步初始化。

       è°ƒç”¨æ‰€æœ‰çš„SpringApplicationRunListeners的contextPrepared方法,通知这些Listener当前ApplicationContext已经创建完毕。

       æœ€æ ¸å¿ƒçš„一步,将之前通过@EnableAutoConfiguration获取的所有配置以及其他形式的IoC容器配置加载到已经准备完毕的ApplicationContext。

       è°ƒç”¨æ‰€æœ‰çš„SpringApplicationRunListener的contextLoaded方法,加载准备完毕的ApplicationContext。

       è°ƒç”¨refreshContext,注册一个关闭Spring容器的钩子ShutdownHook,当程序在停止的时候释放资源(包括:销毁Bean,关闭SpringBean的创建工厂等)

       æ³¨ï¼šé’©å­å¯ä»¥åœ¨ä»¥ä¸‹å‡ ç§åœºæ™¯ä¸­è¢«è°ƒç”¨ï¼š

       1)程序正常退出

       2)使用System.exit()

       3)终端使用Ctrl+C触发的中断

       4)系统关闭

       5)使用Killpid命令杀死进程

       èŽ·å–当前所有ApplicationRunner和CommandLineRunner接口的实现类,执行其run方法

       éåŽ†æ‰€æœ‰çš„SpringApplicationRunListener的finished()方法,完成SpringBoot的启动。

SpringBoot应用启动原理(二)扩展URLClassLoader实现嵌套jar加载

       åœ¨ä¸Šç¯‡æ–‡ç« ã€ŠSpringBoot应用启动原理(一)将启动脚本嵌入jar》中介绍了SpringBoot如何将启动脚本与RunnableJar整合为ExecutableJar的原理,使得生成的jar/war文件可以直接启动

       æœ¬ç¯‡å°†ä»‹ç»SpringBoot如何扩展URLClassLoader实现嵌套jar的类(资源)加载,以启动我们的应用。

       é¦–先,从一个简单的示例开始

       build.gradle

       WebApp.java

       æ‰§è¡Œgradlebuild构建jar包,里面包含应用程序、第三方依赖以及SpringBoot启动程序,其目录结构如下

       æŸ¥çœ‹MANIFEST.MF的内容(MANIFEST.MF文件的作用请自行GOOGLE)

       å¯ä»¥çœ‹åˆ°ï¼Œjar的启动类为org.springframework.boot.loader.JarLauncher,而并不是我们的com.manerfan.SpringBoot.theory.WebApp,应用程序入口类被标记为了Start-Class

       jar启动并不是通过应用程序入口类,而是通过JarLauncher代理启动。其实SpringBoot拥有3中不同的Launcher:JarLauncher、WarLauncher、PropertiesLauncher

       SpringBoot使用Launcher代理启动,其最重要的一点便是可以自定义ClassLoader,以实现对jar文件内(jarinjar)或其他路径下jar、class或资源文件的加载

       å…³äºŽClassLoader的更多介绍可参考《深入理解JVM之ClassLoader》

       SpringBoot抽象了Archive的概念,一个Archive可以是jar(JarFileArchive),可以是一个文件目录(ExplodedArchive),可以抽象为统一访问资源的逻辑层。

       ä¸Šä¾‹ä¸­ï¼Œspring-boot-theory-1.0.0.jar既为一个JarFileArchive,spring-boot-theory-1.0.0.jar!/BOOT-INF/lib下的每一个jar包也是一个JarFileArchive

       å°†spring-boot-theory-1.0.0.jar解压到目录spring-boot-theory-1.0.0,则目录spring-boot-theory-1.0.0为一个ExplodedArchive

       æŒ‰ç…§å®šä¹‰ï¼ŒJarLauncher可以加载内部/BOOT-INF/lib下的jar及/BOOT-INF/classes下的应用class

       å…¶å®žJarLauncher实现很简单

       å…¶ä¸»å…¥å£æ–°å»ºäº†JarLauncher并调用父类Launcher中的launch方法启动程序

       å†åˆ›å»ºJarLauncher时,父类ExecutableArchiveLauncher找到自己所在的jar,并创建archive

       åœ¨Launcher的launch方法中,通过以上archive的getNestedArchives方法找到/BOOT-INF/lib下所有jar及/BOOT-INF/classes目录所对应的archive,通过这些archives的url生成LaunchedURLClassLoader,并将其设置为线程上下文类加载器,启动应用

       è‡³æ­¤ï¼Œæ‰æ‰§è¡Œæˆ‘们应用程序主入口类的main方法,所有应用程序类文件均可通过/BOOT-INF/classes加载,所有依赖的第三方jar均可通过/BOOT-INF/lib加载

       åœ¨åˆ†æžLaunchedURLClassLoader前,首先了解一下URLStreamHandler

       java中定义了URL的概念,并实现多种URL协议(见URL)*/didi/cube-ui。

       cube-ui 从滴滴业务中提炼而来,源码b应用源由滴滴 WebApp 前端架构组开发和维护。启动cube-ui 的源码b应用源目标是让移动端的开发更加容易,让开发人员更加专注于业务逻辑的启动jdk的源码在开发,提升研发效率。源码b应用源源码分享网开源

       cube-ui 的启动特性包括:精简提炼自滴滴内部组件库,每个组件都有充分单元测试;追求迅速响应、源码b应用源动画流畅、启动接近原生的源码b应用源交互体验;遵循统一的设计交互标准,接口标准化,启动支持按需引入和后编译,源码b应用源轻量灵活;扩展性强,启动牛b支付源码可以方便地基于现有组件实现二次开发。源码b应用源

       cube-ui 相对于同类型的启动移动端组件库的优势在于,其组件主要包括基础组件、弹出层组件和滚动组件,简单跑腿系统源码总共开源了 个组件,且在组件的体验和交互,包括易用性上我们都追求极致。cube-ui 支持 2 种使用方式,口红机源码月入声明式和 API 式。

       cube-ui 的某些组件有着很好的扩展性,可以根据实际场景需求做功能的扩展,例如基于弹层类组件的基类开发更丰富的弹层类组件,或者基于移动端选择器组件扩展出城市选择器组件。

       cube-ui 底层依赖了 Vue 和 better-scroll,并依赖了一系列工具做了构建部署、单元测试等工作。未来我们会持续对 cube-ui 迭代和优化,包括但不限于开发更多通用的组件,支持换肤功能,以及考虑对静态类型检查的支持。

更多内容请点击【热点】专栏