zoukankan      html  css  js  c++  java
  • 我的Spring Boot学习记录(二):Tomcat Server以及Spring MVC的问题

    Spring Boot版本: 2.0.0.RELEASE
    这里需要引入依赖 spring-boot-starter-web
    这里有可能有个人的误解,请抱着怀疑态度看。
    建议: 感觉自己也会被绕晕,所以有兴趣的使用IDE工具看下源码

    1、Tomcat在什么时候被初始化了?

    ServletWebServerApplicationContext中有段代码,如下:

    org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext
    
    	@Override
    	protected void onRefresh() {
    		super.onRefresh();
    		try {
    			createWebServer();
    		}
    		catch (Throwable ex) {
    			throw new ApplicationContextException("Unable to start web server", ex);
    		}
    	}
    	
    	/**
    	 * 创建Web服务容器,如:Tomcat,Jetty等;具体创建的容器根据#getWebServerFactory得到
    	 * 而WebServerFactory在BeanFactory获取,也就是在加载Bean时确定的,
    	 * 这里通过Spring Boot自动配置了Tomcat,如果想要深入可以追着#getWebServerFactory看
    	 * 下面有对应的不太详细的解释
    	 */
    	private void createWebServer() {
    		WebServer webServer = this.webServer;
    		ServletContext servletContext = getServletContext();
    		if (webServer == null && servletContext == null) {
    			ServletWebServerFactory factory = getWebServerFactory();
    			this.webServer = factory.getWebServer(getSelfInitializer());
    		}
    		// .....
    	}
    
    org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration.EmbeddedTomcat#tomcatServletWebServerFactory
    	/**
    	 * 这里就通过自动加载Bean时加载了关于Tomcat的WebServerFactory
    	 *  至于为什么加载Tomcat而不是Jetty,就要多谢Bean加载时@ConditionalOnClass注解
    	 * 因为我们引入依赖spring-boot-starter-web 其次,它又引入了 spring-boot-starter-tomcat依赖
    	 * 因为存在{ Servlet.class, Tomcat.class, UpgradeProtocol.class }这些class,所以加载Tomcat
    	 */
    	@Configuration
    	@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
    	@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
    	public static class EmbeddedTomcat {
    
    		@Bean
    		public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
    			return new TomcatServletWebServerFactory();
    		}
    
    	}
    	// .......
    

    上述中说明了Tomcat的创建,而什么时候调用onRefresh并创建Tomcat呢?

    其实onRefreshorg.springframework.context.support.AbstractApplicationContext一个未具体实现方法,交给子类实现,它在调用refresh()方法中调用。
    org.springframework.boot.SpringApplication#run(java.lang.String...)调用了refreshContext,又间接调用上述refresh()方法

    onRefresh调用后在refresh()中还调用了finishRefresh(),而重写了其方法finishRefreshServletWebServerApplicationContext就启动了Web服务,代码如下:

    org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#finishRefresh
    	@Override
    	protected void finishRefresh() {
    		super.finishRefresh();
    		WebServer webServer = startWebServer();
    		if (webServer != null) {
    			publishEvent(new ServletWebServerInitializedEvent(webServer, this));
    		}
    	}
    	
    	private WebServer startWebServer() {
    		WebServer webServer = this.webServer;
    		if (webServer != null) {
    			// 这里如何启动具体可看org.springframework.boot.web.embedded.tomcat.TomcatWebServer
    			// 这里之所以我们启动了Spring Boot后程序还在运行是因为Tomcat启动线程在后台运行
    			webServer.start();
    		}
    		return webServer;
    	}
    
    org.springframework.boot.web.embedded.tomcat.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 {
    		//.....
    		// Unlike Jetty, all Tomcat threads are daemon threads. We create a
    		// blocking non-daemon to stop immediate shutdown
    		// 大概意思是创建一个非守护线程来运行吧
    		startDaemonAwaitThread();
    		//....
    	}
    
    	private void startDaemonAwaitThread() {
    		Thread awaitThread = new Thread("container-" + (containerCounter.get())) {
    
    			@Override
    			public void run() {
    				TomcatWebServer.this.tomcat.getServer().await();
    			}
    
    		};
    		awaitThread.setContextClassLoader(getClass().getClassLoader());
    		awaitThread.setDaemon(false);
    		awaitThread.start();
    	}
    

    上面就大概说了Tomcat怎么在Spring Boot启动后加载创建的

    2、Spring MVC在这里怎么工作的?

    平时使用SSM开发时,使用Spring MVC 我们知道需要配置DispatcherServlet,而这里的是通过自动配置的,还对DispatcherServlet通过ServletRegistrationBean类进行封装,这里自动装配的代码在org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration中可见,代码如下:

    org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration.DispatcherServletConfiguration
    	@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
    	public DispatcherServlet dispatcherServlet() {
    		DispatcherServlet dispatcherServlet = new DispatcherServlet();
    		dispatcherServlet.setDispatchOptionsRequest(
    				this.webMvcProperties.isDispatchOptionsRequest());
    		dispatcherServlet.setDispatchTraceRequest(
    				this.webMvcProperties.isDispatchTraceRequest());
    		dispatcherServlet.setThrowExceptionIfNoHandlerFound(
    				this.webMvcProperties.isThrowExceptionIfNoHandlerFound());
    		return dispatcherServlet;
    	}
    
    
    org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration.DispatcherServletRegistrationConfiguration
    	/**
    	 * 这里通过构造器获取已经注册的 DispatcherServlet Bean 
    	 * 然后封装在ServletRegistrationBean
    	 */
    	@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
    	@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
    	public ServletRegistrationBean<DispatcherServlet> dispatcherServletRegistration(
    			DispatcherServlet dispatcherServlet) {
    		ServletRegistrationBean<DispatcherServlet> registration = new ServletRegistrationBean<>(
    				dispatcherServlet,
    				this.serverProperties.getServlet().getServletMapping());
    		registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
    		registration.setLoadOnStartup(
    				this.webMvcProperties.getServlet().getLoadOnStartup());
    		if (this.multipartConfig != null) {
    			registration.setMultipartConfig(this.multipartConfig);
    		}
    		return registration;
    	}
    

    封装了Servlet的ServletRegistrationBean各个Bean又会被ServletContextInitializerBeans进行管理。

    在上述第一点中Tomcat创建加载中有个方法没有详述,就是ServletWebServerApplicationContext中的createWebServer方法,里面调用了一个getSelfInitializer方法,使用返回值作为参数,代码如下:

    org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext
    	private void createWebServer() {
    		//...
    		this.webServer = factory.getWebServer(getSelfInitializer());
    		//...
    	}
    
    	/**
    	 * Returns the {@link ServletContextInitializer} that will be used to complete the
    	 * setup of this {@link WebApplicationContext}.
    	 * @return the self initializer
    	 * @see #prepareWebApplicationContext(ServletContext)
    	 */
    	private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
    		// 又封装成了ServletContextInitializer
    		return this::selfInitialize;
    	}
    	
    	private void selfInitialize(ServletContext servletContext) throws ServletException {
    		//....
    		//getServletContextInitializerBeans这里就能获取到封装了Servlet或Filter等的ServletContextInitializer
    		for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
    			// 这里通过回调Servlet或Filter能够获取到servletContext,并且将自己(Servlet或Filter)注册到servletContext,这里可能要去了解下Tomcat了
    			beans.onStartup(servletContext);
    		}
    	}
    
    	/**
    	 * Returns {@link ServletContextInitializer}s that should be used with the embedded
    	 * web server. By default this method will first attempt to find
    	 * {@link ServletContextInitializer}, {@link Servlet}, {@link Filter} and certain
    	 * {@link EventListener} beans.
    	 * @return the servlet initializer beans
    	 */
    	protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
    		// 这里在new ServletContextInitializerBeans 时会将BeanFactory给它
    		// 它在构造器中将封装了Servlet或Filter等的ServletContextInitializer子类获取
    		// 并放入一个成员变量sortedList中
    		return new ServletContextInitializerBeans(getBeanFactory());
    	}
    

    到此,大概就知道了Spring MVC中DispatcherServlet是怎么进入Tomcat的了,如果还想细究DispatcherServlet是怎么被一步步置入Tomcat容器中的,可以看下org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#getWebServer中的代码,这里不展开了。

    3、随便唠嗑

    入职后好久没运动,被同事拉去打羽毛球,结果,气喘不上来,脑晕,感觉不好,第一天全身酸痛,第二天更痛。我认为,入职后第一条建议就是找时间运动。

    这篇其实接着第一篇文章我的Spring Boot学习记录(一):自动配置的大致调用过程 就想要写的了,拖了好久。期间问自己,为什么干这种无趣的东西。不论如何,还是抽了时间写了,就这样吧。

    这里有可能有个人的误解,请抱着怀疑态度看。
    建议: 感觉自己也会被绕晕,所以有兴趣的使用IDE工具看下源码

  • 相关阅读:
    Visual Studio 2015安装后没有C++ WIN32 console
    ORACLE SEQUENCE用法
    adoconnection断线重连
    海康威视网络摄像机 SDK二次开发(JAVA)(1-DEMO测试)
    FFmpeg,H.264,Directshow,和opencv及视频编码与封装格式
    DirectShow 视频采集
    CoCreateInstance运行原理
    二叉树中序线索化
    Xvid-Delphi
    风冷冷藏库的化霜周期和化霜时间是多长?
  • 原文地址:https://www.cnblogs.com/lger/p/11681367.html
Copyright © 2011-2022 走看看