Hello大家好,本章我们自己实现一个简易版的tomcat 。有问题可以联系我mr_beany@163.com。另求各路大神指点,感谢
程序的运行少不了服务器的支持,而tomcat因其性能稳定,免费等优点,深受 Java 爱好者的喜爱并得到了部分软件开发商的认可,为目前比较流行的 Web 应用服务器。
那么到底它是怎么运行的呢?今天我们来实现一个简化版tomcat来感受一下。
<dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency>复制代码
package com.example.demo.utils; import java.util.List; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; /** * @author zy */ public class XmlUtils { /** * 定义解析器和文档对象 */ public SAXReader saxReader; public Document document; public XmlUtils(String path) { //获取解析器 saxReader = new SAXReader(); try { //获取文档对象 document = saxReader.read(path); } catch (DocumentException e) { e.printStackTrace(); } } /** * 获取节点下的所有节点 * * @param name * @return */ public List<Element> getNodes(String name) { Element root = document.getRootElement(); return root.elements(name); } } 复制代码
其中Request是对浏览器的请求的封装,而Response是对浏览器请求的响应,换而言之就是Request 用来取出请求信息,而 Response 则用来添加要返回给浏览器的信息。
创建Request.java
package com.example.demo.http; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; /** * @author zy */ public class Request { private String method; private String url; public Request(InputStream inputStream) throws IOException { BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String[] methodAndUrl = bufferedReader.readLine().split(" "); this.method = methodAndUrl[0]; this.url = methodAndUrl[1]; System.out.println("请求类型:"+ method); System.out.println("请求路径:"+ url); } public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } }复制代码
创建Response.java
package com.example.demo.http; import java.io.OutputStream; /** * @author zy */ public class Response { private OutputStream outputStream; private String write; public Response(OutputStream outputStream){ this.outputStream = outputStream; } public String getWrite() { return write; } public void setWrite(String write) { this.write = write; } }复制代码
先做个接口
package com.example.demo.servlet; import com.example.demo.http.Request; import com.example.demo.http.Response; /** * @author zy */ public abstract class AbstractServlet { public abstract void doGet(Request request, Response response); public abstract void doPost(Request request, Response response); }复制代码
再来实现自己的Servlet
package com.example.demo.servlet; import com.example.demo.http.Request; import com.example.demo.http.Response; /** * @author zy */ public class FirstServlet extends AbstractServlet { @Override public void doGet(Request request, Response response) { response.setWrite("我的第一个Servlet"); } @Override public void doPost(Request request, Response response) { this.doGet(request, response); } }复制代码
这里配置了请求路径的servlet的对应关系,文件放在resources文件夹下
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <servlet> <servlet-name>first.html</servlet-name> <servlet-class>com.example.demo.servlet.FirstServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>first.html</servlet-name> <url-pattern>/first.html</url-pattern> </servlet-mapping> </web-app>复制代码
package com.example.demo.tomcat; import java.io.IOException; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.HashMap; import java.util.List; import com.example.demo.utils.XmlUtils; import org.dom4j.Element; /** * @author zy */ public class Tomcat { /** * 设置端口号 */ private static final int PORT = 8080; public static final HashMap<String, Object> SERVLET_MAPPING = new HashMap<>(); public static final HashMap<String, Object> SERVLET = new HashMap<>(); /** * 控制服务器启动关闭 */ public boolean tomcatStarBool = true; private void init() { InputStream io = null; try { System.out.println("加载配置文件开始"); //读取配置文件 XmlUtils xml = new XmlUtils(XmlUtils.class.getResource("/")+"web.xml"); //将所有的类都存储到容器中 List<Element> list = xml.getNodes("servlet"); for (Element element : list) { SERVLET.put(element.element("servlet-name").getText(), Class.forName(element.element("servlet-class").getText()).newInstance()); } //映射关系创建 List<Element> list2 = xml.getNodes("servlet-mapping"); for (Element element : list2) { SERVLET_MAPPING.put(element.element("url-pattern").getText(), element.element("servlet-name").getText()); } System.out.println("加载配置文件结束"); } catch (Exception ex) { ex.printStackTrace(); } finally { if (io != null) { try { io.close(); } catch (Exception e) { e.printStackTrace(); } } } } }复制代码
package com.example.demo.tomcat; import com.example.demo.http.Request; import com.example.demo.http.Response; import com.example.demo.servlet.AbstractServlet; import java.io.OutputStream; import java.net.Socket; /** * @author zy */ public class SocketProcess extends Thread{ protected Socket socket; public SocketProcess(Socket socket){ this.socket = socket; } @Override public void run() { try { Request request = new Request(socket.getInputStream()); Response response = new Response(socket.getOutputStream()); String servletName = (String) Tomcat.SERVLET_MAPPING.get(request.getUrl()); if(servletName!=null && !servletName.isEmpty()) { //映射有的话找到对应的对象 AbstractServlet servlet = (AbstractServlet) Tomcat.SERVLET.get(servletName); if(servlet!=null) { servlet.doGet(request, response); }else { System.out.println("找不到对应的servlet"); } }else { System.out.println("找不到对应的servletMapping"); } String res = response.getWrite(); OutputStream outputStream = socket.getOutputStream(); outputStream.write(res.getBytes("GBK")); outputStream.flush(); outputStream.close(); }catch (Exception ex){ ex.printStackTrace(); }finally { if (socket != null) { try { socket.close(); } catch (Exception e) { e.printStackTrace(); } } } } }复制代码
private void start() { try { ServerSocket serverSocket = new ServerSocket(PORT); System.out.println("Tomcat 服务已启动,地址:localhost ,端口:" + PORT); this.init(); //持续监听 do { Socket socket = serverSocket.accept(); //处理任务 Thread thread = new SocketProcess(socket); thread.start(); } while (tomcatStarBool); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { Tomcat tomcat = new Tomcat(); tomcat.start(); }复制代码
启动在tomcat.java中运行void main方法
出现如下字样代表启动成功,然后地址栏中输入
http://localhost:8080/first.html
?????为什么是这样。。。。。。
啊!原来没有添加响应头信息
Response.java中加入
/** * 响应头信息 */ public static final String RESPONSE_HEADER ="HTTP/1.1 200 /r/n" + "Content-Type: text/html/r/n" + "/r/n";复制代码
SocketProcess.java中
String res = response.getWrite();复制代码
改成
String res = Response.RESPONSE_HEADER + response.getWrite();复制代码
重新启动,访问