1. 前置知識
1.1.java註解、反射
1.2.java網路程式設計
1.2.servlet基礎
2. 本章重點
2.1. 回顧tomcat
2.2. 建立xml版本servelt
2.3. 自建xml版本tomcat1.0
2.4. 建立註解版本servlet
2.5. 自建註解版本tomcat2.0
3. 具體內容
3.1. 回顧tomcat
3.1.1. tomcat的概念
Tomcat 伺服器是一個免費的開放原始碼的Web 應用伺服器,屬於輕量級應用,在中小型系統和併發訪問使用者不是很多的場合下被普遍使用,是開發和除錯JSP 程式的首選。對於一個初學者來說,可以這樣認為,當在一臺機器上配置好Apache 伺服器,可利用它響應(下的一個應用)頁面的訪問請求。實際上Tomcat是Apache 伺服器的擴充套件,但執行時它是獨立執行的,所以當你執行tomcat 時,它實際上作為一個與Apache 獨立的程序單獨執行的。訣竅是,當配置正確時,Apache 為HTML頁面服務,而Tomcat 實際上執行JSP 頁面和Servlet。
3.1.2. 建立一個web工程
3.1.2.1. 步驟一:配置tomcat伺服器
3.1.2.2. 建立web工程
3.1.2.3. 補齊工程
3.2. 建立xml版本servelt
3.2.1. 建立一個servlet
package com.aaa.servlet;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;/*** @author :Teacher陳* @date :Created in 2021/1/20 9:26* @description:測試一個servlet* @modified By:* @version: 1.0*/public class HelloServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doPost(req,resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//業務邏輯System.out.println("HelloServlet is running!");StringBuffer sb= new StringBuffer();sb.append("<html>");sb.append("<body>");sb.append("<h1>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</h1>");sb.append("</body>");sb.append("</html>");resp.getWriter().write(sb.toString());}}
3.2.2. 配置servlet
<?xml version="1.0" encoding="UTF-8"?><web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"version="4.0"><servlet><servlet-name>hello</servlet-name><servlet-class>com.aaa.servlet.HelloServlet</servlet-class></servlet><servlet-mapping><servlet-name>hello</servlet-name><url-pattern>/xxx</url-pattern></servlet-mapping></web-app>
3.2.3. 測試servlet
可以發現,tomcat正常的接收了瀏覽器傳送的請求
tomcat根據請求對映到響應的servlet
抓包工具顯示的請求原始資訊如下:
GET /aaa/xxx HTTP/1.1Host: localhost:8080User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2Accept-Encoding: gzip, deflateConnection: keep-aliveCookie: JSESSIONID=6E593EFE70D7DE906671505FDBAEEAAC; Hm_lvt_d214947968792b839fd669a4decaaffc=1603700593,1603779032; Idea-661643a5=d5722ef7-628d-418c-960f-6f353393ba3b; cookie_lang=1Upgrade-Insecure-Requests: 1Cache-Control: max-age=0
HelloServelt做出響應,回傳到瀏覽器端
HTTP/1.1 200
Content-Length: 66
Date: Wed, 20 Jan 2021 01:39:37 GMT
3.3. 自建xml版本tomcatv1.0
3.3.1. 思路分析
根據上面的思路,需要設計一下幾個java類
tomcat啟動類
請求實體類
響應實體類
servlet抽象類
servlet的具體實現類
3.3.2. 建立maven專案,引入相關jar
3.3.3. 建立請求類
package com.aaa.tomcat;import lombok.Data;import java.io.IOException;import java.io.InputStream;/*** @author :Teacher陳* @date :Created in 2021/1/20 10:22* @description:自定義的請求類* @modified By:* @version: 1.0*/@Datapublic class RequestAAA {private String method;private String url;private InputStream inputStream;public RequestAAA(InputStream inputStream) throws Exception {byte[] buf= new byte[1024];//定義一個請求字串String requestStr="";//處理inputStream,獲取請求方式和請求地址int count=0;if((count=inputStream.read(buf))>0){requestStr= new String(buf,0,count);}/*** GET /aaa/xxx HTTP/1.1* Host: localhost:8080*///獲取第一行String head = requestStr.split("\n")[0];this.method = head.split("\\s")[0];this.url=head.split("\\s")[1];}}
3.3.4. 建立響應類
package com.aaa.tomcat;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import java.io.IOException;import java.io.OutputStream;/*** @author :Teacher陳* @date :Created in 2021/1/20 10:35* @description:自創響應類* @modified By:* @version: 1*/@Data@AllArgsConstructor@NoArgsConstructorpublic class ResponseAAA {private OutputStream outputStream;/*** @create by: Teacher陳* @description:將內容寫回瀏覽器* @create time: 2021/1/20 10:37* @param context* @return void*/public void write(String context) throws IOException {StringBuffer stringBuffer = new StringBuffer();stringBuffer.append("HTTP/1.1 200\r\n");stringBuffer.append("Content-Type: text/html\r\n");stringBuffer.append("\r\n");stringBuffer.append("<html>");stringBuffer.append("<body>");stringBuffer.append(context);stringBuffer.append("</body>");stringBuffer.append("</html>");outputStream.write(stringBuffer.toString().getBytes());outputStream.flush();outputStream.close();}}
3.3.5. 建立servlet對映類
package com.aaa.tomcat;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;/*** @author :Teacher陳* @date :Created in 2021/1/20 10:45* @description:servlet對映類* @modified By:* @version: 1*/@Data@AllArgsConstructor@NoArgsConstructorpublic class ServletMappingAAA {/*** 對應web.xml中的servlet url-pattern*/private String url;/***對應web.xml中的servlet-class*/private String clazz;}
3.3.6. 建立servlet抽象類
package com.aaa.tomcat;import java.io.IOException;/*** @author :Teacher陳* @date :Created in 2021/1/20 10:48* @description:我自己的servlet* @modified By:* @version: 1*/public abstract class ServletAAA {/*** @create by: Teacher陳* @description: 處理get請求* @create time: 2021/1/20 10:51* @return*/public abstract void doGet(RequestAAA req, ResponseAAA resp) ;/*** @create by: Teacher陳* @description: 處理post請求* @create time: 2021/1/20 10:51* @return*/public abstract void doPost(RequestAAA req, ResponseAAA resp);/*** @create by: Teacher陳* @description: 根據請求方式,去進入不同的處理方法* @create time: 2021/1/20 10:55* @param req, resp*/public void service(RequestAAA req, ResponseAAA resp){if("GET".equalsIgnoreCase(req.getMethod())){doGet(req,resp);}else if("POST".equalsIgnoreCase(req.getMethod())){doPost(req,resp);}}}
3.3.7. 建立servlet實現類
package com.aaa.tomcat;import java.io.IOException;/*** @author :Teacher陳* @date :Created in 2021/1/20 10:56* @description:測試servlet* @modified By:* @version: 1*/public class HelloServlet extends ServletAAA {@Overridepublic void doGet(RequestAAA req, ResponseAAA resp) {this.doPost(req,resp);}@Overridepublic void doPost(RequestAAA req, ResponseAAA resp) {System.out.println("HelloServlet is called");try {resp.write(" I love you ");} catch (IOException e) {e.printStackTrace();}}}
3.3.8. 建立tomcat類
package com.aaa.tomcat;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.ServerSocket;import java.net.Socket;import java.util.HashMap;import java.util.Map;/*** @author :Teacher陳* @date :Created in 2021/1/20 11:00* @description:AAA的tomcat* @modified By:* @version: 1*/public class TomcatAAA {/*** 1:透過serverSoket啟動一個監聽程式,監聽8080埠* 2:socket物件可以獲取inputstream請求位元組流,還可以獲取響應outputStream* 3:從請求中解析http報文,獲取請求方式和請求地址* 4:根據請求地址獲取servlet類,然後根據請求方式執行servlet中對應的邏輯處理方法* 5:構建響應物件,響應給瀏覽器*/private int port = 9090;/*** 定義一個servlet對映集合*/private Map<String, String> urlServletMap = new HashMap();public TomcatAAA(int port) {this.port = port;}public TomcatAAA() {}/*** @return* @create by: Teacher陳* @description: 啟動tomcat* @create time: 2021/1/20 11:04*/public void start() {ServerSocket serverSocket = null;Socket socket = null;try {serverSocket = new ServerSocket(this.port);System.out.println("tomcat is running");while (true) {//等待客戶端請求socket = serverSocket.accept();//獲取客戶端傳送的資訊InputStream inputStream = socket.getInputStream();//響應客戶端的資訊OutputStream outputStream = socket.getOutputStream();//構建請求和響應RequestAAA requestAAA = new RequestAAA(inputStream);ResponseAAA responseAAA = new ResponseAAA(outputStream);//獲取servletServletMappingAAA servletMappingAAA = new ServletMappingAAA("/yyy", "com.aaa.tomcat.TestServlet");Class<ServletAAA> servletAAAClass = (Class<ServletAAA>) Class.forName(servletMappingAAA.getClazz());ServletAAA servletAAA = servletAAAClass.newInstance();servletAAA.service(requestAAA, responseAAA);}} catch (Exception e) {e.printStackTrace();} finally {try {socket.close();serverSocket.close();} catch (IOException e) {e.printStackTrace();}}}public static void main(String[] args) {TomcatAAA tomcatAAA = new TomcatAAA();tomcatAAA.start();}}
3.3.9. 建立tomcatv1.1類,支援讀取web.xml中單個servlet
web.xml<web-app><servlet><servlet-name>hello</servlet-name><servlet-class>com.aaa.tomcat.HelloServlet</servlet-class></servlet><servlet-mapping><servlet-name>hello</servlet-name><url-pattern>/ooo</url-pattern></servlet-mapping></web-app>//在tomcat啟動的時候載入所有的url和servlet的對映關係Document docResult= XmlUtil.readXML("web.xml");Object clazz = XmlUtil.getByXPath("//web-app/servlet/servlet-class", docResult, XPathConstants.STRING);Object url = XmlUtil.getByXPath("//web-app/servlet-mapping/url-pattern", docResult, XPathConstants.STRING);urlServletMap.put(url.toString(),clazz.toString());
3.3.10. 建立tomcatV1.2,讀取web.xml中多個servlet
上面的案例可以讀取單個servlet配置資訊,真實的web.xml中包含多個servlet配置。
web.xml
<web-app><servlet><servlet-name>hello</servlet-name><servlet-class>com.aaa.tomcat.HelloServlet</servlet-class></servlet><servlet-mapping><servlet-name>hello</servlet-name><url-pattern>/ooo</url-pattern></servlet-mapping><servlet><servlet-name>test</servlet-name><servlet-class>com.aaa.tomcat.TestServlet</servlet-class></servlet><servlet-mapping><servlet-name>test</servlet-name><url-pattern>/zzz</url-pattern></servlet-mapping></web-app>
建立讀取web.xml的工具類
package com.aaa.tomcat;import cn.hutool.core.collection.SpliteratorUtil;import cn.hutool.core.util.XmlUtil;import org.w3c.dom.Document;import org.w3c.dom.Element;import org.w3c.dom.Node;import org.w3c.dom.NodeList;import javax.xml.xpath.XPathConstants;import java.util.*;/*** @author :Teacher陳* @date :Created in 2021/1/20 14:59* @description:我自己的xml工具類* @modified By:* @version: 1.0*/public class MyXmlUtil {/*** @create by: Teacher陳* @description: 讀取web.xml檔案* @create time: 2021/1/20 16:41* @return map*/public static Map<String, String> loadWebXml() {//載入web.xmlDocument docResult = XmlUtil.readXML("web.xml");//將Document轉換成ElementElement element = docResult.getDocumentElement();//按照元素名稱servlet查詢所有的元素List<Element> elementList = XmlUtil.getElements(element, "servlet");//key servletName value clazzMap<String, String> servletMap = new HashMap();//遍歷servlet元素for (Element elementServlet : elementList) {//獲取單個servlet元素的所有孩子NodeList childNodes = elementServlet.getChildNodes();List<String> tempList = new ArrayList<String>();//遍歷所有孩子節點for (int i = 0; i < childNodes.getLength(); i++) {//去除多餘的特殊字元和空格String textContent = childNodes.item(i).getTextContent().trim().replace("\\n", "").replace("\\t", "");if (textContent != "" && textContent.length() > 0) {tempList.add(textContent);}}//key servletName value clazz 封裝map集合servletMap.put(tempList.get(0), tempList.get(1));}//key servletName value urlMap<String, String> servletMappingMap = new HashMap();List<Element> elementListMapping = XmlUtil.getElements(element, "servlet-mapping");for (Element elementMapping : elementListMapping) {NodeList childNodes = elementMapping.getChildNodes();List<String> tempList = new ArrayList<String>();for (int i = 0; i < childNodes.getLength(); i++) {String textContent = childNodes.item(i).getTextContent().trim().replace("\\n", "").replace("\\t", "");if (textContent != "" && textContent.length() > 0) {tempList.add(textContent);}}servletMappingMap.put(tempList.get(0), tempList.get(1));}//合併兩個mapSet<String> keySet = servletMappingMap.keySet();Map<String, String> finalMap = new HashMap();for (String s : keySet) {finalMap.put(servletMappingMap.get(s), servletMap.get(s));}System.out.println(finalMap.toString());return finalMap;}}
tomcat啟動類
TomcatAAA
package com.aaa.tomcat;import cn.hutool.core.util.XmlUtil;import org.w3c.dom.Document;import javax.xml.xpath.XPathConstants;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.ServerSocket;import java.net.Socket;import java.util.HashMap;import java.util.Map;/*** @author :Teacher陳* @date :Created in 2021/1/20 11:00* @description:AAA的tomcat* @modified By:* @version: 1*/public class TomcatAAA {/*** 1:透過serverSoket啟動一個監聽程式,監聽8080埠* 2:socket物件可以獲取inputstream請求位元組流,還可以獲取響應outputStream* 3:從請求中解析http報文,獲取請求方式和請求地址* 4:根據請求地址獲取servlet類,然後根據請求方式執行servlet中對應的邏輯處理方法* 5:構建響應物件,響應給瀏覽器*/private int port = 9090;/*** 定義一個servlet對映集合,key url , value clazz*/private Map<String, String> urlServletMap = new HashMap();public TomcatAAA(int port) {this.port = port;}public TomcatAAA() {}/*** @return* @create by: Teacher陳* @description: 啟動tomcat* @create time: 2021/1/20 11:04*/public void start() {//載入所有的url和servlet的對映關係urlServletMap = MyXmlUtil.loadWebXml();ServerSocket serverSocket = null;Socket socket = null;try {serverSocket = new ServerSocket(this.port);System.out.println("tomcat is running");while (true) {//等待客戶端請求socket = serverSocket.accept();//獲取客戶端傳送的資訊InputStream inputStream = socket.getInputStream();//響應客戶端的資訊OutputStream outputStream = socket.getOutputStream();//構建請求和響應RequestAAA requestAAA = new RequestAAA(inputStream);ResponseAAA responseAAA = new ResponseAAA(outputStream);//獲取servletfor (Map.Entry<String, String> entry : urlServletMap.entrySet()) {if (entry.getKey().equals(requestAAA.getUrl())) {Class<ServletAAA> servletAAAClass = (Class<ServletAAA>) Class.forName(entry.getValue());ServletAAA servletAAA = servletAAAClass.newInstance();servletAAA.service(requestAAA, responseAAA);}}}} catch (Exception e) {e.printStackTrace();} finally {try {socket.close();serverSocket.close();} catch (IOException e) {e.printStackTrace();}}}public static void main(String[] args) {TomcatAAA tomcatAAA = new TomcatAAA();tomcatAAA.start();}}
3.4. 建立註解版本servlet
3.4.2. 在servlet上添加註解
package com.aaa.servlet;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;/*** @author :Teacher陳* @date :Created in 2021/1/20 9:26* @description:測試一個servlet* @modified By:* @version: 1.0*/@WebServlet(urlPatterns = "/xxx")public class HelloServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doPost(req,resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//業務邏輯System.out.println("HelloServlet is running!");StringBuffer sb= new StringBuffer();sb.append("<html>");sb.append("<body>");sb.append("<h1>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</h1>");sb.append("</body>");sb.append("</html>");resp.getWriter().write(sb.toString());}}
3.5. 自建註解版本tomcatV2.0
3.5.1. 建立servlet註解
package com.aaa.tomcat;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/*** @create by: Teacher陳* @description: 自定義的servlet註解* @create time: 2021/1/20 16:51* @return*/@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface MyWebServlet {/*** 需要攔截的url,可以是多個* @return*/String[] urlPatterns() default {};}
3.5.2. 建立servlet
package com.aaa.servlet;import com.aaa.tomcat.MyWebServlet;import com.aaa.tomcat.RequestAAA;import com.aaa.tomcat.ResponseAAA;import com.aaa.tomcat.ServletAAA;import java.io.IOException;/*** @author :Teacher陳* @date :Created in 2021/1/20 16:54* @description:註解的servlet* @modified By:* @version: 1*/@MyWebServlet(urlPatterns = "/aaa")public class AnnServlet extends ServletAAA {@Overridepublic void doGet(RequestAAA req, ResponseAAA resp) {this.doPost(req,resp);}@Overridepublic void doPost(RequestAAA req, ResponseAAA resp) {System.out.println("AnnServlet is called");try {System.out.println("response is running ");resp.write(" I miss you ");} catch (IOException e) {e.printStackTrace();}}}
3.5.3. 掃描並解析註解
/*** @create by: Teacher陳* @description: 掃描所有的sevlet註解* @create time: 2021/1/20 16:58* @param packageName* @return*/public static Map<String, String> loadServletAnn(String packageName) {if(packageName==null||packageName.equals("")){packageName="com.aaa.servlet";}Reflections reflections= new Reflections(packageName);Set<Class<?>> classSet = reflections.getTypesAnnotatedWith(MyWebServlet.class);Map<String,String> map = new HashMap<String, String>();for (Class<?> aClass : classSet) {MyWebServlet annotation = aClass.getAnnotation(MyWebServlet.class);map.put(annotation.urlPatterns()[0],aClass.getName());}return map;}
3.5.4. 測試
4. 本章總結