问:标题说的什么意思?
答:简单说,一个spring boot应用(我这里,版本升到2.1.7.Release了,没什么问题),默认使用了tomcat作为底层容器来接收和处理连接。 我这里,在依赖中排除了tomcat,使用Netty作为了替代品。优势在于,启动飞快,线程数量完全可控(多少个netty的boss、worker线程,多少个业务线程),如果能优化得好,效率会很高(我这个还有很多优化空间,见文末总结)
流程图如下(中间的三个handler是自定义的):
这个东西,年初我就弄出来了,然后用在了某个我负责的微服务里,之前一直想写,但是一直没把demo代码从微服务里抽出来,然后就一直拖着。前一阵吧,把代码抽出来了,然后又觉得要优化下,不然有些低级问题怎么办?
前一阵抽了代码出来,然后想着优化下,结果忙起来搞忘了,而且优化无底洞啊,所以先不优化了,略微补了些注释,就发上来了,希望大家看到后,多多批评指正。
先附上代码地址:https://gitee.com/ckl111/Netty_Spring_MVC_Sample/
启动后,访问::8081/test.do即可。
实现大体思路排除掉tomcat依赖
解决掉报错,保证spring mvc的上下文正常启动
启动netty容器,最后一个handler负责将servlet request交给dispatcherServlet处理
具体实现解决dispatcherServlet不能正常工作的问题
问题1:缺少servletContext报错经过追踪发现,这个servletContext来源于:org.springframework.web.context.support.GenericWebApplicationContext中的servletContext字段
解决办法:
在META-INF/spring.factories中,定义了一个listener,来参与spring boot启动时的生命周期:
org.springframework.boot.SpringApplicationRunListener=com.ceiec.router.config.MyListener在我的自定义listener中,实现org.springframework.boot.SpringApplicationRunListener,然后重写如下方法:
package com.ceiec.router.config; import com.ceiec.router.config.servletconfig.MyServletContext; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplicationRunListener; import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.PropertySource; import javax.servlet.ServletContext; import java.util.Map; @Data @Slf4j public class MyListener implements SpringApplicationRunListener { public MyListener(SpringApplication application, String[] args) { super(); } ... @Override public void contextPrepared(ConfigurableApplicationContext context) { // 这里手动new一个servletContext,然后设置给spring上下文 ServletContext servletContext = new MyServletContext(); ServletWebServerApplicationContext applicationContext = (ServletWebServerApplicationContext) context; applicationContext.setServletContext(servletContext); } ... }自定义实现了com.ceiec.router.config.servletconfig.MyServletContext,这个很简单,继承spring test包中的org.springframework.mock.web.MockServletContext即可。
package com.ceiec.router.config.servletconfig; import org.springframework.mock.web.MockServletContext; import javax.servlet.Filter; import javax.servlet.FilterRegistration; import javax.servlet.Servlet; import javax.servlet.ServletRegistration; public class MyServletContext extends MockServletContext{ @Override public ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet) { return null; } @Override public FilterRegistration.Dynamic addFilter(String filterName, Filter filter){ return null; } } 问题2:暂时没有。之前的版本本来有一个问题,升到spring boot 2.1.7后,好像不需要了,先不管。
问题3:怎么保证少了tomcat后,dispatcherServlet还能用?准确地说,dispatcherServlet这个东西和tomcat是两回事,以前写struts 2的时候,也没dispatcherServlet这个类,不是吗?
所以,在spring boot启动时,并不强依赖底层容器,dispatcherServlet 这个bean会自动装配,装配代码在
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration.DispatcherServletConfiguration
@Configuration @Conditional(DefaultDispatcherServletCondition.class) @ConditionalOnClass(ServletRegistration.class) @EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class }) protected static class DispatcherServletConfiguration { private final HttpProperties httpProperties; private final WebMvcProperties webMvcProperties; //这里自动装配DispatcherServlet @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) public DispatcherServlet dispatcherServlet() { DispatcherServlet dispatcherServlet = new DispatcherServlet(); dispatcherServlet.setDispatchOptionsRequest( this.webMvcProperties.isDispatchOptionsRequest()); dispatcherServlet.setDispatchTraceRequest( this.webMvcProperties.isDispatchTraceRequest()); return dispatcherServlet; } 问题4:自动装配DispatcherServlet后,处理请求时报错: