1、tomcat实现原理
servlet 没有主方法main,依赖tomcat才能运行,因为tomcat 有主方法main,由java编写
servlet中doGet和doPost方法属于非静态方法,只能依托new对象存在,tomcat无法new出来对象,因此tomcat无法事先知道他们的存在。而任何语言都可以通过类的所在的路径或目录获取类信息,去某个目录下遍历所有子文件,能够获取所有文件的路径信息。tomcat可以通过servlet注解,找到对应的类,servlet注解相当于给类加了标记。
Tomcat和Servlet的关系是,Tomcat是Servlet容器,负责处理客户端的请求并将请求传递给Servlet,然后将Servlet的响应返回给客户。Servlet是一种运行在支持Java语言的服务器上的组件。
静态页面请求资源时,tomcat拷贝Html页面发送给浏览器,浏览器对html进行解析,浏览器看到src/href自动发请求。Tomcat将Http请求文本接收并解析,然后封装成HttpServletRequest类型的request对象,所有的Http头数据可以通过request对象调用对应的方法查询到。Servlet要响应的信息封装为HttpServletResponse类型的response对象,通过设置response属性就可以控制要输出到浏览器的内容,然后将response交给Tomcat,Tomcat就会将其变成响应文本的格式发送给浏览器。
浏览器对请求的资源会在content-type中标记类型,然后以相同类型返回,如请求的是html页面,返回的就是html页面;请求的是css,返回的就是css。
Tomcat的工作方式是,当它启动时,会把所有的Servlet都加载到内存中。具体而言,就是前端页面需要写请求路径和请求方法,一般是通过js的ajax给前端传输请求,这些请求借助socket接收,然后socket将这些信息根据http协议对这些字符串进行解析,识别出请求路径和请求参数,根据每个servlet中的注解找出请求路径,再根据请求路径判断调用哪个servlet。找到对应的Servlet后,Tomcat会调用该Servlet的相应方法(doGet或doPost)来处理这个请求。Servlet会解析请求参数,可能会做一些处理,然后返回一个响应。Servlet返回的响应会通过Tomcat传回给前端。这可能是一个HTML页面,也可能是一些JSON数据,取决于你的Servlet是如何处理的。前端接收到响应后,浏览器会根据响应的内容进行渲染。如果响应是一个HTML页面,浏览器就会展示这个页面。如果响应是一些JSON数据,那么前端可能会根据这些数据进行一些更新或者其他的操作。
具体流程如下图所示:
2、tomcat优化
2.1 tomcat存在的问题
①挑选servlet很慢,因为跟硬盘交互;
②反射也很慢,就是遍历内存,CPU运算一次是纳秒级,这些都是毫秒级
2.2 tomcat优化一
挑选servlet后反射获取注解信息这一步可以进行优化,将servlet及对应的注解类信息放入一个map中,之后每次请求就直接通过map中的信息对servlet及注解信息进行筛选,只需要O(1)的时间复杂度,时间缩短为微秒级,tomcat启动时就会进行这一步操作。map中的value值是一个集合体,包含servlet对象,doget方法对象,dopost方法对象。
2.3 tomcat优化二
方法的调用是拷贝这一份方法压入栈中去执行,没有必要每次都创建对象执行方法,很占用内存,因为在操作系统中最小的内存分配单位是4KB,不管对象实际大小多大,每次最小分配4KB,大于4KB则是分配4KB的倍数,内存占的多影响运行速度,因此可以在tomcat启动阶段只创建一个对象,没有必要每次发送请求都创建一个对象获取方法执行,然后tomcat每次请求通过这一个对象进行方法的调用。因此,也可以只保留一个doGet和doPost方法,一个对象被多线程共用,类的对象保留一个,方法的对象保留一个,静态方法的调用不需要依托对象,非静态方法的调用需要依托对象
3、tomcat实现代码
①Client
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Scanner; public class Client extends Thread { //定义一个Socket对象 Socket socket = null; public Client(String host, int port) { try { //需要服务器的IP地址和端口号,才能获得正确的Socket对象 socket = new Socket(host, port); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } @Override public void run() { //客户端一连接就可以写数据给服务器了 new sendMessThread().start(); super.run(); try { // 读Sock里面的数据 InputStream s = socket.getInputStream(); byte[] buf = new byte[4096]; int len = 0; while ((len = s.read(buf)) != -1) { System.out.println(getdate() + " 服务器说: "+new String(buf, 0, len,"UTF-8")); } } catch (IOException e) { e.printStackTrace(); } } //往Socket里面写数据,需要新开一个线程 class sendMessThread extends Thread{ @Override public void run() { super.run(); //写操作 Scanner scanner=null; OutputStream os= null; try { scanner=new Scanner(System.in); os= socket.getOutputStream(); String in=""; do { in=scanner.next(); os.write((""+in).getBytes("UTF-8")); os.write(("哈哈哈哈").getBytes("UTF-8")); os.flush(); } while (!in.equals("bye")); } catch (IOException e) { e.printStackTrace(); } scanner.close(); try { os.close(); } catch (IOException e) { e.printStackTrace(); } } } public static String getdate() { Date date = new Date(); SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); String result = format.format(date); return result; } //函数入口 public static void main(String[] args) { //需要服务器的正确的IP地址和端口号 Client clientTest=new Client("62.234.175.16", 80); clientTest.start(); } }
②Server
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; public class AppServer { public static void main(String[] args) throws Exception { FindFile.init(); System.out.println("服务启动"); try (ServerSocket serverSocket = new ServerSocket(80);// 监听自己的服务器的80端口 Socket clientSocket = serverSocket.accept();//?只要发来的数据就接收到这里,网络流--->基本类型数组 PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true); BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));) { System.out.println("客户端连接"); String inputLine; String httpstr = ""; while ((inputLine = in.readLine()) != null) { httpstr += inputLine; if(inputLine.equals("") ){ break; } } System.out.println("HTTP协议开始:" + httpstr + "###HTTP协议结束"); String[] arr = httpstr.split("\?"); String str = arr[0]; String[] strarr = str.split("/"); String url = strarr[strarr.length-1]; System.out.println("提取到的url:" + url ); String params = arr[1].split(" HTTP")[0]; //key1=22&key2=value2&key3=value3&key4=value4&key5=88 String[] pramarr = params.split("&"); Learn1.chose2(url,pramarr); } catch (IOException e) { System.out.println( "Exception caught when trying to listen on port " + 80 + " or listening for a connection"); System.out.println(e.getMessage()); } System.out.println("服务退出"); } }
③FindFile
import java.io.File; import java.util.HashMap; public class FindFile { public static HashMap<String,Object[]> map = new HashMap<>(); public static void main(String[] args) throws Exception { } public static void init() { File folder = new File("D:\eclipse project\testDemo\src"); try { traverseFolder2(folder); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void traverseFolder(File folder) { File[] files = folder.listFiles(); if (files != null) { for (File file : files) { if (file.isDirectory()) { traverseFolder(file); // 递归遍历子文件夹 } else { System.out.println(file.getAbsolutePath().split("src")[1]); } } } } public static void traverseFolder2(File folder) throws Exception{ File[] files = folder.listFiles(); if (files != null) { for (File file : files) { if (file.isDirectory()) { traverseFolder2(file); } else { String filepath = file.getAbsolutePath().split("src")[1]; filepath = filepath.substring(1,filepath.length()); filepath = filepath.replace("\", "."); if(filepath.endsWith("java")) { filepath = filepath.replace(".java", ""); Class<?> cl = Class.forName(filepath); WebServlet2 annotation = cl.getAnnotation(WebServlet2.class); if(annotation!=null) { String urlname = annotation.url(); //System.out.println(filepath); //System.out.println("提取到的路径:" + urlname); map.put(urlname, new Object[] {cl.newInstance(), cl.getMethod("doGet", new Class[] {HSRequest.class}), cl.getMethod("doPost", new Class[] {HSRequest.class})}); } } } } } } }
④learn
package tomcat_you; import java.lang.reflect.Method; import java.util.Arrays; public class Learn1 { public static void chose(String url,String[] pramarr)throws Exception { String[] arr = {"tomcat_you.TestDemo2", "tomcat_you.TestDemo3", "tomcat_you.TestDemo4"}; Class<?>[] cls = new Class[arr.length]; for(int i =0;i < cls.length; i++) { cls[i] = Class.forName(arr[i]); WebServlet2 annotation = cls[i].getAnnotation(WebServlet2.class); if(annotation!=null) { //模拟挑选出了servlet(带有注解的类)? String urlname = annotation.url(); if(url.equals(urlname)) { Object x = cls[i].newInstance(); // HttpServletDemo w = (HttpServletDemo)x; // w.doGet(22, "www"); Method method = cls[i].getDeclaredMethod("doGet", new Class[] {HSRequest.class}); HSRequest request = new HSRequest(); for(String param : pramarr) { String[] arrw = param.split("="); request.put(arrw[0], arrw[1]); } method.invoke(x, request); } }else { continue; } } } //tomcat优化 public static void chose2(String url,String[] pramarr)throws Exception { HSRequest request = new HSRequest(); for(String param : pramarr) { String[] arrw = param.split("="); request.put(arrw[0], arrw[1]); } Object[] arr = FindFile.map.get(url); Method m = (Method) arr[1]; m.invoke(arr[0], request); } }
⑤HSRequest
package tomcat_you; import java.util.HashMap; public class HSRequest { private HashMap<String,String> map = new HashMap<>(); public String getParameter(String key) { return map.get(key); } public void put(String key, String value) { map.put(key, value); } }
⑥HttpServletDemo
package tomcat_you; public class HttpServletDemo { public void doGet(HSRequest request) { } public void doPost(HSRequest request){ } }
⑦WebServlet
package tomcat_you; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(value = RetentionPolicy.RUNTIME) @Target(value = {ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD,ElementType.TYPE}) public @interface WebServlet2 { public int age(); // 为name指定初始值? public String url() default "小花"; }
⑧TestDemo
package tomcat_you; @WebServlet2(url = "servlet1",age=12) public class TestDemo2 extends HttpServletDemo{ public void doGet(HSRequest request){ String name = request.getParameter("name"); String age = request.getParameter("age"); System.out.println("姓名" + name + ",年龄:" + age); } public void doPost(HSRequest request){ } }