步骤
1.启动tomcat
2.建立socket连接 用了一个线程池去处理并发请求的问题
3.处理socket连接
3.1解析客户端发送过来的http请求 封装成为request对象 因为要遵循servlet规范所以要实现HttpservletRequest里面的方法
3.2 请求格式分析
3.3请求行-请求方法
3.4请求行-请求路径
3.5请求行-请求协议
3.6-封装成request对象
3.7-因为要遵循servlet规范,则request间接实现了HttpservletRequest那些方法
3.8构造一个response对象(用来构造响应信息)
3.9因为要遵循servlet规范 所以要实现HttpservletResponse里面的方法 此时这个response是默认值
3.10调用servlet的service 方法处理请求,
3.11servlet的service会方法会根据请求类型(get,post)调用相应的
(doget,dopost)并把结果写到response的缓存里面(因为需要一起将响应行、响应体、响应头返回给客户端)
报空指针的原因
4.返回响应,调用response的complete方法 把响应行 响应体 响应头都返回给客户端
4.1现在还存在的问题就是,客户端等待服务端响应的数据,但是不知道响应的长度,就会出现阻塞的状态。tomcat内部帮我们完成了这个事情
4.2发送响应行
4.3发送响应头
4.4发送响应体
5.因为Tomcat的webapps目录是用来存放Web应用程序的,默认情况下,每一个Web应用都会被部署到这个目录下。当Tomcat服务器启动时,它会自动加载并运行webapps目录下的所有Web应用。所有我们要模拟一下这个目录结构
5.tomcat部署应用实现 在启动之前tomcat需要把所有的应用都加载完成
找到所有的应用
6.遍历每一个应用(需要全路径),拿到每一个应用下的class文件,
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();