Spring Boot 20天入门(day5)

Springboot嵌入式Servlet容器

Springboot默认使用Tomcat作为嵌入式的Servlet容器。

Springboot嵌入式Servlet容器配置修改

1)、properties配置文件修改

// 修改请嵌入式Servlet容器启动端口号
server.port=9527
// 配置访问默认路径
server.context-path=/crud

2)、WebServerFactoryCustomizer(web服务器工厂定制器)

// 需要写在Spring的配置类中,并添加到容器中,重写customizer方法,实现自我定制
@SpringBootConfiguration
public class MyServerConfig {
    @Bean
    public WebServerFactoryCustomizer<ConfigurableWebServerFactory> customizer(){
        return factory -> factory.setPort(9888);
    }
}

注册Servlet三大组件(Servlet,Filter,Listener)

@SpringBootConfiguration
public class MyServerConfig {

    //配置三大组件
    // Servlet
    @Bean
    public ServletRegistrationBean<MyServlet> myServlet() {
        ServletRegistrationBean<MyServlet> registrationBean = new ServletRegistrationBean<>(new MyServlet(), "/myServlet");
        return registrationBean;
    }

    // filter
    @Bean
    public FilterRegistrationBean<MyFIlter> myFiltern() {
        FilterRegistrationBean<MyFIlter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new MyFIlter());
        registrationBean.setUrlPatterns(Arrays.asList("/hello", "/myServlet"));
        return registrationBean;
    }

    // listener
    @Bean
    public ServletListenerRegistrationBean<MyLIstener> myListener() {
        ServletListenerRegistrationBean<MyLIstener> listener = new ServletListenerRegistrationBean<>();
        listener.setListener(new MyLIstener());
        return listener;
    }

}

Springboot帮我们自动配置SpringMVC的时候,自动注册SpringMVC的前端控制器:DispatchServlet

@Configuration(proxyBeanMethods = false)
	@Conditional(DispatcherServletRegistrationCondition.class)
	@ConditionalOnClass(ServletRegistration.class)
	@EnableConfigurationProperties(WebMvcProperties.class)
	@Import(DispatcherServletConfiguration.class)
	protected static class DispatcherServletRegistrationConfiguration {

		@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
		@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
		public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
				WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
			DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
					webMvcProperties.getServlet().getPath());
            // 默认拦截 : / 所有请求,包括静态资源 ,但不拦截jsp请求,/*会拦截
			registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
			registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
			multipartConfig.ifAvailable(registration::setMultipartConfig);
			return registration;
		}

	}

切换嵌入式Servlet容器

以下以jetty为例:

首先将tomcat的依赖排除,然后引入jetty的依赖,其他容器也是如此

<dependencies>
        <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>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-jetty</artifactId>
        </dependency>

嵌入式Servlet容器自动配置原理

Springboot 通过运行主程序类的main()方法来启动Springboot应用,应用启动后调用ServletWebServerApplicationContext这个类中的createWebServer()方法:

private void createWebServer() {
		WebServer webServer = this.webServer;
		ServletContext servletContext = getServletContext();
		if (webServer == null && servletContext == null) {
			ServletWebServerFactory factory = getWebServerFactory();
			this.webServer = factory.getWebServer(getSelfInitializer());
			getBeanFactory().registerSingleton("webServerGracefulShutdown",
					new WebServerGracefulShutdownLifecycle(this.webServer));
			getBeanFactory().registerSingleton("webServerStartStop",
					new WebServerStartStopLifecycle(this, this.webServer));
		}
		else if (servletContext != null) {
			try {
				getSelfInitializer().onStartup(servletContext);
			}
			catch (ServletException ex) {
				throw new ApplicationContextException("Cannot initialize servlet context", ex);
			}
		}
		initPropertySources();
	}

该方法最终能获得一个与当前应用所导入的Servlet类型相匹配的web服务工厂定制器,假设你导入的Servlet依赖是Tomcat:

在这里插入图片描述

那么最终会生成tomcat web服务工厂定制器,定制Servlet容器配置,并通过上述代码中的getWebServer方法创建对应的Servlet容器,并启动。

this.webServer = factory.getWebServer(getSelfInitializer());

然后代码会执行来到EmbeddedWebServerFactoryCustomizerAutoConfiguration(嵌入式web服务工厂定制器自动配置类)

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication
@EnableConfigurationProperties(ServerProperties.class)
public class EmbeddedWebServerFactoryCustomizerAutoConfiguration {

该自动配置类和其他的一样,会标注一个注解,用于从配置文件中获取配置信息。

// 从配置文件中获取前缀为server的配置信息
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {}

然后,根据导入的配置,该配置类会自动创建相应类型的容器工厂定制器,以Tomcat为例:

/**
	 * Nested configuration if Tomcat is being used.
	 */
	@Configuration(proxyBeanMethods = false)
	// 导入的依赖是Tomcat,创建Tomcat的定制器
	@ConditionalOnClass({ Tomcat.class, UpgradeProtocol.class })
	public static class TomcatWebServerFactoryCustomizerConfiguration {

		@Bean
		public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(Environment environment,
				ServerProperties serverProperties) {
			return new TomcatWebServerFactoryCustomizer(environment, serverProperties);
		}
	}

这样,Tomcat的定制器就创建完成了,对应的服务配置类也被添加到容器中。

然后,Springboot会调用一个WebServerFactoryCustomizerBeanPostProcessor(web服务工厂定制器组件的后置处理器)的类,该类负责在容器配置之前完成初始化操作:

该类会先从容器中拿到配置好的嵌入式web服务工厂定制器

@SuppressWarnings({ "unchecked", "rawtypes" })
	private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() {
		return (Collection) this.beanFactory.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();
	}

经过调试,改方法从IOC容器中拿到5个定制器,包括前面创建的TomcatWebServerFactoryCustomizer定制器:

img

获得所有的定制器后,后置处理器会调用定制器的customize方法来对嵌入式Servlet容器进行配置(默认或自定义的配置)

@SuppressWarnings("unchecked")
	private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
		LambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory)
				.withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
				.invoke((customizer) -> customizer.customize(webServerFactory));
	}

配置器的customize方法:

获得一个服务器的配置类,进行自动配置。

@Override
	public void customize(TomcatServletWebServerFactory factory) {
		ServerProperties.Tomcat tomcatProperties = this.serverProperties.getTomcat();
		if (!ObjectUtils.isEmpty(tomcatProperties.getAdditionalTldSkipPatterns())) {
			factory.getTldSkipPatterns().addAll(tomcatProperties.getAdditionalTldSkipPatterns());
		}
		if (tomcatProperties.getRedirectContextRoot() != null) {
			customizeRedirectContextRoot(factory, tomcatProperties.getRedirectContextRoot());
		}
		customizeUseRelativeRedirects(factory, tomcatProperties.isUseRelativeRedirects());
		factory.setDisableMBeanRegistry(!tomcatProperties.getMbeanregistry().isEnabled());
	}

Tomcat的WebServer工厂类创建Tomcat对象实例,进行属性配置,引擎设置等等:

@Override
	public WebServer getWebServer(ServletContextInitializer... initializers) {
		if (this.disableMBeanRegistry) {
			Registry.disableRegistry();
		}
		Tomcat tomcat = new Tomcat();
		File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
		tomcat.setBaseDir(baseDir.getAbsolutePath());
		Connector connector = new Connector(this.protocol);
		connector.setThrowOnFailure(true);
		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);
	}

到此,嵌入式的Servlet容器就配置完成了。

嵌入式Servlet容器启动原理

上面说到,嵌入式的Servlet容器配置完成了,接下来会生成tomcat server对象,在构造函数里面调用一个我们熟悉的initialize()方法

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

public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
		Assert.notNull(tomcat, "Tomcat Server must not be null");
		this.tomcat = tomcat;
		this.autoStart = autoStart;
		this.gracefulShutdown = (shutdown == Shutdown.GRACEFUL) ? new GracefulShutdown(tomcat) : null;
		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())) {
						// Remove service connectors so that protocol binding doesn't
						// happen when the service is started.
						removeServiceConnectors();
					}
				});

				// Start the server to trigger initialization listeners
				this.tomcat.start();

				// We can re-throw failure exception directly in the main thread
				rethrowDeferredStartupExceptions();

				try {
					ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
				}
				catch (NamingException ex) {
					// Naming is not enabled. Continue
				}

				// Unlike Jetty, all Tomcat threads are daemon threads. We create a
				// blocking non-daemon to stop immediate shutdown
				startDaemonAwaitThread();
			}
			catch (Exception ex) {
				stopSilently();
				destroySilently();
				throw new WebServerException("Unable to start embedded Tomcat", ex);
			}
		}
	}
	
	

initialize()方法中,tomcat初始化完毕,会调用start()方法。

这样,嵌入式的tomcat容器就被启动起来了。

总结

1、Spring Boot 根据导入的依赖信息,自动创建对应的web服务工厂定制器;
2、web服务工厂定制器组件的后置处理器获取所有类型为web服务工厂定制器的组件(包含实现WebServerFactoryCustomizer接口,自定义的定制器组件),依次调用customize()定制接口,定制Servlet容器配置;
3、嵌入式的Servlet容器工厂创建tomcat容器,初始化并启动容器。

以上…

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值