您现在的位置是:主页 > news > 深圳市宝安区西乡/最好的关键词排名优化软件
深圳市宝安区西乡/最好的关键词排名优化软件
admin2025/6/29 11:58:10【news】
简介深圳市宝安区西乡,最好的关键词排名优化软件,驾校网站建设和推广,怎么修改网页源代码写在前面 三年前,我在第一家公司的时候,还是用的原生SSM框架,用的是Double zookeeper去实现“微服务”治理。 那时候发现市面上已经有很多开始用起了SpringBoot来搭建服务。第一印象,便是快捷、方便、不用外置tomcat,…
写在前面
三年前,我在第一家公司的时候,还是用的原生SSM框架,用的是Double + zookeeper去实现“微服务”治理。 那时候发现市面上已经有很多开始用起了SpringBoot来搭建服务。第一印象,便是快捷、方便、不用外置tomcat,甩原生开发几条街(不由得想起了现在低代码时代)。
后来到现在的第二家公司,有幸用SpringBoot + SpringCloud治理微服务(所以后面我还会出SpringCloud相关的文章)。一用就是三年,有相当大的微服务治理心得和实战经验,扯的有点远了,话说回来,今天的主角SpringBoot
知其然,知其所以然,用了这么久,总得知道一些原理,前两天面了两个人,都答的不太好,今天我们来学习下。关于介绍SpringBoot的文章太多了,今天这里不多啰嗦了,应该没有人还不知道这是什么东东。我们今天主要是通过源码去了解启动背后的秘密
启动流程
为了故事的发展,我们先建一个SpringBoot服务
@SpringBootApplication
public class DemoApiApplication {public static void main(String[] args) {//run为启动入口ApplicationContext applicationContext = SpringApplication.run(DemoApiApplication.class, args);}
}
复制代码
这行代码大家再了解不过了,更知道的是,这是启动入口,还有上面的SpringBootApplication注解,这个放到第二步讲,今天主要讲启动流程。
运行main函数,调用run方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources,String[] args) {//分为两步// 1、new SpringApplication(primarySources)// 2、run(args)return new SpringApplication(primarySources).run(args);
}
复制代码
先看SpringApplication的构造方法
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {this.resourceLoader = resourceLoader;Assert.notNull(primarySources, "PrimarySources must not be null");//将启动类放入primarySourcesthis.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));//根据classpath 下的类,推算当前web应用类型(webFlux, servlet)//SERVLET : 该应用程序应作为基于 servlet 的 Web 应用程序运行,并应启动嵌入式 servlet Web 服务器。//REACTIVE : 该应用程序应作为响应式 Web 应用程序运行,并应启动嵌入式响应式 Web 服务器this.webApplicationType = deduceWebApplicationType();// 去spring.factories 中去获取所有key:org.springframework.context.ApplicationContextInitializersetInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));//就是去spring.factories 中去获取所有key: org.springframework.context.ApplicationListener setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));// 根据main方法推算出mainApplicationClassthis.mainApplicationClass = deduceMainApplicationClass();
}
复制代码
上面两个set太抽象,什么意思呢,其实是利用SPI机制扫描 META-INF/spring.factories 这个文件,并且加载 ApplicationContextInitializer、ApplicationListener 接口实例。
扩展:什么是SPI,全称为 Service Provider Interface(服务提供者接口),是一种服务发现机制。它通过在ClassPath路径下的META-INF/services文件 夹查找文件,自动加载文件里所定义的类。
- ApplicationContextInitializer 这个类当springboot上下文Context初始化完成后会调用
- ApplicationListener 当springboot启动时事件change后都会触发
有没有人疑问扫描收集这两个接口,目的是什么,看到第二步run的时候 大家就会明白,靠这些监听去处理事情
new SpringApplication总结
上面就是SpringApplication初始化的代码,new SpringApplication()没做啥事情 ,利用SPI机制主要加载了META-INF/spring.factories 下面定义的事件监听器接口实现类
也可以分为几个步骤讲:
- 获取启动类
- 获取web应用类型
- 读取了对外扩展的ApplicationContextInitializer ,ApplicationListener
- 根据main推算出所在的类
核心run方法
上面第一步看构造方法发现并没有什么,那肯定核心的在第二步,我们看启动springboot最核心的逻辑run方法
public ConfigurableApplicationContext run(String... args) {// 用来记录当前springboot启动耗时StopWatch stopWatch = new StopWatch();//记录了启动开始时间stopWatch.start();//关键类,它是任何spring上下文的接口, 所以可以接收任何ApplicationContext实现ConfigurableApplicationContext context = null;Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();configureHeadlessProperty();// 去spring.factroies中读取了SpringApplicationRunListener 的组件, 就是用来发布事件或者运行监听器SpringApplicationRunListeners listeners = getRunListeners(args);// 发布1.ApplicationStartingEvent事件,在运行开始时发送listeners.starting();try {// 根据命令行参数 实例化一个ApplicationArgumentsApplicationArguments applicationArguments = new DefaultApplicationArguments(args);// 很重要:::预初始化环境: 读取环境变量,读取配置文件信息(基于监听器) ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);//忽略beaninfo的bean configureIgnoreBeanInfo(environment);// 打印Banner 横幅,就是我们的启动logoBanner printedBanner = printBanner(environment);//根据webApplicationType创建Spring上下文//这个一会儿在下面再讲,主要就是内置tomcatcontext = createApplicationContext();exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);//预初始化spring上下文 prepareContext(context, environment, listeners, applicationArguments,printedBanner);//加载spring ioc 容器相当重要 由于是使用AnnotationConfigServletWebServerApplicationContext 启动的sring容器所以springboot对它做了扩展:// 加载自动配置类:invokeBeanFactoryPostProcessors , 创建servlet容器onRefresh refreshContext(context);afterRefresh(context, applicationArguments);stopWatch.stop();if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}listeners.started(context);callRunners(context, applicationArguments);}//部分代码省略return context;
}
复制代码
上面的每行代码,每行解释,都要细细品一下,接下来我们把上面几个重要的点再深入下。预准备环境prepareEnvironment
预初始化环境prepareEnvironment
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments) {//根据webApplicationType 创建Environment 创建就会读取: java环境变量和系统环境变量ConfigurableEnvironment environment = getOrCreateEnvironment();// 将命令行参数读取环境变量中configureEnvironment(environment, applicationArguments.getSourceArgs());// 发布了ApplicationEnvironmentPreparedEvent 的监听器 读取了全局配置文件listeners.environmentPrepared(environment);//将所有spring.main 开头的配置信息绑定SpringApplicationbindToSpringApplication(environment);//判断web容器,装载,说白了 就是tomcat类型,还记上上面讲的web类型吧if (this.webApplicationType == WebApplicationType.NONE) {environment = new EnvironmentConverter(getClassLoader()).convertToStandardEnvironmentIfNecessary(environment);}// 更新PropertySourcesConfigurationPropertySources.attach(environment);return environment;
}
复制代码
上面我们看到发布了很多监听事件去处理很多事情,第一步的时候初始化监听的意义就是所在
创建上下文 createApplicationContext
这段代码主要是根据项目类型创建上下文,并且会注入几个核心组件类
protected ConfigurableApplicationContext createApplicationContext() {Class<?> contextClass = this.applicationContextClass;if (contextClass == null) {try {switch (this.webApplicationType) {case SERVLET:contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);break;case REACTIVE:contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);break;default:contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);}}catch (ClassNotFoundException ex) {throw new IllegalStateException("Unable create a default ApplicationContext, "+ "please specify an ApplicationContextClass",ex);}}//注入几个核心上下文return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
复制代码
预初始化上下文prepareContext
private void prepareContext(ConfigurableApplicationContext context,ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments, Banner printedBanner) {// 给上下文对象设置环境对象,给AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner设置环境对象context.setEnvironment(environment);// 1. 上下文后置处理,包括向BeanFactory中注册BeanNameGenerator和ConversionServicepostProcessApplicationContext(context);// 2. SpringApplication构造器中初始化了各种ApplicationContextInitializer,循环调用他们的initialize方法applyInitializers(context);// 3. 发送ApplicationContextInitializedEvent事件listeners.contextPrepared(context);if (this.logStartupInfo) {// 打印启动信息,包括pid,用户等logStartupInfo(context.getParent() == null);// 答应profile信息logStartupProfileInfo(context);}// Add boot specific singleton beansConfigurableListableBeanFactory beanFactory = context.getBeanFactory();// 将ApplicationArguments注册到容器中beanFactory.registerSingleton("springApplicationArguments", applicationArguments);if (printedBanner != null) {// 将Banner注册到容器中beanFactory.registerSingleton("springBootBanner", printedBanner);}if (beanFactory instanceof DefaultListableBeanFactory) {// 设置不允许定义同名的BeanDefinition,重复注册时抛出异常((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);}if (this.lazyInitialization) {// 如果懒加载,添加懒加载后置处理器context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());}// Load the sourcesSet<Object> sources = getAllSources();Assert.notEmpty(sources, "Sources must not be empty");load(context, sources.toArray(new Object[0]));// 发送ApplicationPreparedEvent事件listeners.contextLoaded(context);
}
复制代码
最后一步 最最重要的就是调用Spring的启动代码加载了bean
refreshContext
@Override
public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// 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);// Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.registerBeanPostProcessors(beanFactory);// 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();}//省略
复制代码
上面这个有没有很熟悉,就是Spring的初始化bean(启动容器),这里就不展开细讲。
说到这里 还有个小细节,那就是内置tomcat在哪,我们一起看下。
内置tomcat
上面说的Spring启动容器,这里主要看一下onRefresh() 这个方法。转到定义发现这个方法里面啥都没有,这明显是一个钩子方法,它会钩到它子类重写onRefresh()方法。所以去看子类里面的onRefresh()
protected void onRefresh() throws BeansException {//这是一个空方法,AbstractApplicationContext 这个类是一个抽象类,//所以我们要找到集成AbstractApplicationContext的子类,去看子类里面的onRefresh()// For subclasses: do nothing by default.
}
复制代码
因为我们是web容器context,所以创建context的时候,createApplicationContext方法里有个ConfigurableApplicationContext接口 那么实现类即是ServletWebServerApplicationContext (上面有讲)
@Override
protected void onRefresh() {super.onRefresh();try {//看到web容器了,我们进去看下createWebServer();}catch (Throwable ex) {throw new ApplicationContextException("Unable to start web server", ex);}
}
复制代码
上图中可以看到有jetty容器,tomcat容器,原来如此
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {Tomcat tomcat = new Tomcat();File baseDir = (this.baseDirectory != null ? this.baseDirectory: createTempDir("tomcat"));tomcat.setBaseDir(baseDir.getAbsolutePath());Connector connector = new Connector(this.protocol);tomcat.getService().addConnector(connector);customizeConnector(connector);tomcat.setConnector(connector);tomcat.getHost().setAutoDeploy(false);configureEngine(tomcat.getEngine());for (Connector additionalConnector : this.additionalTomcatConnectors) {tomcat.getService().addConnector(additionalConnector);}prepareContext(tomcat.getHost(), initializers);return getTomcatWebServer(tomcat);
}
复制代码
OK,今天的学习就到这里,最后我们先进行下简单总结
总结:
1. 初始化SpringApplication 从spring.factories 读取 listener ApplicationContextInitializer 。
2.运行run方法
3.读取 环境变量 配置信息.....
4. 创建springApplication上下文:ServletWebServerApplicationContext
5. 预初始化上下文 : 读取启动类
6.调用refresh 加载ioc容器
01 加载所有的自动配置类02 创建servlet容器
复制代码
7.在这个过程中springboot会调用很多监听器对外进行
原文链接:https://juejin.cn/post/7091646469001707557