• SpringBoot-嵌入式Servlet容器原理


    WebServerFactory自动配置

    @Configuration
    @AutoConfigureOrder(-2147483648)
    @ConditionalOnClass({ServletRequest.class})
    @ConditionalOnWebApplication(
        type = Type.SERVLET
    )
    @EnableConfigurationProperties({ServerProperties.class})
    @Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, EmbeddedTomcat.class, EmbeddedJetty.class, EmbeddedUndertow.class})
    public class ServletWebServerFactoryAutoConfiguration {
    
    	// Servlet Web Server 通用配置,将ServerProperties注入到定制器中,实现application.properties中的配置信息配置 Servlet Web Server
    	@Bean
    	public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {
    	return new ServletWebServerFactoryCustomizer(serverProperties);
    	}
    
    	// Tomcat Servlet Web Server 配置信息
    	@Bean
    	@ConditionalOnClass(
    	name = {"org.apache.catalina.startup.Tomcat"}
    	)
    	public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) {
    	return new TomcatServletWebServerFactoryCustomizer(serverProperties);
    	}
    }

    EmbeddedTomcat 嵌入式Tomcat

    @Configuration
    // 判断当前是否引入了 org.apache.catalina.startup.Tomcat 依赖
    @ConditionalOnClass({Servlet.class, Tomcat.class, UpgradeProtocol.class})
    // 当没有 ServletWebServerFactory 类型的Bean时才注入该组件
    @ConditionalOnMissingBean(
    	value = {ServletWebServerFactory.class},
    	search = SearchStrategy.CURRENT
    )
    public static class EmbeddedTomcat {
    	public EmbeddedTomcat() {
    	}
    
      // 创建Tomcat容器工厂
    	@Bean
    	public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
    		return new TomcatServletWebServerFactory();
    	}
    }

      ServletWebServerFactory(Servlet容器工厂) 继承关系

    @FunctionalInterface
    public interface ServletWebServerFactory {
    	// 创建WebServer即Servlet容器 
      WebServer getWebServer(ServletContextInitializer... initializers);
    }

      WebServer (Servlet容器) 继承关系

    public interface WebServer {
        void start() throws WebServerException;
    
        void stop() throws WebServerException;
    
        int getPort();
    }
    

      TomcatServletWebServerFactory 获得Tomcat Servlet 容器(即org.apache.catalina.startup.Tomcat)

    public WebServer getWebServer(ServletContextInitializer... initializers) {
    	// 创建Tomcat容器	
    	Tomcat tomcat = new Tomcat();
      // 配置Tomcat运行环境
    	File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
    	tomcat.setBaseDir(baseDir.getAbsolutePath());
    	Connector connector = new Connector(this.protocol);
    	tomcat.getService().addConnector(connector);
    	this.customizeConnector(connector);
    	tomcat.setConnector(connector);
    	tomcat.getHost().setAutoDeploy(false);
    	this.configureEngine(tomcat.getEngine());
    	Iterator var5 = this.additionalTomcatConnectors.iterator();
    
    	while(var5.hasNext()) {
    		Connector additionalConnector = (Connector)var5.next();
    		tomcat.getService().addConnector(additionalConnector);
    	}
    
    	this.prepareContext(tomcat.getHost(), initializers);
      // 调用 tomcat.start(); 启动容器
    	return this.getTomcatWebServer(tomcat);
    }

    修改Servlet容器配置信息的方式 

      1)修改application.properties

    server.port=8888
    

      2)  添加定制器

    package com.wjz.config;
    
    import org.springframework.boot.web.server.ConfigurableWebServerFactory;
    import org.springframework.boot.web.server.WebServerFactoryCustomizer;
    import org.springframework.stereotype.Component;
    
    @Component
    public class WebServerConfig implements WebServerFactoryCustomizer<ConfigurableWebServerFactory> {
        @Override
        public void customize(ConfigurableWebServerFactory factory) {
            factory.setPort(8888);
        }
    }  

    定制器后置处理器

    public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
    
    	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    		if (bean instanceof WebServerFactory) {
    			this.postProcessBeforeInitialization((WebServerFactory)bean);
    		}
    
    		return bean;
    	}
    
    	private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
    		((Callbacks)LambdaSafe.callbacks(WebServerFactoryCustomizer.class, this.getCustomizers(), webServerFactory, new Object[0]).withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)).invoke((customizer) -> {
    			customizer.customize(webServerFactory);
    		});
    	}
    
    	private Collection<WebServerFactoryCustomizer<?>> getCustomizers() {
    		if (this.customizers == null) {
    			this.customizers = new ArrayList(this.getWebServerFactoryCustomizerBeans());
    			this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE);
    			this.customizers = Collections.unmodifiableList(this.customizers);
    		}
    
    		return this.customizers;
    	}
    
    	private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() {
    		return this.beanFactory.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();
    	}
    }
    

    Servlet容器启动流程

      什么时候创建ServletWebServerFactory(Servlet容器工厂),什么时候创建 WebServer (Servlet容器)并启动?

      1)EmbeddedTomcat.TomcatServletWebServerFactory处和TomcatServletWebServerFactory#getWebServer()处打断点,debug方式启动系统

      2)创建SpringApplication对象并执行run()方法

      3)context = this.createApplicationContext(); this.refreshContext(context); SpringBoot刷新IOC容器即创建IOC容器对象并初始化容器,创建容器中的每一个组件

    如果是Web环境创建AnnotationConfigServletWebServerApplicationContext,否则创建默认的IOC容器AnnotationConfigApplicationContext

      4)this.refresh(context); 刷新刚创建好的IOC容器即执行IOC容器的refresh()方法

      5)this.onRefresh(); Web容器会创建嵌入式Servlet Web Server容器(参见ServletWebServerApplicationContext)

      6)ServletWebServerFactory factory = this.getWebServerFactory(); 创建ServletWebServerFactory(Servlet容器工厂),后置处理器工作获取所有的定制器定制Servlet容器的相关配置

      7)this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});  创建 WebServer (Servlet容器)并启动

    外部Servlet容器&支持JSP

      1)创建一个war项目

      2)嵌入式Tomcat依赖指定为provided

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
        <scope>provided</scope>
    </dependency>
    

      3)编写一个SpringBootServletInitializer的子类并复写configure方法

    package com.wjz.springboot;
    
    import org.springframework.boot.builder.SpringApplicationBuilder;
    import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
    
    public class ServletInitializer extends SpringBootServletInitializer {
    
        @Override
        protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
            return application.sources(SpringBootJspApplication.class);
        }
    }  

      4)启动外部Tomcat

    外部Servlet容器启动流程

      1)Servlet3.0的一个规范,容器启动时会创建每一个应用实现了javax.servlet.ServletContainerInitializer的实例

      2)寻找各个应用每个jar包路径 META-INF/services/ 路径下的文件,文件名为 javax.servlet.ServletContainerInitializer,内容是实现类的全类名,参考 spring-web jar包下对应的路径文件

    org.springframework.web.SpringServletContainerInitializer
    

      3)还可以使用@HandlesTypes注解,在容器启动时传入类

      4)SpringServletContainerInitializer将@HandlesTypes({WebApplicationInitializer.class})标注的这个类型的所有类传入到onStartup方法的Set<Class<?>>中,

    将这些类型创建实例且调用各自的onStartup方法

      

       相当于SpringBootServletInitializer类会创建对象并调用onStartup方法,该方法中会创建IOC容器

    public void onStartup(ServletContext servletContext) throws ServletException {
    	this.logger = LogFactory.getLog(this.getClass());
    	// 创建IOC容器
    	WebApplicationContext rootAppContext = this.createRootApplicationContext(servletContext);
    	if (rootAppContext != null) {
    		servletContext.addListener(new ContextLoaderListener(rootAppContext) {
    			public void contextInitialized(ServletContextEvent event) {
    			}
    		});
    	} else {
    		this.logger.debug("No ContextLoaderListener registered, as createRootApplicationContext() did not return an application context");
    	}
    }
    

      

  • 相关阅读:
    【Monkey】Monkey稳定性测试常用命令
    【Monkey】Monkey基础概念
    推荐一些前端开发常用框架
    MySql 分表复制表结构和数据脚本
    通过apo切面,动态切换数据源
    MySq 数据迁移,把单字段数据解析出插入到另一张表中
    hadoop 集群搭建与mapreduce开发实战(二)
    hadoop mvn项目 pom配置
    hadoop 集群搭建与mapreduce开发实战(一)
    MySql 存储过程及调用方法
  • 原文地址:https://www.cnblogs.com/BINGJJFLY/p/11878064.html
Copyright © 2020-2023  润新知