SpringBoot学习(四):内嵌Servlet引擎(Tomcat,Jetty等)的实现原理

概述

 

  • SpringBoot使用main方法启动的一个重要特性是,不需要打包成war部署到Tomcat这种Servlet容器中,而是只需打包成jar,然后通过java或mvn等命令运行这个jar包,然后应用就可以在指定的端口监听客户端的连接请求了。
  • 在SpringBoot内部主要是引用了embedded的Tomcat或Jetty等作为Servlet引擎,由该Servlet引擎负责接收Web请求并交给应用处理和生成响应,从而可以将应用打包成jar,直接在命令行启动,以独立进程的方式运行,无需依赖Tomcat等Servlet容器,但是可以实现跟部署到Tomcat中的war包一样处理Web请求和响应。

接口设计

  • 在接口设计层面,对Spring容器而言,是通过拓展ApplicationContext接口来增加Spring容器对Servlet引擎的支持;对Servlet引擎而言,则是定义了WebServer接口来代表Servlet引擎或者说是Web服务器,WebServer接口实现类为具体的Servlet引擎实现。
  • Spring容器通过包含WebServer的引用来负责Servlet引擎的启动关闭。同时与普通Servlet容器实现(如Tomcat)一样,将Spring应用通过关联一个ServletContext引用来建立与Servlet引擎的关联,同时将自身保存为ServletContext引用的attribute。

  • 应用自身在启动的时候,会创建和启动Spring容器,在Spring容器中通过该WebServer引用来启动对应的Servlet引擎。并在启动Servlet引擎过程中,创建该应用对应的ServletContext,由该ServletContext来间接建立当前应用与Servlet引擎的关联。

Spring容器ApplicationContext体系

  • WebServerApplicationContext接口:定义获取WebServer引用的方法
     

    /**
     * Interface to be implemented by {@link ApplicationContext application contexts} that
     * create and manage the lifecycle of an embedded {@link WebServer}.
     *
     * @author Phillip Webb
     * @since 2.0.0
     */
    public interface WebServerApplicationContext extends ApplicationContext {
    
    	/**
    	 * Returns the {@link WebServer} that was created by the context or {@code null} if
    	 * the server has not yet been created.
    	 * @return the web server
    	 */
    	WebServer getWebServer();
    
    	/**
    	 * Returns the namespace of the web server application context or {@code null} if no
    	 * namespace has been set. Used for disambiguation when multiple web servers are
    	 * running in the same application (for example a management context running on a
    	 * different port).
    	 * @return the server namespace
    	 */
    	String getServerNamespace();
    
    }
    

     

  • ServletWebServerApplicationContext:WebServerApplicationContext接口的具体实现类,包含WebServer的引用,类定义如下:
     

    /**
     * A {@link WebApplicationContext} that can be used to bootstrap itself from a contained
     * {@link ServletWebServerFactory} bean.
     * <p>
     * This context will create, initialize and run an {@link WebServer} by searching for a
     * single {@link ServletWebServerFactory} bean within the {@link ApplicationContext}
     * itself. The {@link ServletWebServerFactory} is free to use standard Spring concepts
     * (such as dependency injection, lifecycle callbacks and property placeholder variables).
     * <p>
     * In addition, any {@link Servlet} or {@link Filter} beans defined in the context will be
     * automatically registered with the web server. In the case of a single Servlet bean, the
     * '/' mapping will be used. If multiple Servlet beans are found then the lowercase bean
     * name will be used as a mapping prefix. Any Servlet named 'dispatcherServlet' will
     * always be mapped to '/'. Filter beans will be mapped to all URLs ('/*').
     * <p>
     * For more advanced configuration, the context can instead define beans that implement
     * the {@link ServletContextInitializer} interface (most often
     * {@link ServletRegistrationBean}s and/or {@link FilterRegistrationBean}s). To prevent
     * double registration, the use of {@link ServletContextInitializer} beans will disable
     * automatic Servlet and Filter bean registration.
     * <p>
     * Although this context can be used directly, most developers should consider using the
     * {@link AnnotationConfigServletWebServerApplicationContext} or
     * {@link XmlServletWebServerApplicationContext} variants.
     *
     * @author Phillip Webb
     * @author Dave Syer
     * @see AnnotationConfigServletWebServerApplicationContext
     * @see XmlServletWebServerApplicationContext
     * @see ServletWebServerFactory
     */
    public class ServletWebServerApplicationContext extends GenericWebApplicationContext
    		implements ConfigurableWebServerApplicationContext {
    
    	private static final Log logger = LogFactory
    			.getLog(ServletWebServerApplicationContext.class);
    
    	/**
    	 * Constant value for the DispatcherServlet bean name. A Servlet bean with this name
    	 * is deemed to be the "main" servlet and is automatically given a mapping of "/" by
    	 * default. To change the default behavior you can use a
    	 * {@link ServletRegistrationBean} or a different bean name.
    	 */
    	public static final String DISPATCHER_SERVLET_NAME = "dispatcherServlet";
    
    	private volatile WebServer webServer;
    
    	private ServletConfig servletConfig;
    
    	private String serverNamespace;
    	
    	...
    
    		    
    }
    
    • 由继承体系可知,继承了GenericWebApplicationContext,在GenericWebApplicationContext中定义了ServletContext引用。
    • 由代码注释可知,Spring容器管理了一个ServletWebServerFactory引用,ServletWebServerFactory为WebServer实现类对象的工厂类,通过ServletWebServerFactory来创建servlet引擎WebServer,其中由ServletWebServerFactory创建的WebServer默认在8080端口监听请求,具体为在基类AbstractConfigurableWebServerFactory中定义。

    • 同时注册到Spring容器的Servlet,Filter接口的实现类会自动注册到Servlet引擎,具体为应用对应的ServletContext。
  • AnnotationConfigServletWebServerApplicationContext:基于注解的Spring容器,继承了ServletWebServerApplicationContext。

  • XmlServletWebServerApplicationContext:基于XML的Spring容器,继承了ServletWebServerApplicationContext。

Servlet引擎体系

  • WebServer接口:声明Servlet引擎启动,关闭的方法,相应的实现类实现这些方法来定义启动和关闭逻辑,而对Spring容器ServletWebServerApplicationContext而言,只需依赖这个接口即可,不依赖具体实现,这也是遵循了依赖倒置设计原则。
     

    /**
     * Simple interface that represents a fully configured web server (for example Tomcat,
     * Jetty, Netty). Allows the server to be {@link #start() started} and {@link #stop()
     * stopped}.
     *
     * @author Phillip Webb
     * @author Dave Syer
     * @since 2.0.0
     */
    public interface WebServer {
    
    	/**
    	 * Starts the web server. Calling this method on an already started server has no
    	 * effect.
    	 * @throws WebServerException if the server cannot be started
    	 */
    	void start() throws WebServerException;
    
    	/**
    	 * Stops the web server. Calling this method on an already stopped server has no
    	 * effect.
    	 * @throws WebServerException if the server cannot be stopped
    	 */
    	void stop() throws WebServerException;
    
    	/**
    	 * Return the port this server is listening on.
    	 * @return the port (or -1 if none)
    	 */
    	int getPort();
    
    }
    

     

  • ServletWebServerFactory接口:Servlet引擎WebServer的工厂接口,接口定义如下,声明了getWebServer方法。也是遵循依赖倒置设计原则,即Spring容器ServletWebServerApplicationContext只依赖这个接口,具体为这个接口的getWebServer方法来获取一个Servlet引擎WebServer对象,而该接口的具体实现类,负责实现这个接口。实现类包括:TomcatServletWebServerFactory,JettyServletWebServerFactory,UndertowServletWebServerFactory,具体在embedded包定义。
     

    /**
     * Factory interface that can be used to create a {@link WebServer}.
     *
     * @author Phillip Webb
     * @see WebServer
     * @since 2.0.0
     */
    @FunctionalInterface
    public interface ServletWebServerFactory {
    
    	/**
    	 * Gets a new fully configured but paused {@link WebServer} instance. Clients should
    	 * not be able to connect to the returned server until {@link WebServer#start()} is
    	 * called (which happens when the {@link ApplicationContext} has been fully
    	 * refreshed).
    	 * @param initializers {@link ServletContextInitializer}s that should be applied as
    	 * the server starts
    	 * @return a fully configured and started {@link WebServer}
    	 * @see WebServer#stop()
    	 */
    	WebServer getWebServer(ServletContextInitializer... initializers);
    
    }
    

     

Spring容器和Servlet引擎启动过程

  • Spring容器是在ApplicationContext的refresh方法定义启动流程的,具体为在AbstractApplicationContext中定义refresh方法的流程模板:关于Servlet引擎的启动,是在onRefresh和finishRefresh方法定义的,由onRefresh方法的注释可知,这个方法是设计用来注册有特殊功能的bean对象到Spring容器内部的BeanFactory的。所以Spring容器的设计拓展性是很好的。
     

    @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();
    		}
            
            ...
            
    	}
    }
    

     

  • ServletWebServerApplicationContext的refresh方法,onRefresh方法,finishRefresh方法。其中onRefresh方法负责Server引擎和ServletContext的创建;finishOnRefresh方法负责Servlet引擎的启动,即调用WebServer的start方法,然后在指定的端口,如8080,监听客户端的请求。
     

    @Override
    public final void refresh() throws BeansException, IllegalStateException {
    	try {
    		super.refresh();
    	}
    	catch (RuntimeException ex) {
    		stopAndReleaseWebServer();
    		throw ex;
    	}
    }
    
    @Override
    protected void onRefresh() {
    	super.onRefresh();
    	try {
    		createWebServer();
    	}
    	catch (Throwable ex) {
    		throw new ApplicationContextException("Unable to start web server", ex);
    	}
    }
    
    @Override
    protected void finishRefresh() {
    	super.finishRefresh();
    	WebServer webServer = startWebServer();
    	if (webServer != null) {
    		publishEvent(new ServletWebServerInitializedEvent(webServer, this));
    	}
    }
    

     

  • createWebServer方法实现:创建servlet引擎WebServer和ServletContext。
     

    private void createWebServer() {
    	WebServer webServer = this.webServer;
    	ServletContext servletContext = getServletContext();
    	if (webServer == null && servletContext == null) {
    		ServletWebServerFactory factory = getWebServerFactory();
    		
    		// 创建servlet引擎WebServer和ServletContext
    		
    		this.webServer = factory.getWebServer(getSelfInitializer());
    	}
    	else if (servletContext != null) {
    		try {
    		
    		// 如果已经启动过,则将应用配置的
    		// Servlet规范的Servlet,Filter,Listener再初始化一次ServletContext
    		
    			getSelfInitializer().onStartup(servletContext);
    		}
    		catch (ServletException ex) {
    			throw new ApplicationContextException("Cannot initialize servlet context",
    					ex);
    		}
    	}
    	initPropertySources();
    }
    

     

  • startWebServer方法实现:调用Servlet引擎的start方法完成启动。
     

    private WebServer startWebServer() {
    	WebServer webServer = this.webServer;
    	if (webServer != null) {
    		webServer.start();
    	}
    	return webServer;
    }
    
    • 以下为TomcatWebServer的start方法实现:启动应用在Servlet规范中对应的Context,即TomcatEmbeddedContext
      @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) {
      			
      			// 启动TomcatEmbeddedContext
      			
      				performDeferredLoadOnStartup();
      			}
      			checkThatConnectorsHaveStarted();
      			this.started = true;
      			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());
      		}
      	}
      }
      
      // 启动TomcatEmbeddedContext
      private void performDeferredLoadOnStartup() {
      	try {
      		for (Container child : this.tomcat.getHost().findChildren()) {
      			if (child instanceof TomcatEmbeddedContext) {
      				((TomcatEmbeddedContext) child).deferredLoadOnStartup();
      			}
      		}
      	}
      	catch (Exception ex) {
      		if (ex instanceof WebServerException) {
      			throw (WebServerException) ex;
      		}
      		throw new WebServerException("Unable to start embedded Tomcat connectors",
      				ex);
      	}
      }
      

       

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值