手写Tomcat

63 阅读3分钟

步骤

1.启动tomcat

image.png

2.建立socket连接  用了一个线程池去处理并发请求的问题

image.png

3.处理socket连接

3.1解析客户端发送过来的http请求 封装成为request对象 因为要遵循servlet规范所以要实现HttpservletRequest里面的方法

3.2 请求格式分析

image.png

3.3请求行-请求方法    

image.png

3.4请求行-请求路径   

image.png

3.5请求行-请求协议

image.png

3.6-封装成request对象

image.png image.png

3.7-因为要遵循servlet规范,则request间接实现了HttpservletRequest那些方法

image.png image.png

3.8构造一个response对象(用来构造响应信息)

image.png

3.9因为要遵循servlet规范 所以要实现HttpservletResponse里面的方法 此时这个response是默认值

image.png image.png

3.10调用servlet的service 方法处理请求,

image.png

3.11servlet的service会方法会根据请求类型(get,post)调用相应的
(doget,dopost)并把结果写到response的缓存里面(因为需要一起将响应行、响应体、响应头返回给客户端)

image.png

image.png 报空指针的原因 image.png image.png

4.返回响应,调用response的complete方法 把响应行 响应体 响应头都返回给客户端  

image.png image.png

4.1现在还存在的问题就是,客户端等待服务端响应的数据,但是不知道响应的长度,就会出现阻塞的状态。tomcat内部帮我们完成了这个事情
4.2发送响应行

image.png

4.3发送响应头

image.png

4.4发送响应体

image.png

5.因为Tomcat的webapps目录是用来存放Web应用程序的,默认情况下,每一个Web应用都会被部署到这个目录下。当Tomcat服务器启动时,它会自动加载并运行webapps目录下的所有Web应用。所有我们要模拟一下这个目录结构

image.png

  5.tomcat部署应用实现   在启动之前tomcat需要把所有的应用都加载完成
  找到所有的应用  

image.png

  6.遍历每一个应用(需要全路径),拿到每一个应用下的class文件,      

image.png

  7.通过class文件用类加载 器反射生成class实例对象,
  判断此实例对象是不是servlet实例对象,
  如果是拿到所有servlet对应的urlpattern数组   
private void deployApp(File webapps,String appName) throws MalformedURLException, ClassNotFoundException, InstantiationException, IllegalAccessException {  
//部署应用,看有哪些servlet webapps下面的hello  
  
Context context = new Context(appName);  
File appdirectory = new File(webapps, appName); // 拿到webapps下的hello的绝对路径  
File classesDirectory = new File(appdirectory, "classes");//拿到classes的绝对路径  
List<File> allFilePath = getAllFilePath(classesDirectory);//拿到classes下面的所有文件的路径  
for (File clazz : allFilePath) {  
//因为加载类的时候需要用到包名加上类名  
String name = clazz.getPath();  
System.out.println(name);  
name = name.replace(classesDirectory.getPath() + "\\", "");  
System.out.println(name);  
name = name.replace(".class", "");  
System.out.println(name);  
name = name.replace("\\", ".");  
System.out.println(name);  
  
//自定义类加载器用来加载webapps下面的类,而不用默认的加载器  
WebappClassLoader classLoader = new WebappClassLoader(new URL[]{classesDirectory.toURL()});  
Class<?> servletClass = classLoader.loadClass(name);  
//System.out.println(servletClass);  
if (HttpServlet.class.isAssignableFrom(servletClass)) {  
System.out.println(servletClass);  
if (servletClass.isAnnotationPresent(WebServlet.class)) {  
WebServlet annotation = servletClass.getAnnotation(WebServlet.class);  
String[] urlPatterns = annotation.urlPatterns();  
for (String urlPattern : urlPatterns) {  
context.addUrlPatternMapping(urlPattern, (Servlet) servletClass.newInstance());  
}  
  
}  
}   
  
}  
map.put(appName,context);  
}
8. 把url->servlet的对象的映射,存到一个map里,
每个app需要一个map,所以,定义app的类,Context,
把map以及appName放到Context里
9.把context,以及appname放到一个contextmap里,key: APPName, Value:Context

public class Context {  
private Map<String ,Servlet> map=new HashMap<>();  
  
  
private String appname;  
  
public Context(String appname) {  
this.appname = appname;  
}  
  
public void addUrlPatternMapping(String urlPattern, Servlet servlet) {  
map.put(urlPattern,servlet);  
}  
//请求的时候需要根据urlpattern和servlet进行匹配  
public Servlet urlPatternMapping(String urlpattern){  
for(String key:map.keySet()){  
if(key.contains(urlpattern)){  
return map.get(key);  
}  
}  
return null;  
}  
}
10. 修改请求流程,从request拿到url,解析出来appName以及请求路径
11. 从ContextMap中,通过appName,找到Context
12. 从Context的map中,通过路径,获取到servlet
13. 调用service
15. 如果找不到servlet,调用自定义的DefaultServlet的service
String requestUrl = request.getRequestURL().toString();  
System.out.println(requestUrl);  
String substring = requestUrl.substring(1);  
String[] parts = substring.split("/");  
String appName = parts[0];  
String urlPattern = parts[1];  
Context context = tomcat.getContext(appName);  
Servlet servlet = context.urlPatternMapping(urlPattern);  
servlet.service(request,response);  
response.complete();