spring boot中servlet启动原理

启动过程及原理

1 spring boot 应用启动运行run方法

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);//创建一个ApplicationContext容器context = createApplicationContext();analyzers = new FailureAnalyzers(context);prepareContext(context, environment, listeners, applicationArguments,printedBanner);//刷新IOC容器
            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);}     

 

2  createApplicationContext():创建IOC容器,如果是web应用则创建AnnotationConfigEmbeddedWebApplacation的IOC容器,如果不是,则创建AnnotationConfigApplication的IOC容器

    public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."+ "annotation.AnnotationConfigApplicationContext";/*** The class name of application context that will be used by default for web* environments.*/public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework."+ "boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext";protected ConfigurableApplicationContext createApplicationContext() {Class<?> contextClass = this.applicationContextClass;if (contextClass == null) {try {
          //根据应用环境,创建不同的IOC容器contextClass
= Class.forName(this.webEnvironment? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);}catch (ClassNotFoundException ex) {throw new IllegalStateException("Unable create a default ApplicationContext, "+ "please specify an ApplicationContextClass",ex);}}return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);}

3    refreshContext(context) spring boot刷新IOC容器(创建容器对象,并初始化容器,创建容器每一个组件)

private void refreshContext(ConfigurableApplicationContext context) {refresh(context);if (this.registerShutdownHook) {try {context.registerShutdownHook();}catch (AccessControlException ex) {// Not allowed in some environments.
            }}}

4 refresh(context);刷新刚才创建的IOC容器

protected void refresh(ApplicationContext applicationContext) {Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);((AbstractApplicationContext) applicationContext).refresh();}

5 调用父类的refresh()的方法

public void refresh() throws BeansException, IllegalStateException {Object var1 = this.startupShutdownMonitor;synchronized(this.startupShutdownMonitor) {this.prepareRefresh();ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();this.prepareBeanFactory(beanFactory);try {this.postProcessBeanFactory(beanFactory);this.invokeBeanFactoryPostProcessors(beanFactory);this.registerBeanPostProcessors(beanFactory);this.initMessageSource();this.initApplicationEventMulticaster();this.onRefresh();this.registerListeners();this.finishBeanFactoryInitialization(beanFactory);this.finishRefresh();} catch (BeansException var9) {if (this.logger.isWarnEnabled()) {this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);}this.destroyBeans();this.cancelRefresh(var9);throw var9;} finally {this.resetCommonCaches();}}}

6  抽象父类AbstractApplicationContext类的子类EmbeddedWebApplicationContext的onRefresh方法

@Overrideprotected void onRefresh() {super.onRefresh();try {createEmbeddedServletContainer();}catch (Throwable ex) {throw new ApplicationContextException("Unable to start embedded container",ex);}}

7  在createEmbeddedServletContainer放啊发中会获取嵌入式Servlet容器工厂,由容器工厂创建Servlet

private void createEmbeddedServletContainer() {EmbeddedServletContainer localContainer = this.embeddedServletContainer;ServletContext localServletContext = getServletContext();if (localContainer == null && localServletContext == null) {
                //获取嵌入式Servlet容器工厂EmbeddedServletContainerFactory containerFactory
= getEmbeddedServletContainerFactory();
          //根据容器工厂获取对应嵌入式Servlet容器
this.embeddedServletContainer = containerFactory.getEmbeddedServletContainer(getSelfInitializer());}else if (localServletContext != null) {try {getSelfInitializer().onStartup(localServletContext);}catch (ServletException ex) {throw new ApplicationContextException("Cannot initialize servlet context",ex);}}initPropertySources();}

8  从IOC容器中获取Servlet容器工厂

//EmbeddedWebApplicationContext#getEmbeddedServletContainerFactory  
protected EmbeddedServletContainerFactory getEmbeddedServletContainerFactory() {  // Use bean names so that we don't consider the hierarchy  String[] beanNames = getBeanFactory()  .getBeanNamesForType(EmbeddedServletContainerFactory.class);  if (beanNames.length == 0) {  throw new ApplicationContextException(  "Unable to start EmbeddedWebApplicationContext due to missing "  + "EmbeddedServletContainerFactory bean.");  }  if (beanNames.length > 1) {  throw new ApplicationContextException(  "Unable to start EmbeddedWebApplicationContext due to multiple "  + "EmbeddedServletContainerFactory beans : "  + StringUtils.arrayToCommaDelimitedString(beanNames));  }  return getBeanFactory().getBean(beanNames[0],  EmbeddedServletContainerFactory.class);  
}  

 

9  使用Servlet容器工厂获取嵌入式Servlet容器,具体使用哪一个容器工厂看配置环境依赖

this.embeddedServletContainer = containerFactory  .getEmbeddedServletContainer(getSelfInitializer());  

10  上述创建过程  首先启动IOC容器,接着启动嵌入式Servlet容器,接着将IOC容器中剩下没有创建的对象获取出来,比如自己创建的controller

// Instantiate all remaining (non-lazy-init) singletons.finishBeanFactoryInitialization(beanFactory);
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 bean post-processor// (such as a PropertyPlaceholderConfigurer bean) registered any before:// at this point, primarily for resolution in annotation attribute values.if (!beanFactory.hasEmbeddedValueResolver()) {beanFactory.addEmbeddedValueResolver(new StringValueResolver() {@Overridepublic String resolveStringValue(String strVal) {return 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();}

 

看看 preInstantiateSingletons方法

public void preInstantiateSingletons() throws BeansException {if (this.logger.isDebugEnabled()) {this.logger.debug("Pre-instantiating singletons in " + this);}List<String> beanNames = new ArrayList(this.beanDefinitionNames);Iterator var2 = beanNames.iterator();while(true) {while(true) {String beanName;RootBeanDefinition bd;do {do {do {if (!var2.hasNext()) {var2 = beanNames.iterator();while(var2.hasNext()) {beanName = (String)var2.next();Object singletonInstance = this.getSingleton(beanName);if (singletonInstance instanceof SmartInitializingSingleton) {final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton)singletonInstance;if (System.getSecurityManager() != null) {AccessController.doPrivileged(new PrivilegedAction<Object>() {public Object run() {smartSingleton.afterSingletonsInstantiated();return null;}}, this.getAccessControlContext());} else {smartSingleton.afterSingletonsInstantiated();}}}return;}beanName = (String)var2.next();bd = this.getMergedLocalBeanDefinition(beanName);} while(bd.isAbstract());} while(!bd.isSingleton());} while(bd.isLazyInit());if (this.isFactoryBean(beanName)) {final FactoryBean<?> factory = (FactoryBean)this.getBean("&" + beanName);boolean isEagerInit;if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {isEagerInit = ((Boolean)AccessController.doPrivileged(new PrivilegedAction<Boolean>() {public Boolean run() {return ((SmartFactoryBean)factory).isEagerInit();}}, this.getAccessControlContext())).booleanValue();} else {isEagerInit = factory instanceof SmartFactoryBean && ((SmartFactoryBean)factory).isEagerInit();}if (isEagerInit) {this.getBean(beanName);}} else {
            //注册bean
this.getBean(beanName);}}}}

是使用getBean方法来通过反射将所有未创建的实例创建出来

  使用嵌入式Servlet容器:

     优点:   简单,便携

     缺点:   默认不支持jsp,优化定制比较复杂

使用外置Servlet容器的步骤:

  1  必须创建war项目,需要剑豪web项目的目录结构

  2  嵌入式Tomcat依赖scope指定provided

  3  编写SpringBootServletInitializer类子类,并重写configure方法

public class ServletInitializer extends SpringBootServletInitializer {  @Override  protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {  return application.sources(SpringBoot04WebJspApplication.class);  }  
} 

 

       4  启动服务器

jar包和war包启动区别

    jar包:执行SpringBootApplication的run方法,启动IOC容器,然后创建嵌入式Servlet容器

 war包:  先是启动Servlet服务器,服务器启动Springboot应用(springBootServletInitizer),然后启动IOC容器

Servlet 3.0+规则

    1  服务器启动(web应用启动),会创建当前web应用里面所有jar包里面的ServletContainerlnitializer实例

     2 ServletContainerInitializer的实现放在jar包的META-INF/services文件夹下

   3  还可以使用@HandlesTypes注解,在应用启动的时候加载指定的类。

外部Tomcat流程以及原理

  ①  启动Tomcat

  ②  根据上述描述的Servlet3.0+规则,可以在Spring的web模块里面找到有个文件名为javax.servlet.ServletContainerInitializer的文件,而文件的内容为org.springframework.web.SpringServletContainerInitializer,用于加载SpringServletContainerInitializer类

  ③看看SpringServletContainerInitializer定义

@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {/*** Delegate the {@code ServletContext} to any {@link WebApplicationInitializer}* implementations present on the application classpath.* <p>Because this class declares @{@code HandlesTypes(WebApplicationInitializer.class)},* Servlet 3.0+ containers will automatically scan the classpath for implementations* of Spring's {@code WebApplicationInitializer} interface and provide the set of all* such types to the {@code webAppInitializerClasses} parameter of this method.* <p>If no {@code WebApplicationInitializer} implementations are found on the classpath,* this method is effectively a no-op. An INFO-level log message will be issued notifying* the user that the {@code ServletContainerInitializer} has indeed been invoked but that* no {@code WebApplicationInitializer} implementations were found.* <p>Assuming that one or more {@code WebApplicationInitializer} types are detected,* they will be instantiated (and <em>sorted</em> if the @{@link* org.springframework.core.annotation.Order @Order} annotation is present or* the {@link org.springframework.core.Ordered Ordered} interface has been* implemented). Then the {@link WebApplicationInitializer#onStartup(ServletContext)}* method will be invoked on each instance, delegating the {@code ServletContext} such* that each instance may register and configure servlets such as Spring's* {@code DispatcherServlet}, listeners such as Spring's {@code ContextLoaderListener},* or any other Servlet API componentry such as filters.* @param webAppInitializerClasses all implementations of* {@link WebApplicationInitializer} found on the application classpath* @param servletContext the servlet context to be initialized* @see WebApplicationInitializer#onStartup(ServletContext)* @see AnnotationAwareOrderComparator*/@Overridepublic void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)throws ServletException {List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();if (webAppInitializerClasses != null) {for (Class<?> waiClass : webAppInitializerClasses) {// Be defensive: Some servlet containers provide us with invalid classes,// no matter what @HandlesTypes says...if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&WebApplicationInitializer.class.isAssignableFrom(waiClass)) {try {
                //为所有的WebApplicationInitializer类型创建实例,并加入集合中initializers.add((WebApplicationInitializer) waiClass.newInstance());}
catch (Throwable ex) {throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);}}}}if (initializers.isEmpty()) {servletContext.log("No Spring WebApplicationInitializer types detected on classpath");return;}servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");AnnotationAwareOrderComparator.sort(initializers);
      //调用每一个WebApplicationInitializer实例的onstartup方法
for (WebApplicationInitializer initializer : initializers) {initializer.onStartup(servletContext);}}}

 在上面一段长长的注释中可以看到,SpringServletContainerInitializer将@HandlesTypes(WebApplicationInitializer.class)标注的所有WebApplicationInitializer这个类型的类都传入到onStartup方法的Set参数中,并通过反射为这些WebApplicationInitializer类型的类创建实例;

  ④  方法最后,每一个WebApplicationInitilizer实现调用自己onstartup方法

  ⑤  而WebApplicationInitializer有个抽象实现类SpringBootServletInitializer(记住我们继承了该抽象类),则会调用每一个WebApplicationInitializer实例(包括SpringBootServletInitializer)的onStartup方法:

public abstract class SpringBootServletInitializer implements WebApplicationInitializer {  //other code...  
      @Override  public void onStartup(ServletContext servletContext) throws ServletException {  // Logger initialization is deferred in case a ordered  // LogServletContextInitializer is being used  this.logger = LogFactory.getLog(getClass());  //创建IOC容器  WebApplicationContext rootAppContext = createRootApplicationContext(  servletContext);  if (rootAppContext != null) {  servletContext.addListener(new ContextLoaderListener(rootAppContext) {  @Override  public void contextInitialized(ServletContextEvent event) {  // no-op because the application context is already initialized  
                }  });  }  else {  this.logger.debug("No ContextLoaderListener registered, as "  + "createRootApplicationContext() did not "  + "return an application context");  }  }  protected WebApplicationContext createRootApplicationContext(  ServletContext servletContext) {  //创建Spring应用构建器,并进行相关属性设置  SpringApplicationBuilder builder = createSpringApplicationBuilder();  StandardServletEnvironment environment = new StandardServletEnvironment();  environment.initPropertySources(servletContext, null);  builder.environment(environment);  builder.main(getClass());  ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);  if (parent != null) {  this.logger.info("Root context already created (using as parent).");  servletContext.setAttribute(  WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);  builder.initializers(new ParentContextApplicationContextInitializer(parent));  }  builder.initializers(  new ServletContextApplicationContextInitializer(servletContext));  builder.contextClass(AnnotationConfigEmbeddedWebApplicationContext.class);  //调用configure方法,创建war类型的web项目后,由于编写SpringBootServletInitializer的子类重写configure方法,所以此处调用的是我们定义的子类重写的configure方法  builder = configure(builder);  //通过构建器构建了一个Spring应用  SpringApplication application = builder.build();  if (application.getSources().isEmpty() && AnnotationUtils  .findAnnotation(getClass(), Configuration.class) != null) {  application.getSources().add(getClass());  }  Assert.state(!application.getSources().isEmpty(),  "No SpringApplication sources have been defined. Either override the "  + "configure method or add an @Configuration annotation");  // Ensure error pages are registered  if (this.registerErrorPageFilter) {  application.getSources().add(ErrorPageFilterConfiguration.class);  }  //启动Spring应用  return run(application);  }  //Spring应用启动,创建并返回IOC容器  protected WebApplicationContext run(SpringApplication application) {  return (WebApplicationContext) application.run();  }  } 

   SpringBootServletInitializer实例执行onStartup方法的时候会通过createRootApplicationContext方法来执行run方法,接下来的过程就同以jar包形式启动的应用的run过程一样了,在内部会创建IOC容器并返回,只是以war包形式的应用在创建IOC容器过程中,不再创建Servlet容器了。

转载于:https://www.cnblogs.com/developerxiaofeng/p/9081689.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/280263.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

某乎有人问--微软会抛弃C#吗,有点担心?

在某乎有人问&#xff1a;微软会抛弃C#吗&#xff0c;有点担心&#xff1f;&#xff0c;类似这样的问题&#xff0c;一直都有很多人在问&#xff0c;今天我们就来聊聊这个问题。没必要担心微软倒闭了&#xff0c;C#都不会消失&#xff0c;其实.Net已经不属于微软的了。C#是属于…

mailing list的原理

1 发往mailing list邮箱的邮件会被所有订阅了该邮箱的人收到 说白了&#xff0c;就是一种邮件群发机制&#xff0c;为了简化群发&#xff0c;不是将所有的收件人放到收件人列表中&#xff0c;而是发往总的邮箱即可。 2 要向该mailing list邮箱中发送邮件需要先要订阅 但是&…

icloud上传错误_如何修复HomeKit“地址未注册到iCloud”错误

icloud上传错误While Apple has made serious improvements to the HomeKit smarthome framework, there are still more than a few ghosts in the machine. Let’s look at how to banish the extremely frustrating “Address is not registered with iCloud” error to get…

Jenkins安装部署

Jenkins安装部署 Jenkins简介 Jenkins是一个开源软件项目&#xff0c;是基于Java开发的一种持续集成工具&#xff0c;用于监控持续重复的工作&#xff0c;旨在提供一个开放易用的软件平台&#xff0c;使软件的持续集成变成可能。 安装步骤 本文以CentOS7为环境&#xff0c;安装…

Angular2中的路由(简单总结)

Angular2中建立路由的4个步骤&#xff1a; 1、路由配置&#xff1a;最好新建一个app.toutes.ts文件&#xff08;能不能用ng命令新建有待调查&#xff09; Angular2中路由要解决的是URL与页面的对应关系&#xff08;比如URL是http://localhost:4200/all-people&#xff0c;那么页…

受 SQLite 多年青睐,C 语言到底好在哪儿?

SQLite 近日发表了一篇博文&#xff0c;解释了为什么多年来 SQLite 一直坚持用 C 语言来实现&#xff0c;以下是正文内容&#xff1a; C 语言是最佳选择 从2000年5月29日发布至今&#xff0c;SQLite 一直都是用 C 语言实现。C 一直是实现像 SQLite 这类软件库的最佳语言。目前&…

为什么 Random.Shared 是线程安全的

在多线程环境中使用 Random 类来生成伪随机数时&#xff0c;很容易出现线程安全问题。例如&#xff0c;当多个线程同时调用 Next 方法时&#xff0c;可能会出现种子被意外修改的情况&#xff0c;导致生成的伪随机数不符合预期。为了避免这种情况&#xff0c;.NET 框架引入了 Ra…

(3)Python3笔记之变量与运算符

一、变量 1&#xff09;. 命名规则&#xff1a; 1. 变量名不能使用系统关键字或保留关键字 2. 变量区分大小写 3. 变量命名由字母&#xff0c;数字&#xff0c;下划线组成但不能以数字开头 4. 不需要声明变量类型 是 a 1 非 int a 1 5. 查看变量内存地址 id(a), id(b) 6…

some demos

import ../css/detail.css;// 找到字符串中重复次数最多的字符 function findMax(str) {let maxChar ;let maxValue 1;if (!str.length) return;let arr str.replace(/\s/g, ).split();let obj {};for (let i 0; i < arr.length; i) {if (!obj[arr[i]]) {obj[arr[i]] …

WPF 实现视频会议与会人员动态布局

WPF 实现视频会议与会人员动态布局控件名&#xff1a;SixGridView作 者&#xff1a;WPFDevelopersOrg - 驚鏵原文链接[1]&#xff1a;https://github.com/WPFDevelopersOrg/WPFDevelopers框架使用.NET40&#xff1b;Visual Studio 2019;接着上一篇是基于Grid实现的视频查看感…

汉三水属国(北地属国、安定属国)

汉三水属国&#xff08;北地属国、安定属国&#xff09; 两汉&#xff08;西汉、东汉&#xff09;400年中&#xff0c;由于各种原因&#xff0c;经常有成批的匈奴归附汉朝&#xff0c;两汉政府对他们采取了较为妥善的安置政策&#xff0c;其中最主要的措施是为他们设立专门的居…

《爆发》作者:大数据领域将有新赢家

本文讲的是《爆发》作者&#xff1a;大数据领域将有新赢家,全球复杂网络研究专家日前到访中国&#xff0c;为其新作《爆发》作宣传。他在接受国内媒体采访时表示&#xff0c;未来可能有新公司取代谷歌、Facebook等公司&#xff0c;成为大数据领域的赢家。 《爆发》一书是一本讨…

chromebook刷机_如何获取Android应用以查看Chromebook上的外部存储

chromebook刷机Android apps are a great way to expand the sometimes limited capabilities of Chromebooks, but they can be a problem if you store most of your data on an external medium—like an SD card, for example. Android应用程序是扩展Chromebook有时有限功能…

Stream流与Lambda表达式(四) 自定义收集器

一、自定义SetCustomCollector收集器 package com.java.design.Stream.CustomCollector;import java.util.*; import java.util.function.BiConsumer; import java.util.function.BinaryOperator; import java.util.function.Function; import java.util.function.Supplier; im…

ModelState.IsValid忽略型别的检查错误

Web Api在Int或DateTime如果传空值的话会自动帮忙设预设值&#xff0c;但是在ModelState.IsValid的时候&#xff0c;却会出现型别上的错误.解决方式把Model改成正确&#xff0c;也就是预设允许可以为nullpublic class DemoModel { …

android 指纹添加_如何将手势添加到Android手机的指纹扫描仪

android 指纹添加So you have a shiny new Android phone, equipped with a security-friendly fingerprint scanner. Congratulations! But did you know that, while useful on its own, you can actually make the fingerprint scanner do more than just unlock your phone…

关于前端性能优化

常用的优化有两部分 第一&#xff1a;面向内容的优化 减少 HTTP 请求减少 DNS 查找避免重定向使用 Ajax 缓存延迟载入组件预先载入组件减少 DOM 元素数量切分组件到多个域最小化 iframe 的数量不要出现http 404 错误第二&#xff1a;面向 Server 缩小 Cookie针对 Web 组件使用域…

前端工程化:围绕Jenkins打造工作流的过程

背景 1年前入职时&#xff0c;公司前端部门的静态代码部署都是用ftp工具拖拽部署&#xff0c;没有记录&#xff0c;没有关联&#xff0c;经常造成许多困扰的问题&#xff0c; 比如&#xff1a;今天有没有其他人在我要部署的路径上工作&#xff1f;我的代码为啥被盖掉了&#xf…

业务id转密文短链的一种实现思路

业务场景&#xff1a; 买家通过电商app下单后&#xff0c;会受到一条短信&#xff0c;短信内容中包括改订单详情页面的h5地址连接&#xff0c;因为是出现在短信中&#xff0c;所以对连接有要求&#xff1a;1.尽量短&#xff1b;2.安全性考虑&#xff0c;订单在数据库中对应的自…