SpringBoot 내장 tomcat 작 동 원리 상세 설명

머리말
SpringBoot 의 개발 자 는 대중 프로그램 원숭이 들 을 위해 복 지 를 도모 하고 있 습 니 다.모 두 를 게으름뱅이 로 만 들 었 습 니 다.xml 는 설정 하지 않 고 tomcat 도 게 으 른 설정 을 했 습 니 다.전형 적 인 원 키 로 시스템 을 작 동 시 켰 습 니 다.그러면 tomcat 는 springboot 에서 어떻게 작 동 합 니까?
tomcat 내장
개발 단 계 는 우리 에 게 내 장 된 tomcat 를 사용 하 는 것 이 매우 충분 하 며,물론 Jetty 도 사용 할 수 있다.

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
   <version>2.1.6.RELEASE</version>
</dependency>

@SpringBootApplication
public class MySpringbootTomcatStarter{
    public static void main(String[] args) {
        Long time=System.currentTimeMillis();
        SpringApplication.run(MySpringbootTomcatStarter.class);
        System.out.println("===      :"+(System.currentTimeMillis()-time)+"===");
    }
}
여 기 는 main 함수 입구 입 니 다.두 개의 코드 가 가장 눈 에 띄 는데 그것 이 바로 SpringBootApplication 주해 와 SpringApplication.run()방법 입 니 다.
생산 을 발표 하 다
발표 할 때 현재 대부분의 방법 은 내 장 된 tomcat 를 제외 하고 기와 가방(war)을 친 다음 에 생산 된 tomcat 에 배치 하 는 것 입 니 다.좋 습 니 다.그럼 포장 할 때 어떻게 처리 해 야 합 니까?

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <!--      tomcat   -->
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<!--  servlet-api  --->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
</dependency>
main 함 수 를 업데이트 합 니 다.주로 SpringBootServletInitializer 를 계승 하고 configure()방법 을 다시 씁 니 다.

@SpringBootApplication
public class MySpringbootTomcatStarter extends SpringBootServletInitializer {
    public static void main(String[] args) {
        Long time=System.currentTimeMillis();
        SpringApplication.run(MySpringbootTomcatStarter.class);
        System.out.println("===      :"+(System.currentTimeMillis()-time)+"===");
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(this.getClass());
    }
}

main 함수 부터 말하자면

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
    return run(new Class[]{primarySource}, args);
}
--여기 서 run 방법 은 ConfigurableApplication Context 를 되 돌려 줍 니 다.

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
 return (new SpringApplication(primarySources)).run(args);
}

public ConfigurableApplicationContext run(String... args) {
 ConfigurableApplicationContext context = null;
 Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
 this.configureHeadlessProperty();
 SpringApplicationRunListeners listeners = this.getRunListeners(args);
 listeners.starting();

 Collection exceptionReporters;
 try {
  ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
  ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
  this.configureIgnoreBeanInfo(environment);
  
  //  banner,           ,       logo
  Banner printedBanner = this.printBanner(environment);
  
  //       
  context = this.createApplicationContext();
  exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);

  //      
  this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
  
  //     
  this.refreshContext(context);
  
  //      
  this.afterRefresh(context, applicationArguments);
  
  listeners.started(context);
  this.callRunners(context, applicationArguments);
 } catch (Throwable var10) {
  
 }

 try {
  listeners.running(context);
  return context;
 } catch (Throwable var9) {
  
 }
}

tomcat 가 SpringBoot 에서 어떻게 시작 되 는 지 알 고 싶 으 면 run 방법 에 서 는 응용 컨 텍스트(createapplication Context)를 만 들 고 컨 텍스트(refreshContext)를 새로 고침 하 는 데 중점 을 두 고 있 습 니 다.
상하 문 생 성

//     
protected ConfigurableApplicationContext createApplicationContext() {
 Class<?> contextClass = this.applicationContextClass;
 if (contextClass == null) {
  try {
   switch(this.webApplicationType) {
    case SERVLET:
                    //  AnnotationConfigServletWebServerApplicationContext
        contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
     break;
    case REACTIVE:
     contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
     break;
    default:
     contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
   }
  } catch (ClassNotFoundException var3) {
   throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
  }
 }

 return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
}

AnnotationConfigServletWebServerApplication Context 클래스 를 만 듭 니 다.
한편,AnnotationConfigServletWebServerApplication Context 류 는 ServletWebServerApplication Context 를 계승 하 였 으 며,이 종 류 는 최종 적 으로 AbstractApplication Context 를 통합 하 였 다.
문맥 새로 고침

//SpringApplication.java
//     
private void refreshContext(ConfigurableApplicationContext context) {
 this.refresh(context);
 if (this.registerShutdownHook) {
  try {
   context.registerShutdownHook();
  } catch (AccessControlException var3) {
  }
 }
}

//          AbstractApplicationContext.refresh()  
protected void refresh(ApplicationContext applicationContext) {
 ((AbstractApplicationContext)applicationContext).refresh();
}
//AbstractApplicationContext.java
public void refresh() throws BeansException, IllegalStateException {
 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();
   //       onRefresh()  ,          :ServletWebServerApplicationContext,     onRefresh()  
   this.onRefresh();
   this.registerListeners();
   this.finishBeanFactoryInitialization(beanFactory);
   this.finishRefresh();
  } catch (BeansException var9) {
   this.destroyBeans();
   this.cancelRefresh(var9);
   throw var9;
  } finally {
   this.resetCommonCaches();
  }

 }
}


//ServletWebServerApplicationContext.java
//              ,this.createWebServer,          。
protected void onRefresh() {
 super.onRefresh();
 try {
  this.createWebServer();
 } catch (Throwable var2) {
  
 }
}

//ServletWebServerApplicationContext.java
//     webServer,       tomcat,     ServletWebServerFactory  ,      ServletWebServerFactory
private void createWebServer() {
 WebServer webServer = this.webServer;
 ServletContext servletContext = this.getServletContext();
 if (webServer == null && servletContext == null) {
  ServletWebServerFactory factory = this.getWebServerFactory();
  this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
 } else if (servletContext != null) {
  try {
   this.getSelfInitializer().onStartup(servletContext);
  } catch (ServletException var4) {
  
  }
 }

 this.initPropertySources();
}

//  
public interface ServletWebServerFactory {
    WebServer getWebServer(ServletContextInitializer... initializers);
}

//  
AbstractServletWebServerFactory
JettyServletWebServerFactory
TomcatServletWebServerFactory
UndertowServletWebServerFactory
여기 ServletWebServerFactory 인터페이스 에 4 개의 실현 클래스 가 있 습 니 다.

그 중에서 우리 가 자주 사용 하 는 것 은 두 가지 가 있 는데 그것 이 바로 TomcatServletWebServerFactory 와 Jetty ServletWebServerFactory 이다.

//TomcatServletWebServerFactory.java
//       tomcat,      TomcatServletWebServerFactory。         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 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);
}

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

 
//Tomcat.java
//  Engine  ,    ,    tomcat    , engine      。
public Engine getEngine() {
    Service service = getServer().findServices()[0];
    if (service.getContainer() != null) {
        return service.getContainer();
    }
    Engine engine = new StandardEngine();
    engine.setName( "Tomcat" );
    engine.setDefaultHost(hostname);
    engine.setRealm(createDefaultRealm());
    service.setContainer(engine);
    return engine;
}
//Engine       ,Host Engine    ,Context Host    ,Wrapper Context    
getWebServer 라 는 방법 은 Tomcat 대상 을 만 들 고 두 가지 중요 한 일 을 했 습 니 다.Connector 대상 을 tomcat 에 추가 하고 configureEngine(tomcat.getEngine();
           getWebServer 방법 은 TomcatWebServer 를 되 돌려 줍 니 다.

//TomcatWebServer.java
//           TomcatWebServer
public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
 Assert.notNull(tomcat, "Tomcat Server must not be null");
 this.tomcat = tomcat;
 this.autoStart = autoStart;
 initialize();
}

private void initialize() throws WebServerException {
    //           
 logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
 synchronized (this.monitor) {
  try {
   addInstanceIdToEngineName();

   Context context = findContext();
   context.addLifecycleListener((event) -> {
    if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
     removeServiceConnectors();
    }
   });

   //===  tomcat  ===
   this.tomcat.start();

   rethrowDeferredStartupExceptions();

   try {
    ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
   }
   catch (NamingException ex) {
                
   }
            
            //         
   startDaemonAwaitThread();
  }
  catch (Exception ex) {
   stopSilently();
   destroySilently();
   throw new WebServerException("Unable to start embedded Tomcat", ex);
  }
 }
}
//Tomcat.java
public void start() throws LifecycleException {
 getServer();
 server.start();
}
//  server.start    TomcatWebServer 
public void stop() throws LifecycleException {
 getServer();
 server.stop();
}

//TomcatWebServer.java
//  tomcat  
@Override
public void start() throws WebServerException {
 synchronized (this.monitor) {
  if (this.started) {
   return;
  }
  try {
   addPreviouslyRemovedConnectors();
   Connector connector = this.tomcat.getConnector();
   if (connector != null && this.autoStart) {
    performDeferredLoadOnStartup();
   }
   checkThatConnectorsHaveStarted();
   this.started = true;
   //          ,   yml      ,     
   logger.info("Tomcat started on port(s): " + getPortsDescription(true) + " with context path '"
     + getContextPath() + "'");
  }
  catch (ConnectorStartFailedException ex) {
   stopSilently();
   throw ex;
  }
  catch (Exception ex) {
   throw new WebServerException("Unable to start embedded Tomcat server", ex);
  }
  finally {
   Context context = findContext();
   ContextBindings.unbindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
  }
 }
}

//  tomcat  
@Override
public void stop() throws WebServerException {
 synchronized (this.monitor) {
  boolean wasStarted = this.started;
  try {
   this.started = false;
   try {
    stopTomcat();
    this.tomcat.destroy();
   }
   catch (LifecycleException ex) {
    
   }
  }
  catch (Exception ex) {
   throw new WebServerException("Unable to stop embedded Tomcat", ex);
  }
  finally {
   if (wasStarted) {
    containerCounter.decrementAndGet();
   }
  }
 }
}

첨부:tomcat 꼭대기 층 구조 도

      tomcat 최상 위 용 기 는 서버 로 전체 서버 를 대표 하 며 하나의 서버 는 여러 개의 서 비 스 를 포함 합 니 다.위의 그림 에서 볼 수 있 듯 이 Service 는 주로 여러 개의 Connector 와 하나의 Container 를 포함한다.커 넥 터 는 연결 과 관련 된 일 을 처리 하고 소켓 에서 Request 와 Response 로 전환 합 니 다.Container 는 Servlet 을 패키지 하고 관리 하 며 구체 적 인 Request 요청 을 처리 하 는 데 사 용 됩 니 다.그렇다면 앞서 언급 한 Engine>Host>Context>Wrapper 용 기 는 어떻게 된 것 일 까?다음 그림 을 보 겠 습 니 다.

      다시 말 하면 하나의 tomcat 는 하나의 서버 만 포함 하고 하나의 서버 는 여러 개의 서 비 스 를 포함 할 수 있 으 며 하나의 Service 는 하나의 Container 만 있 지만 여러 개의 Connector 가 있 습 니 다.이런 서 비 스 는 여러 개의 연결 을 처리 할 수 있 습 니 다.
      여러 개의 커 넥 터 와 하나의 Container 는 하나의 서 비 스 를 형성 하고 Service 가 있 으 면 대외 적 으로 서 비 스 를 제공 할 수 있 습 니 다.그러나 Service 가 서 비 스 를 제공 하려 면 반드시 숙주 환경 을 제공 해 야 합 니 다.그러면 Server 가 아 닙 니 다.그래서 전체 tomcat 의 성명 주 기 는 Server 에 의 해 제 어 됩 니 다.
총결산
SpringBoot 의 시작 은 주로 실 용화 SpringApplication 을 통 해 시 작 됩 니 다.시작 과정 은 주로 다음 과 같은 몇 가지 일 을 했 습 니 다.속성 설정,감청 기 가 져 오기,발표 응용 프로그램 시작 이벤트 초기,시작 입력 매개 변수,설정 환경,출력 배 너,컨 텍스트 생 성,프 리 처리 컨 텍스트,컨 텍스트 새로 고침,컨 텍스트 새로 고침,발표 응용 프로그램 시작 이벤트,응용 프로그램 시작 완료 이벤트 발표.SpringBoot 에서 tomcat 를 시작 하 는 작업 은 다음 단 계 를 갱신 합 니 다.한편,tomcat 의 시작 은 주로 두 개의 구성 요 소 를 예화 하 는 것 입 니 다.Connector,Container,하나의 tomcat 인 스 턴 스 는 하나의 서버 이 고 하나의 서버 는 여러 개의 서 비 스 를 포함 합 니 다.즉,여러 개의 응용 프로그램 입 니 다.모든 Service 는 여러 개의 Connector 와 하나의 Container 를 포함 하고 하나의 Container 아래 에는 여러 개의 키 용기 가 포함 되 어 있 습 니 다.
SpringBoot 내 장 된 tomcat 작 동 원리 에 대한 상세 한 설명 은 여기까지 입 니 다.더 많은 SpringBoot 내 장 된 tomcat 작 동 내용 은 우리 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 찾 아 보 세 요.앞으로 많은 응원 부 탁 드 리 겠 습 니 다!

좋은 웹페이지 즐겨찾기