写一个下民用版的Tomcat ,目的是相互学习一下 。
首先根据Servlet 协议顶一个接口
interface JxdServlet { public void service(JxdRequest req, JxdResponse res) throws IOException; }
我对接口的理解是 一种规范,我在开发的时候突然有点感悟,就是你开发一套系统,或者写一个小的业务,在你使用我这个系统、业务的时候,你要 遵守我的规范,怎么解释规范呢,就是用接口去理解。当别人看你代码的时候,通过看接口就能知道你的规范是啥 。
然后定义Request,和Response
public class JxdRequest { private InputStream input; private String uri; public JxdRequest(InputStream input) { this.input = input; } public void parse() { StringBuffer request = new StringBuffer(2048); int i; byte[] buffer = new byte[2048]; try { i = input.read(buffer); } catch (IOException e) { e.printStackTrace(); i = -1; } for (int j = 0; j < i; j++) { request.append((char) buffer[j]); } uri = parseUri(request.toString()); } private static String parseUri(String requestString) { int index1, index2; index1 = requestString.indexOf(' '); if (index1 != -1) { index2 = requestString.indexOf(' ', index1 + 1); if (index2 > index1) return requestString.substring(index1 + 1, index2); } return null; } public String getUri() { return uri; } }
public class JxdResponse { private static final int BUFFER_SIZE = 1024; JxdRequest request; OutputStream output; public OutputStream getOutput() { return output; } public void setOutput(OutputStream output) { this.output = output; } public JxdResponse(OutputStream output) { this.output = output; } public void setRequest(JxdRequest request) { this.request = request; } /** * 这个方法是静态方法演示,可以不关注 * @throws IOException */ public void sendStaticResource() throws IOException { byte[] bytes = new byte[BUFFER_SIZE]; FileInputStream fis = null; try { File file = new File(HttpServer.FILE_ROOT, request.getUri()); if (file.exists()) { fis = new FileInputStream(file); int ch = fis.read(bytes, 0, BUFFER_SIZE); while (ch != -1) { output.write(bytes, 0, ch); ch = fis.read(bytes, 0, BUFFER_SIZE); } output.flush(); } else { String errorMessage = "HTTP/1.1 404 FIle Not Found\r\n" + "Content-Type: text/html\r\n" + "Content-Length: 23\r\n" + "\r\n" + "<h1>error未知</h1>"; output.write(errorMessage.getBytes()); } } catch (Exception e) { System.out.println(e.toString()); } finally { if(fis != null) { fis.close(); } } } }
刚开始很简单 就是输出流 按照 http协议 进行 传输到页面就可以。
定义一个Servlet 下执行策略
public class JxdServletProcessor { //根据HTTP协议响应头定义,里面包含变量 private static String OKMessage = "HTTP/1.1 ${StatusCode} ${StatusName}\r\n" + "Content-Type: ${ContentType}\r\n" + "Server: minit\r\n" + "Date: ${ZonedDateTime}\r\n" + "\r\n"; public void process(JxdRequest request, JxdResponse response) { //首先根据uri最后一个/号来定位,后面的字符串认为是servlet名字 String uri = request.getUri(); String servletName = uri.substring(uri.lastIndexOf("/") + 1); URLClassLoader loader = null; OutputStream outputStream = null; try { // create a URLClassLoader URL[] urls = new URL[1]; URLStreamHandler streamHandler = null; File classPath = new File(HttpServer.WEB_ROOT); String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString(); urls[0] = new URL(null, repository, streamHandler); loader = new URLClassLoader(urls); } catch (IOException e) { System.out.println(e.toString()); } outputStream = response.getOutput(); //由上面的URLClassLoader加载这个servlet Class<?> servletClass = null; try { servletClass = loader.loadClass(servletName); } catch (ClassNotFoundException e) { System.out.println(e.toString()); } //写响应头 String head = composeResponseHead(); //创建servlet新实例,然后调用 // service(),由它来写动态内容到响应体 JxdServlet servlet = null; try { outputStream.write(head.getBytes()); servlet = (JxdServlet) servletClass.newInstance(); servlet.service(request, response); outputStream.flush(); } catch (Exception e) { System.out.println(e.toString()); } catch (Throwable e) { System.out.println(e.toString()); } } //生成响应头,填充变量值 private String composeResponseHead() { Map<String, Object> valuesMap = new HashMap<>(); valuesMap.put("StatusCode", "200"); valuesMap.put("StatusName", "OK"); valuesMap.put("ContentType", "text/html;charset=uft-8"); valuesMap.put("ZonedDateTime", DateTimeFormatter.ISO_ZONED_DATE_TIME.format(ZonedDateTime.now())); StrSubstitutor sub = new StrSubstitutor(valuesMap); String responseHead = sub.replace(OKMessage); return responseHead; } }
设计一个main 方法 执行 http 页面的请求
public class HttpServer { public static final String WEB_ROOT = System.getProperty("user.dir"); public static final String FILE_ROOT = "D:\\"; public static void main(String[] args) { HttpServer server = new HttpServer(); server.await(); } public void await() { //服务器循环等待请求并处理 ServerSocket serverSocket = null; int port = 8080; try { serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1")); } catch (IOException e) { e.printStackTrace(); System.exit(1); } while (true) { Socket socket = null; InputStream input = null; OutputStream output = null; try { socket = serverSocket.accept(); //接收请求连接 input = socket.getInputStream(); output = socket.getOutputStream(); // create Request object and parse JxdRequest request = new JxdRequest(input); request.parse(); // create Response object JxdResponse response = new JxdResponse(output); if(request.getUri().startsWith("/servlet/")){ //加载动态资源 JxdServletProcessor jxdServletProcessor= new JxdServletProcessor(); jxdServletProcessor.process(request,response); }else{ //加载静态资源 StaticResourceProcessor staticResourceProcessor = new StaticResourceProcessor(); staticResourceProcessor.process(request,response); } // close the socket socket.close(); } catch (Exception ea) { ea.printStackTrace(); } } } }
上面是一个静态资源 else 如果不关注的话 可以不看注释掉,我这边是获取D盘下边的 txt 文件 相应的页面上,没啥 东西
我们实现JxdServlet 接口 这个规范 打印一个hello world 给页面 public class HelloWorldServlet implements JxdServlet { //http://localhost:8080/servlet/com.jxd.HelloWorldServlet // 注意包名 我是com.jxd包下边 做的HelloWorldServlet @Override public void service(JxdRequest req, JxdResponse res) throws IOException { System.out.println("进入>>>>>>HelloWorldServlet.service方法"); String doc = "Hello World !!!"; res.getOutput().write(doc.getBytes("utf-8")); } }
以上就是一个很简单的 Tomcat 下一期我们对他进行一个优化 最后逐渐达到 准 民用级别。