Web 服务持有路径,Tomcat 持有 Service(默认实现为 StandardService),Service 持有 Connector 和 Container,业务上来看,一个 Service 对应一个域名。
下面展示了启动嵌入式 Tomcat 容器代码,基本与 Spring Boot 保持了一致。
Tomcat tomcat = new Tomcat();
File baseDir = new File("/Users/xuzeng/Documents/usr/Java/private/network-things/servlets/tomcat_file");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
tomcat.getService().addConnector(connector);
connector.setPort(8026);
connector.setAllowTrace(true);
connector.setURIEncoding(StandardCharsets.UTF_8.name());
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());
// 注册 Conntext Servlet
prepareContext(tomcat.getHost(), new HelloServlet());
for (Container child : tomcat.getHost().findChildren()) {
if(child instanceof StandardContext){
StandardContext sc = ((StandardContext) child);
sc.loadOnStartup(sc.findChildren());
}
}
tomcat.start();
在 Spring Boot 中,ServletContainerInitializer 被封装成了 TomcatStarter(对应 HTTP) 和 WsSci(对应 WebSocket),对 WebMVC 只暴露了 org.springframework.boot.web.servlet.ServletContextInitializer 接口,并提供了默认实现 ServletRegistrationBean,所以只要声明一个 ServletRegistrationBean bean 即可注册 Servlet。
private static void configureEngine(Engine engine) {
engine.setBackgroundProcessorDelay(30);
engine.getService().addLifecycleListener(System.out::println);
}
private static void prepareContext(Host host, ServletContainerInitializer... initializers) {
StandardContext context = new StandardContext();
context.setPath("");
context.setDocBase("/Users/xuzeng/Documents/usr/Java/private/network-things/servlets/tomcat_file/docbase");
context.setName("");
context.setDisplayName("");
context.addLifecycleListener(new Tomcat.FixContextListener());
// 往 Context 添加 ServletContainerInitializer,可以利用此回调接口添加 Servlet
for (ServletContainerInitializer sci : initializers){
context.addServletContainerInitializer(sci, Collections.emptySet());
}
context.addValve(new ValveBase() {
@Override
public void invoke(Request request, Response response) throws IOException, ServletException {
System.out.println(request);
System.out.println(response);
getNext().invoke(request, response);
}
});
host.addChild(context);
}
Hello Servlet
public class HelloServlet extends HttpServlet implements ServletContainerInitializer {
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
System.out.println("hello servlet service");
super.service(req, res);
}
// 该方法将在 Tomcat 初始化期间被调用,注册 Servlet
@Override
public void onStartup(Set<Class<?>> c, ServletContext ctx) {
ServletRegistration.Dynamic rd = ctx.addServlet("hello-servlet", this);
rd.addMapping("/");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletOutputStream out = resp.getOutputStream();
out.write("hello heroku\\n".getBytes());
out.flush();
out.close();
}
}
网络处理流程
//todo 逻辑整理
http1.1