1. 程式人生 > >SpringBoot嵌入式Servlet配置原理

SpringBoot嵌入式Servlet配置原理

SpringBoot嵌入式Servlet配置原理

SpringBoot修改伺服器配置

  • 配置檔案方式方式修改,實際修改的是ServerProperties檔案中的值
server.servlet.context-path=/crud
server.port=8081
  • Java程式碼方式修改。通過實現WebServerFactoryCusomizer介面來獲取到達ConfigurableServletWebServerFactory的通道,ConfigurableServletWebServerFactory中提供了很多的方法用來修改伺服器配置。
@Component
public class ServletHandler implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {

    @Override
    public void customize(ConfigurableServletWebServerFactory factory) {
        factory.setPort(8083);
    }
}

SpringBoot使用原生web元件

在之前的Web專案中,我們會通過web.xml來註冊三大元件,在springboot中我們通過提供的類註冊三大元件

  • Servlet。通過ServletRegistrationBean來註冊一個Servlet
@Bean
    public ServletRegistrationBean myServlet(){
        ServletRegistrationBean registration = new ServletRegistrationBean(new MyServlet(),"/hello");
        return registration;
    }
  • Filter。通過FilterRegistrationBean來祖冊Filter
@Bean
    public FilterRegistrationBean myFilter(){
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new MyFilter());
        filterRegistrationBean.setUrlPatterns(Arrays.asList("/hello"));
        return filterRegistrationBean ;
    }
  • Listener。通過ServletListenerRegistrationBean來註冊一個監聽器
@Bean
    public ServletListenerRegistrationBean myServletListener(){
        ServletListenerRegistrationBean registrationBean = new ServletListenerRegistrationBean();
        registrationBean.setListener(new MyServletContextListener());
        return registrationBean ;
    }

Spring使用其他伺服器

SpringBoot提供了三個伺服器工廠,Tomcat,Jetty,Undertow,預設使用了Tomcat

  • 使用Jetty。需要排除Tomcat依賴
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                    <groupId>org.springframework.boot</groupId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <artifactId>spring-boot-starter-jetty</artifactId>
            <groupId>org.springframework.boot</groupId>
        </dependency>
  • 使用Undertow伺服器。同Jetty一樣
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                    <groupId>org.springframework.boot</groupId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <artifactId>spring-boot-starter-undertow</artifactId>
            <groupId>org.springframework.boot</groupId>
        </dependency>

SpringBoot伺服器自動配置原理

  • Springboot通過WebServerInitializedEvent來實現伺服器自動配置,通過這個類來載入一個WebServer
public abstract class WebServerInitializedEvent extends ApplicationEvent {
    protected WebServerInitializedEvent(WebServer webServer) {
        super(webServer);
    }
  • 通過WebServer來建立固定的伺服器。
    • TomcatWebServer
    • JettyWebServer
    • NettyWebServer
    • UndertowWebServer
public interface WebServer {
    void start() throws WebServerException;

    void stop() throws WebServerException;

    int getPort();
}

SpringBoot啟動Tomcat伺服器的過程

  • SpringBoot啟動方法
SpringApplication.run(DemoApplication.class, args)
  • 呼叫SpringAllication.run方法返回了ConfigurableApplicationContext物件
 public ConfigurableApplicationContext run(String... args) {
     context = this.createApplicationContext();//建立了一個Application物件
     this.refreshContext(context);//重新整理ApplicationContext
protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                switch(this.webApplicationType) {
                case SERVLET:
                    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);
    }
  • 建立了AnnotationConfigReactiveWebServerApplicationContext這個類最終實現了AbstractApplicationContext
private void refreshContext(ConfigurableApplicationContext context) {
        this.refresh(context);
        if (this.registerShutdownHook) {
            try {
                context.registerShutdownHook();
            } catch (AccessControlException var3) {
            }
        }

    }
protected void refresh(ApplicationContext applicationContext) {
        Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
        ((AbstractApplicationContext)applicationContext).refresh();
    }
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();
                //呼叫子類的重新整理方法,最終呼叫的是建立ApplicationContext容器中所選擇的容器即ServletWebServerApplicationContext類中的方法
                this.onRefresh();
                this.registerListeners();
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (BeansException var9) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                }

                this.destroyBeans();
                this.cancelRefresh(var9);
                throw var9;
            } finally {
                this.resetCommonCaches();
            }

        }
    }
protected void onRefresh() {
        super.onRefresh();

        try {
            //建立了web容器
            this.createWebServer();
        } catch (Throwable var2) {
            throw new ApplicationContextException("Unable to start web server", var2);
        }
    }
private void createWebServer() {
        WebServer webServer = this.webServer;
        ServletContext servletContext = this.getServletContext();
    //當容器中沒有伺服器的時候
        if (webServer == null && servletContext == null) {
            //建立一個web伺服器,
            ServletWebServerFactory factory = this.getWebServerFactory();
            this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
        } else if (servletContext != null) {
            try {
                this.getSelfInitializer().onStartup(servletContext);
            } catch (ServletException var4) {
                throw new ApplicationContextException("Cannot initialize servlet context", var4);
            }
        }

        this.initPropertySources();
    }
protected ServletWebServerFactory getWebServerFactory() {
    //獲取了容器中ServletWebServerFactory型別的容器
        String[] beanNames = this.getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
        if (beanNames.length == 0) {
            throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.");
        } else if (beanNames.length > 1) {
            throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
        } else {
            //建立了web伺服器
            return (ServletWebServerFactory)this.getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
        }
    }
  • 通過this.getWebServerFactory方法建立了web伺服器,通過this.getBeanFactory()獲取了容器中所存在的型別為ServletWebServerFactory型別的容器,然後獲取bean建立了Tomcat物件