让你想手写一个tomcat容器 能做到吗
Tomcat是Servlet容器,这句话真的是精髓,但有怎么理解呢?
Tomcat的代码实现,原理
尝试自己手写一份tomcat服务,类似tomcat的功能。
tomcat是servlet容器。所以我们自己写的tomcat也要满足这个功能,能接受处理servlet请求。
这一段的代码还是很经典的,让我们可以真正的明白“tomcat是Servlet容器”这样一句话。
Service 容器包括了 Engine容器
Engine 容器包括了Host容器
Host 容器包括了Context容器
可以想象,Service容器是在外层的容器,他还有一个对外的Connector 接口,提供对外的连接服务。
大致思路是这样的:
1. 先创建一个 Tomcat 对象
2. Tomcat 对象创建一个 Server对象
3. Server对象创建一个Service容器
4. Service容器内创建一个Engine容器
5. Engine容器内创建一个Host容器
6. Host容器内创建一个Context容器
这样相当于我们手写了一个tomcat服务,实现对servlet请求的接收,处理工作。文章来源地址https://www.toymoban.com/news/detail-488238.html
创建一个tomcat容器
package com.simulate.protocol.http;
import org.apache.catalina.*;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardEngine;
import org.apache.catalina.core.StandardHost;
import org.apache.catalina.startup.Tomcat;
public class HttpServer {
public void start(String hostname, Integer port) {
Tomcat tomcat = new Tomcat();
Server server = tomcat.getServer();
Service service = server.findService("Tomcat");
Connector connector = new Connector();
connector.setPort(port);
Engine engine = new StandardEngine();
engine.setDefaultHost(hostname);
Host host = new StandardHost();
host.setName(hostname);
String contextPath = "";
Context context = new StandardContext();
context.setPath(contextPath);
context.addLifecycleListener(new Tomcat.FixContextListener());
host.addChild(context);
engine.addChild(host);
service.setContainer(engine);
service.addConnector(connector);
// 接收到的所有请求最终都会转发到 DispatcherServlet 上面去处理
tomcat.addServlet(contextPath, "dispatcher", new DispatcherServlet());
context.addServletMappingDecoded("/*", "dispatcher");
try {
tomcat.start();
tomcat.getServer().await();
} catch (LifecycleException e) {
e.printStackTrace();
}
}
}
Servlet转发
package com.simulate.protocol.http;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class DispatcherServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
new HttpServerHandler().handler(req, resp);
}
}
Servlet的处理工作
package com.simulate.protocol.http;
import com.alibaba.fastjson.JSONObject;
import com.simulate.framework.Invocation;
import com.simulate.provider.api.HelloService;
import com.simulate.provider.impl.HelloServiceImpl;
import org.apache.commons.io.IOUtils;
import com.simulate.provider.LocalRegister;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
public class HttpServerHandler {
// FIXME 这里应该是 服务启动的时候,自动注入进去,而不是在这个类中才注入,如果其他地方有用到这些对象的话,已经晚了
public HttpServerHandler (){
LocalRegister.regist(HelloService.class.getName(), HelloServiceImpl.class);
}
public void handler(HttpServletRequest req, HttpServletResponse resp) {
// 获取到Request 请求中的url参数信息,然后可以转发给具体的servlet对象,进行处理。这里只做转发的工作。
// 这里可以做很多工作,比如访问数据库,数据写入文件等,而我这里做的是通过反射,创建对象,调用对象的方法。
try {
Invocation invocation = JSONObject.parseObject(req.getInputStream(), Invocation.class);
var interfaceName = invocation.getInterfaceName();
var implClass = LocalRegister.get(interfaceName);
var method = implClass.getMethod(invocation.getMethodName(), invocation.getParamType());
var result = (String) method.invoke(implClass.newInstance(), invocation.getParams());
System.out.println("tomcat:" + result);
IOUtils.write(result, resp.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
怎么启动这个tomcat server端呢?可以这样做:
package com.simulate.protocol.http;
import com.simulate.framework.URL;
public class HttpServerTest {
public static void main(String[] args) {
URL url = new URL("localhost", 9090);
HttpServer httpServer = new HttpServer();
httpServer.start(url.getHostname(), url.getPort());
System.out.println("==========[httpserver start!]==============");
}
}
上面的 tomcat启动完成后,就处于等待监听的状态,本地端口是9090的等待状态。等待什么?等待客户端的请求,如果有客户端请求 这个地址 http://localhost:9090/xxx 就会被这个tomcat容器捕捉到,进行后续的处理。
那我们继续看看客户端怎么发起请求呢?
我这里用的是jdk11, 在jdk11中,把HttpClient这个对象纳入进去了,而不需要像以前一样,还要依赖第三方的jar包。如果你用的不是jdk11,可以使用下面注释的代码,就是原生的HttpURLConnection 对象进行 http 连接,请求操作。
package com.simulate.protocol.http;
import com.alibaba.fastjson.JSONObject;
import com.simulate.framework.Invocation;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class HttpClient {
public String send(String hostname, Integer port, Invocation invocation) {
try {
var request = HttpRequest.newBuilder()
.uri(new URI("http", null, hostname, port, "/", null, null))
.POST(HttpRequest.BodyPublishers.ofString(JSONObject.toJSONString(invocation)))
.build();
var client = java.net.http.HttpClient.newHttpClient();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
// URL url = new URL("http", hostname, port, "/");
// HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
//
// httpURLConnection.setRequestMethod("POST");
// httpURLConnection.setDoOutput(true);
//
// OutputStream outputStream = httpURLConnection.getOutputStream();
// ObjectOutputStream oos = new ObjectOutputStream(outputStream);
//
// oos.writeObject(invocation);
// oos.flush();
// oos.close();
//
// InputStream inputStream = httpURLConnection.getInputStream();
// String result = IOUtils.toString(inputStream);
return result;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
}
return null;
}
}
那这个客户端怎么使用呢?
package com.simulate.protocol.http;
import com.simulate.framework.Invocation;
import com.simulate.framework.URL;
import com.simulate.provider.api.HelloService;
public class HttpClientTest {
public static void main(String[] args) {
URL url = new URL("localhost", 9090);
HttpClient httpClient = new HttpClient();
String data = httpClient.send("localhost", 9090, new Invocation(HelloService.class.getName(), "sayHello",
new Object[]{"World"}, new Class[]{String.class}));
// LocalRegister.regist(HelloService.class.getName(), HelloServiceImpl.class);
System.out.println("===========");
System.out.println(data);
System.out.println("===========");
}
}
其他代码:
package com.simulate.framework;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.io.Serializable;
@Data
@AllArgsConstructor
public class Invocation implements Serializable {
private String interfaceName;
private String methodName;
private Object[] params;
private Class[] paramType;
}
package com.simulate.provider.api;
public interface HelloService {
String sayHello(String userName);
}
package com.simulate.provider.impl;
import com.simulate.provider.api.HelloService;
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String userName) {
return "Hello: " + userName;
}
}
先启动HttpServerTest中的main方法
在启动HttpClientTest 中的main方法, 就能看到结果了。文章来源:https://www.toymoban.com/news/detail-488238.html
这样相当于我们手写了一个tomcat服务,实现对servlet请求的接收,处理工作。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.simulate</groupId>
<artifactId>simulate</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.16.Final</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>9.0.12</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.51</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-client</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.13</version>
</dependency>
</dependencies>
</project>
到了这里,关于【Tomcat】Tomcat的实现,tomcat内部是怎么实现的,它是怎么做到对Servlet请求的处理的。的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!