接第二篇

第二篇里面, 看到容器创建的是 AnnotationConfigServletWebServerApplicationContext 类型.

一 .类图

二. 构造

  1. public GenericApplicationContext() {
      //创建 bean 工厂
  2. this.beanFactory = new DefaultListableBeanFactory();
  3. }
  4.  
  5. public AnnotationConfigServletWebServerApplicationContext() {
      //创建读取器
  6. this.reader = new AnnotatedBeanDefinitionReader(this);
      //创建扫描器
  7. this.scanner = new ClassPathBeanDefinitionScanner(this);
  8. }

构造函数中, 创建了三个类, 并赋值给相应的属性.

三. 启动 tomcat

  1. @Override
  2. public void refresh() throws BeansException, IllegalStateException {
  3. synchronized (this.startupShutdownMonitor) {
  4. // Prepare this context for refreshing.
  5. prepareRefresh();
  6.  
  7. // Tell the subclass to refresh the internal bean factory.
  8. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
  9.  
  10. // Prepare the bean factory for use in this context.
  11. prepareBeanFactory(beanFactory);
  12.  
  13. try {
  14. // Allows post-processing of the bean factory in context subclasses.
  15. postProcessBeanFactory(beanFactory);
  16.  
  17. // Invoke factory processors registered as beans in the context.
  18. invokeBeanFactoryPostProcessors(beanFactory);
  19.  
  20. // Register bean processors that intercept bean creation.
  21. registerBeanPostProcessors(beanFactory);
  22.  
  23. // Initialize message source for this context.
  24. initMessageSource();
  25.  
  26. // Initialize event multicaster for this context.
  27. initApplicationEventMulticaster();
  28.  
  29. // Initialize other special beans in specific context subclasses.
  30. onRefresh();
  31.  
  32. // Check for listener beans and register them.
  33. registerListeners();
  34.  
  35. // Instantiate all remaining (non-lazy-init) singletons.
  36. finishBeanFactoryInitialization(beanFactory);
  37.  
  38. // Last step: publish corresponding event.
  39. finishRefresh();
  40. }
  41.  
  42. catch (BeansException ex) {
  43. if (logger.isWarnEnabled()) {
  44. logger.warn("Exception encountered during context initialization - " +
  45. "cancelling refresh attempt: " + ex);
  46. }
  47.  
  48. // Destroy already created singletons to avoid dangling resources.
  49. destroyBeans();
  50.  
  51. // Reset 'active' flag.
  52. cancelRefresh(ex);
  53.  
  54. // Propagate exception to caller.
  55. throw ex;
  56. }
  57.  
  58. finally {
  59. // Reset common introspection caches in Spring's core, since we
  60. // might not ever need metadata for singleton beans anymore...
  61. resetCommonCaches();
  62. }
  63. }
  64. }

这里我主要是想要了解tomcat启动, 所以一些方法, 就先不看.

1. onRefresh()

onRefresh() 方法执行的是 ServletWebServerApplicationContext 的方法.

  1. @Override
  2. protected void onRefresh() {
  3. super.onRefresh();
  4. try {
  5. createWebServer();
  6. }
  7. catch (Throwable ex) {
  8. throw new ApplicationContextException("Unable to start web server", ex);
  9. }
  10. }

createWebServer() 方法中, 会创建 Tomcat 类.

  1. private void createWebServer() {
  2. WebServer webServer = this.webServer;
      //当前进来, servletContext 为null
  3. ServletContext servletContext = getServletContext();
  4. if (webServer == null && servletContext == null) {
         //创建了 TomcatServletWebServerFactory 类
  5. ServletWebServerFactory factory = getWebServerFactory();
         //创建 Tomcat
  6. this.webServer = factory.getWebServer(getSelfInitializer());
  7. }
  8. else if (servletContext != null) {
  9. try {
  10. getSelfInitializer().onStartup(servletContext);
  11. }
  12. catch (ServletException ex) {
  13. throw new ApplicationContextException("Cannot initialize servlet context",
  14. ex);
  15. }
  16. }
  17. initPropertySources();
  18. }

getWebServer方法里面, 就创建了 Tomcat 类, 并对其进行一些配置操作.

  1. @Override
  2. public WebServer getWebServer(ServletContextInitializer... initializers) {
      //创建 Tomcat
  3. Tomcat tomcat = new Tomcat();
  4. File baseDir = (this.baseDirectory != null ? this.baseDirectory
  5. : createTempDir("tomcat"));
  6. tomcat.setBaseDir(baseDir.getAbsolutePath());
  7. Connector connector = new Connector(this.protocol);
  8. tomcat.getService().addConnector(connector);
  9. customizeConnector(connector);
  10. tomcat.setConnector(connector);
  11. tomcat.getHost().setAutoDeploy(false);
  12. configureEngine(tomcat.getEngine());
  13. for (Connector additionalConnector : this.additionalTomcatConnectors) {
  14. tomcat.getService().addConnector(additionalConnector);
  15. }
  16. prepareContext(tomcat.getHost(), initializers);
      //这里会创建 TomcatWebServer 实例, 并返回
  17. return getTomcatWebServer(tomcat);
  18. }

这里的 protocol 是有一个默认值的:

  1. public static final String DEFAULT_PROTOCOL = "org.apache.coyote.http11.Http11NioProtocol";
  2.  
  3. private String protocol = DEFAULT_PROTOCOL;

可以看到, 这里默认使用的是 同步非阻塞io协议. 需要注意的是, 在 new Connector() 的时候 对 Http11NioProtocol 进行了反射实例化.

  1. public Http11NioProtocol() {
  2. super(new NioEndpoint());
  3. }

在实例化的时候, new 了一个 NioEndpoint. 这个东西很重要, 后面会看到.


  1. getTomcatWebServer()
  1. protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
  2. return new TomcatWebServer(tomcat, getPort() >= 0);
  3. }

在创建 TomcatWebServer 的时候, 就会启动 Tomcat

  1. public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
  2. Assert.notNull(tomcat, "Tomcat Server must not be null");
  3. this.tomcat = tomcat;
  4. this.autoStart = autoStart;
  5. initialize();
  6. }
  7.  
  8. private void initialize() throws WebServerException {
  9. TomcatWebServer.logger
  10. .info("Tomcat initialized with port(s): " + getPortsDescription(false));
  11. synchronized (this.monitor) {
  12. try {
  13. addInstanceIdToEngineName();
  14.  
  15. Context context = findContext();
  16. context.addLifecycleListener((event) -> {
  17. if (context.equals(event.getSource())
  18. && Lifecycle.START_EVENT.equals(event.getType())) {
  19. // Remove service connectors so that protocol binding doesn't
  20. // happen when the service is started.
  21. removeServiceConnectors();
  22. }
  23. });
  24.  
  25. // Start the server to trigger initialization listeners
  26. this.tomcat.start();
  27.  
  28. // We can re-throw failure exception directly in the main thread
  29. rethrowDeferredStartupExceptions();
  30.  
  31. try {
  32. ContextBindings.bindClassLoader(context, context.getNamingToken(),
  33. getClass().getClassLoader());
  34. }
  35. catch (NamingException ex) {
  36. // Naming is not enabled. Continue
  37. }
  38.  
  39. // Unlike Jetty, all Tomcat threads are daemon threads. We create a
  40. // blocking non-daemon to stop immediate shutdown
  41. startDaemonAwaitThread();
  42. }
  43. catch (Exception ex) {
  44. stopSilently();
  45. throw new WebServerException("Unable to start embedded Tomcat", ex);
  46. }
  47. }
  48. }

2. finishRefresh()

  1. ServletWebServerApplicationContext 重写了该方法.
  1. @Override
  2. protected void finishRefresh() {
      //调用父类的 finishedRefresh 方法, 保证处理完整性
  3. super.finishRefresh();
      //启动 TomcatWebServer
  4. WebServer webServer = startWebServer();
  5. if (webServer != null) {
  6. publishEvent(new ServletWebServerInitializedEvent(webServer, this));
  7. }
  8. }

startWebServer()

  1. private WebServer startWebServer() {
  2. WebServer webServer = this.webServer;
  3. if (webServer != null) {
  4. webServer.start();
  5. }
  6. return webServer;
  7. }
  8.  
  9. @Override
  10. public void start() throws WebServerException {
  11. synchronized (this.monitor) {
  12. if (this.started) {
  13. return;
  14. }
  15. try {
           //遍历service, 拿到service, 然后绑定 Connector
  16. addPreviouslyRemovedConnectors();
  17. Connector connector = this.tomcat.getConnector();
  18. if (connector != null && this.autoStart) {
  19. performDeferredLoadOnStartup();
  20. }
  21. checkThatConnectorsHaveStarted();
  22. this.started = true;
  23. TomcatWebServer.logger
  24. .info("Tomcat started on port(s): " + getPortsDescription(true)
  25. + " with context path '" + getContextPath() + "'");
  26. ......
  27. }

addPreviouslyRemovedConnectors()

  1. private void addPreviouslyRemovedConnectors() {
  2. Service[] services = this.tomcat.getServer().findServices();
  3. for (Service service : services) {
  4. Connector[] connectors = this.serviceConnectors.get(service);
  5. if (connectors != null) {
  6. for (Connector connector : connectors) {
  7. service.addConnector(connector);
  8. if (!this.autoStart) {
  9. stopProtocolHandler(connector);
  10. }
  11. }
  12. this.serviceConnectors.remove(service);
  13. }
  14. }
  15. }

service 在绑定 Connector 的时候, 会启动 Connector

  1. @Override
  2. public void addConnector(Connector connector) {
  3.  
  4. synchronized (connectorsLock) {
  5. connector.setService(this);
  6. Connector results[] = new Connector[connectors.length + 1];
  7. System.arraycopy(connectors, 0, results, 0, connectors.length);
  8. results[connectors.length] = connector;
  9. connectors = results;
  10.  
  11. if (getState().isAvailable()) {
  12. try {
  13. connector.start();
  14. } catch (LifecycleException e) {
  15. log.error(sm.getString(
  16. "standardService.connector.startFailed",
  17. connector), e);
  18. }
  19. }
  20.  
  21. // Report this property change to interested listeners
  22. support.firePropertyChange("connector", null, connector);
  23. }
  24. }

看一下 connector.start() 方法.

  1. @Override
  2. public final synchronized void start() throws LifecycleException {
  3.  
  4. ......try {
  5. setStateInternal(LifecycleState.STARTING_PREP, null, false);
  6. startInternal();
  7. if (state.equals(LifecycleState.FAILED)) {
  8. // This is a 'controlled' failure. The component put itself into the
  9. // FAILED state so call stop() to complete the clean-up.
  10. stop();
  11. } else if (!state.equals(LifecycleState.STARTING)) {
  12. // Shouldn't be necessary but acts as a check that sub-classes are
  13. // doing what they are supposed to.
  14. invalidTransition(Lifecycle.AFTER_START_EVENT);
  15. } else {
  16. setStateInternal(LifecycleState.STARTED, null, false);
  17. }
  18. } catch (Throwable t) {
  19. // This is an 'uncontrolled' failure so put the component into the
  20. // FAILED state and throw an exception.
  21. ExceptionUtils.handleThrowable(t);
  22. setStateInternal(LifecycleState.FAILED, null, false);
  23. throw new LifecycleException(sm.getString("lifecycleBase.startFail", toString()), t);
  24. }
  25. }

startInternal() 是一个抽象方法, 其中的一个实现类 Connector

  1. @Override
  2. protected void startInternal() throws LifecycleException {
  3. // Validate settings before starting
  4. if (getPort() < 0) {
  5. throw new LifecycleException(sm.getString(
  6. "coyoteConnector.invalidPort", Integer.valueOf(getPort())));
  7. }
  8. setState(LifecycleState.STARTING);
  9. try {
  10. protocolHandler.start();
  11. } catch (Exception e) {
  12. throw new LifecycleException(
  13. sm.getString("coyoteConnector.protocolHandlerStartFailed"), e);
  14. }
  15. }

接着进 start() 方法

  1. @Override
  2. public void start() throws Exception {
  3. if (getLog().isInfoEnabled()) {
  4. getLog().info(sm.getString("abstractProtocolHandler.start", getName()));
  5. }
  6.  
  7. endpoint.start();
  8. // Start async timeout thread
  9. asyncTimeout = new AsyncTimeout();
  10. Thread timeoutThread = new Thread(asyncTimeout, getNameInternal() + "-AsyncTimeout");
  11. int priority = endpoint.getThreadPriority();
  12. if (priority < Thread.MIN_PRIORITY || priority > Thread.MAX_PRIORITY) {
  13. priority = Thread.NORM_PRIORITY;
  14. }
  15. timeoutThread.setPriority(priority);
  16. timeoutThread.setDaemon(true);
  17. timeoutThread.start();
  18. }

endPoint.start() 方法:

  1. public final void start() throws Exception {
  2. if (bindState == BindState.UNBOUND) {
  3. bind();
  4. bindState = BindState.BOUND_ON_START;
  5. }
  6. startInternal();
  7. }

这个bind() 执行的是NioEndpoint 中的方法, 进行端口绑定监听.

  1. @Override
  2. public void bind() throws Exception {
  3.  
  4. serverSock = ServerSocketChannel.open();
  5. socketProperties.setProperties(serverSock.socket());
  6. InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));
  7. serverSock.socket().bind(addr,getAcceptCount());
  8. serverSock.configureBlocking(true); //mimic APR behavior
  9.  
  10. // Initialize thread count defaults for acceptor, poller
  11. if (acceptorThreadCount == 0) {
  12. // FIXME: Doesn't seem to work that well with multiple accept threads
  13. acceptorThreadCount = 1;
  14. }
  15. if (pollerThreadCount <= 0) {
  16. //minimum one poller thread
  17. pollerThreadCount = 1;
  18. }
  19. setStopLatch(new CountDownLatch(pollerThreadCount));
  20.  
  21. // Initialize SSL if needed
  22. initialiseSsl();
  23.  
  24. selectorPool.open();
  25. }

总结:

从执行流程上来看,

1. 在onRefresh() 中, 启动Tomcat

2. 在 finishBeanFactoryInitialization() 中进行了后台方法的路由映射(待续)

3. 在finishRefresh()中进行了端口绑定监听

springboot web - 启动(4) tomcat的更多相关文章

  1. springboot web - 启动(1) 创建SpringApplication

    一. 测试代码 @SpringBootApplication public class SbmvcApplication { public static void main(String[] args ...

  2. springboot web - 启动(2) run()

    接上一篇 在创建 SpringApplication 之后, 调用了 run() 方法. public ConfigurableApplicationContext run(String... arg ...

  3. springboot项目启动后tomcat服务器自动关闭 解决方法

    需要在pom.xml中添加 <dependency> <groupId>org.springframework.boot</groupId> <artifac ...

  4. SpringBoot集成Socket服务后打包(war包)启动时如何启动Socket服务(web应用外部tomcat启动)

      1.首先知道SpringBoot打包为jar和war包是不一样的(只讨论SpringBoot环境下web应用打包)     1.1.jar和war包的打开方式不一样,虽然都依赖java环境,但是j ...

  5. SpringBoot应用部署到Tomcat中无法启动问题

    SpringBoot应用部署到Tomcat中无法启动问题   背景 最近公司在做一些内部的小型Web应用时, 为了提高开发效率决定使用SpringBoot, 这货自带Servlet容器, 你在开发We ...

  6. SpringBoot应用部署到Tomcat中无法启动问题(初识)

    参考http://blog.csdn.net/asdfsfsdgdfgh/article/details/52127562 背景 最近公司在做一些内部的小型Web应用时, 为了提高开发效率决定使用Sp ...

  7. 多个springboot项目部署到tomcat,Error deploying web application archive

    每个springboot单独部署到tomcat下可以正常启动,多个一个就发生异常 Error deploying web application archive 解决:配置文件加上配置区分 sprin ...

  8. 使用spring等框架的web程序在Tomcat下的启动顺序及思路理清

    大牛请绕过,此文仅针对自己小白水平,对web程序的启动流程做个清晰的回顾. 一.使用spring等框架的web程序在Tomcat下的启动流程 1)Tomcat是根据web.xml来启动的.首先到web ...

  9. Intellij IDEA创建的Web项目配置Tomcat并启动Maven项目

    本篇博客讲解IDEA如何配置Tomcat. 大部分是直接上图哦. 点击如图所示的地方,进行添加Tomcat配置页面 弹出页面后,按照如图顺序找到,点击+号 tomcat Service -> L ...

随机推荐

  1. spyder学习记录---如何调试

    调试技巧: 当我们想单步执行某段代码(但是不进入调用的函数)时,点击运行当前行. 当我们想进入某个函数内部进行调试,在函数调用处点击进入函数或方法内运行. 当我们不想看函数内部的运行过程时,点击跳出函 ...

  2. 使用IDEA详解Spring中依赖注入的类型(上)

    使用IDEA详解Spring中依赖注入的类型(上) 在Spring中实现IoC容器的方法是依赖注入,依赖注入的作用是在使用Spring框架创建对象时动态地将其所依赖的对象(例如属性值)注入Bean组件 ...

  3. 基于MySQL+MHA+Haproxy部署高可用负载均衡集群

    一.MHA 概述 MHA(Master High Availability)是可以在MySQL上使用的一套高可用方案.所编写的语言为Perl 从名字上我们可以看到.MHA的目的就是为了维护Master ...

  4. Linux系统之LVS+Keepalived实现

    1.简述lvs四种集群特点及使用场景 LVS集群有4种类型,分别是NAT.DR.TUN.FULLNAT 从工作方式来讲,NAT和FULLNAT都要修改请求报文的目标IP和目标端口(NAT)或源IP目标 ...

  5. JDK14都要问世了,你还在用JDK8吗

    Java开发工具包(JDK)14已进入发布候选阶段,总体功能基本已确定.计划中的标准Java升级将具有新功能,例如JDK Flight Recorder事件流,模式匹配和开关表达式. JDK 14计划 ...

  6. istio-ServiceMesh解决方案

    istio-ServiceMesh解决方案 istio(1):ServiceMesh解决方案-k8s安装istio istio(2):流量管理-基于不同版本访问规则控制 istio(3):流量管理-基 ...

  7. Windows 远程桌面连接Ubuntu14.04

    在Ubuntu系统进行如下系统配置 1.安装xrdp sudo apt-get install xrdp 2.安装vnc4server sudo apt-get install vnc4server ...

  8. android wifi断开原因分析

    最近在解bug的过程中经常遇到密码正确但处于saved的状态,总结原因有已下几种: 1 在ASSOCIATING阶段由于丢包导致ASSOC REJECT 03-16 09:22:12.440 D/Wi ...

  9. 【转载】Linux进程间通信(六):共享内存 shmget()、shmat()、shmdt()、shmctl()

    来源:https://www.cnblogs.com/52php/p/5861372.html 下面将讲解进程间通信的另一种方式,使用共享内存. 一.什么是共享内存 顾名思义,共享内存就是允许两个不相 ...

  10. vue自带的实例属性和方法($打头)

    Vue 实例内置了一些有用的实例属性与方法.它们都有前缀 $,以便与用户定义的属性区分开来.例如: var data = { a: 1 } var vm = new Vue({ el: '#examp ...