文章目录
- 实现任务阶段 3- 处理 Servlet
- 分析+代码实现
- ● 分析示意图
- WyxRequestHandler
- wyxResponse
- wyxRequest
- wyxServlet接口
- wyxHttpServlet
- wyxCalServlet
- WebUtils
- wyxTomcatV3
- 代码里面的容器图
实现任务阶段 3- 处理 Servlet
分析+代码实现
● 分析示意图
WyxRequestHandler
1.这里我们可以对客户端/浏览器进行IO编程/交互
2.新增业务逻辑
(1) 判断uri是什么资源 => 工具方法
(2) 如果是静态资源,就读取该资源,并返回给浏览器 content-type text/html
(3) 因为目前并没有起到tomcat, 不是一个标准的web项目
(4) 把读取的静态资源放到 target/classes/cal.html
过滤,拦截 , 权限等待 => Handler… => 分发
- 有了filter机制,可以理解再调用servlet之前,先匹配filter
package com.wyxdu.tomcat.handler;
import com.wyxdu.tomcat.WyxTomcatV3;
import com.wyxdu.tomcat.http.WyxRequest;
import com.wyxdu.tomcat.http.WyxResponse;
import com.wyxdu.tomcat.servlet.WyxHttpServlet;
import com.wyxdu.tomcat.utils.WebUtils;
import java.io.*;
import java.net.Socket;
public class WyxRequestHandler implements Runnable {
//定义Socket
private Socket socket = null;
public WyxRequestHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
//这里我们可以对客户端/浏览器进行IO编程/交互
try {
WyxRequest wyxRequest = new WyxRequest(socket.getInputStream());
//这里我们可以同wyxResponse对象,返回数据给浏览器/客户端
WyxResponse wyxResponse = new WyxResponse(socket.getOutputStream());
//1. 得到 uri => 就是 servletUrlMapping 的 url-pattern
String uri = wyxRequest.getUri();
//=====================新增业务逻辑==========
//(1) 判断uri是什么资源 => 工具方法
//(2) 如果是静态资源,就读取该资源,并返回给浏览器 content-type text/html
//(3) 因为目前并没有起到tomcat, 不是一个标准的web项目
//(4) 把读取的静态资源放到 target/classes/cal.html
//过滤,拦截 , 权限等待 => Handler.... => 分发
if(WebUtils.isHtml(uri)) {//就是静态页面
String content = WebUtils.readHtml(uri.substring(1));
content = wyxResponse.respHeader + content;
//得到outputstream , 返回信息(静态页面)给浏览器
OutputStream outputStream = wyxResponse.getOutputStream();
outputStream.write(content.getBytes());
outputStream.flush();
outputStream.close();
socket.close();
return;
}
//有了filter机制,可以理解再调用servlet之前,先匹配filter
//1. 根据request对象封装的uri
//2. 到 filterUrlMapping 去匹配
//3. 如果匹配上就调用 filterMapping 对应的filer对象doFilter()
//4. 如果没有匹配上,就直接走我们后的servlet/jsp/html.
String servletName = WyxTomcatV3.servletUrlMapping.get(uri);
if (servletName == null) {
servletName = "";
}
//2. 通过uri->servletName->servlet的实例 , 真正的运行类型是其子类 WyxCalServlet
WyxHttpServlet wyxHttpServlet =
WyxTomcatV3.servletMapping.get(servletName);
//3. 调用service , 通过OOP的动态绑定机制,调用运行类型的 doGet/doPost
if (wyxHttpServlet != null) {//得到
wyxHttpServlet.service(wyxRequest, wyxResponse);
} else {
//没有这个servlet , 返回404的提示信息
String resp = wyxResponse.respHeader + "<h1>404 Not Found</h1>";
OutputStream outputStream = wyxResponse.getOutputStream();
outputStream.write(resp.getBytes());
outputStream.flush();
outputStream.close();
}
socket.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
//最后一定确保socket要关闭
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
wyxResponse
当我们需要给浏览器返回数据时,可以通过wyxResponse 的输出流完成
public class wyxResponse {
private OutputStream outputStream = null;
//写一个http的响应头 => 先死后活
public static final String respHeader = "HTTP/1.1 200 OK\r\n" +
"Content-Type: text/html;charset=utf-8\r\n\r\n";
//在创建 wyxResponse 对象时,传入的outputStream是和Socket关联的
public wyxResponse(OutputStream outputStream) {
this.outputStream = outputStream;
}
//当我们需要给浏览器返回数据时,可以通过wyxResponse 的输出流完成
//
public OutputStream getOutputStream() {
return outputStream;
}
}
wyxRequest
- wyxRequest 作用是封装http请求的数据 get /WyxCalServlet?num1=10&num2=30
- 比如 method(get) 、 uri(/wyxCalServlet) 、 还有参数列表 (num1=10&num2=30)
- wyxRequest 作用就等价原生的servlet 中的HttpServletRequest
- 一会走代码
- 这里考虑的是GET请求
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
/**
* 1. wyxRequest 作用是封装http请求的数据
* get /WyxCalServlet?num1=10&num2=30
* 2. 比如 method(get) 、 uri(/wyxCalServlet) 、 还有参数列表 (num1=10&num2=30)
* 3. wyxRequest 作用就等价原生的servlet 中的HttpServletRequest
* 4. 一会走代码
* 5. 这里考虑的是GET请求
*/
public class wyxRequest {
private String method;
private String uri;
//存放参数列表 参数名-参数值 => HashMap
private HashMap<String, String> parametersMapping =
new HashMap<>();
private InputStream inputStream = null;
//构造器=> 对http请求进行封装 => 可以写的代码封装成方法
//inputStream 是和 对应http请求的socket关联
public wyxRequest(InputStream inputStream) {
this.inputStream = inputStream;
//完成对http请求数据的封装..
encapHttpRequest();
}
/**
* 将http请求的相关数据,进行封装,然后提供相关的方法,进行获取
*/
private void encapHttpRequest() {
System.out.println("wyxRequest init()");
try {
//inputstream -> BufferedReader
BufferedReader bufferedReader =
new BufferedReader(new InputStreamReader(inputStream, "utf-8"));
//读取第一行
/**
* GET /wyxCalServlet?num1=10&num2=30 HTTP/1.1
* Host: localhost:8080
* User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:97.0) Gecko/20100101 Fi
*/
String requestLine = bufferedReader.readLine();
//GET - /WyxCalServlet?num1=10&num2=30 - HTTP/1.1
String[] requestLineArr = requestLine.split(" ");
//得到method
method = requestLineArr[0];
//解析得到 /WyxCalServlet
//1. 先看看uri 有没有参数列表
int index = requestLineArr[1].indexOf("?");
if (index == -1) { //说明没有参数列表
uri = requestLineArr[1];
} else {
//[0,index)
uri = requestLineArr[1].substring(0, index);
//获取参数列表->parametersMapping
//parameters => num1=10&num2=30
String parameters = requestLineArr[1].substring(index + 1);
//num1=10 , num2=30 .... parametersPair= ["num1=10","num2=30" ]
String[] parametersPair = parameters.split("&");
//防止用户提交时 /wyxCalServlet?
if (null != parametersPair && !"".equals(parametersPair)) {
//再次分割 parameterPair = num1=10
for (String parameterPair : parametersPair) {
//parameterVal ["num1", "10"]
String[] parameterVal = parameterPair.split("=");
if (parameterVal.length == 2) {
//放入到 parametersMapping
parametersMapping.put(parameterVal[0], parameterVal[1]);
}
}
}
}
//这里不能关闭流 inputStream 和 socket关联
//inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//request对象有一个特别重要方法
public String getParameter(String name) {
if (parametersMapping.containsKey(name)) {
return parametersMapping.get(name);
} else {
return "";
}
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public String getUri() {
return uri;
}
public void setUri(String uri) {
this.uri = uri;
}
@Override
public String toString() {
return "wyxRequest{" +
"method='" + method + '\'' +
", uri='" + uri + '\'' +
", parametersMapping=" + parametersMapping +
'}';
}
}
wyxServlet接口
public interface wyxServlet {
void init() throws Exception;
void service(wyxRequest request, wyxResponse response) throws IOException;
void destroy();
}
wyxHttpServlet
这里我们使用的了模板设计模式
让wyxHttpServlet 子类 wyxCalServlet 实现
public abstract class wyxHttpServlet implements wyxServlet {
@Override
public void service(wyxRequest request, wyxResponse response) throws IOException {
//说明 equalsIgnoreCase 比较字符串内容是相同,不区别大小写
if("GET".equalsIgnoreCase(request.getMethod())) {
//这里会有动态绑定
this.doGet(request,response);
} else if("POST".equalsIgnoreCase(request.getMethod())) {
this.doPost(request,response);
}
}
//这里我们使用的了模板设计模式
//让wyxHttpServlet 子类 wyxCalServlet 实现
public abstract void doGet(wyxRequest request, wyxResponse response);
public abstract void doPost(wyxRequest request, wyxResponse response);
}
wyxCalServlet
返回计算结果给浏览器
public class wyxCalServlet extends wyxHttpServlet {
@Override
public void doGet(wyxRequest request, wyxResponse response) {
//java基础的 OOP 的动态绑定机制..
//写业务代码,完成计算任务
int num1 = WebUtils.parseInt(request.getParameter("num1"), 0);
int num2 = WebUtils.parseInt(request.getParameter("num2"), 0);
int sum = num1 + num2;
//返回计算结果给浏览器
//outputStream 和 当前的socket关联
OutputStream outputStream = response.getOutputStream();
String respMes = wyxResponse.respHeader
+ "<h1>" + num1 + " + " + num2 + " = " + sum + " wyxTomcatV3 - 反射+xml创建</h1>";
try {
outputStream.write(respMes.getBytes());
outputStream.flush();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void doPost(wyxRequest request, wyxResponse response) {
this.doGet(request, response);
}
@Override
public void init() throws Exception {
}
@Override
public void destroy() {
}
}
WebUtils
1.将字符串转成数字方法
2.判断uri是不是html文件
3.根据文件名来读取该文件->String
public class WebUtils {
//将字符串转成数字方法
public static int parseInt(String strNum, int defaultVal) {
try {
return Integer.parseInt(strNum);
} catch (NumberFormatException e) {
System.out.println(strNum + " 不能转成数字");
}
return defaultVal;
}
//判断uri是不是html文件
public static boolean isHtml(String uri) {
return uri.endsWith(".html");
}
//根据文件名来读取该文件->String
public static String readHtml(String filename) {
String path = com.wyxdu.utils.WebUtils.class.getResource("/").getPath();
StringBuilder stringBuilder = new StringBuilder();
try {
BufferedReader bufferedReader = new BufferedReader(new FileReader(path + filename));
String buf = "";
while ((buf = bufferedReader.readLine()) != null) {
stringBuilder.append(buf);
}
} catch (Exception e) {
e.printStackTrace();
}
return stringBuilder.toString();
}
}
wyxTomcatV3
第3版的Tomcat, 实现通过xml+反射来初始化容器
代码里面的容器图
package com.wyxdu.tomcat;
import com.wyxdu.tomcat.handler.wyxRequestHandler;
import com.wyxdu.tomcat.servlet.wyxHttpServlet;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import javax.servlet.Filter;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
/**
* 第3版的Tomcat, 实现通过xml+反射来初始化容器
*/
public class wyxTomcatV3 {
//1. 存放容器 servletMapping
// -ConcurrentHashMap
// -HashMap
// key - value
// ServletName 对应的实例
public static final ConcurrentHashMap<String, wyxHttpServlet>
servletMapping = new ConcurrentHashMap<>();
//2容器 servletUrlMapping
// -ConcurrentHashMap
// -HashMap
// key - value
// url-pattern ServletName
public static final ConcurrentHashMap<String, String>
servletUrlMapping = new ConcurrentHashMap<>();
//你可以这里理解session, tomcat还维护一个容器
public static final ConcurrentHashMap<String, HttpSession>
sessionMapping = new ConcurrentHashMap<>();
//你可以这里理解filter, tomcat还维护了filter的容器
public static final ConcurrentHashMap<String, String>
filterUrlMapping = new ConcurrentHashMap<>();
public static final ConcurrentHashMap<String, Filter>
filterMapping = new ConcurrentHashMap<>();
public static void main(String[] args) {
wyxTomcatV3 wyxTomcatV3 = new wyxTomcatV3();
wyxTomcatV3.init();
//启动wyxtomcat容器
wyxTomcatV3.run();
}
//启动WyxTomcatV3容器
public void run() {
try {
ServerSocket serverSocket = new ServerSocket(8080);
System.out.println("=====wyxtomcatv3在8080监听======");
while (!serverSocket.isClosed()) {
Socket socket = serverSocket.accept();
wyxRequestHandler wyxRequestHandler =
new wyxRequestHandler(socket);
new Thread(wyxRequestHandler).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
//直接对两个容器进行初始化
public void init() {
//读取web.xml => dom4j =>
//得到web.xml文件的路径 => 拷贝一份.
String path = wyxTomcatV3.class.getResource("/").getPath();
//System.out.println("path= " + path);
//使用dom4j技术完成读取
SAXReader saxReader = new SAXReader();
try {
Document document = saxReader.read(new File(path + "web.xml"));
System.out.println("document= " + document);
//得到根元素
Element rootElement = document.getRootElement();
//得到根元素下面的所有元素
List<Element> elements = rootElement.elements();
//遍历并过滤到 servlet servlet-mapping
for (Element element : elements) {
if ("servlet".equalsIgnoreCase(element.getName())) {
//这是一个servlet配置
//System.out.println("发现 servlet");
//使用反射将该servlet实例放入到servletMapping
Element servletName = element.element("servlet-name");
Element servletClass = element.element("servlet-class");
servletMapping.put(servletName.getText(),
(wyxHttpServlet) Class.forName(servletClass.getText().trim()).newInstance());
} else if ("servlet-mapping".equalsIgnoreCase(element.getName())) {
//这是一个servlet-mapping
//System.out.println("发现 servlet-mapping");
Element servletName = element.element("servlet-name");
Element urlPatter = element.element("url-pattern");
servletUrlMapping.put(urlPatter.getText(), servletName.getText());
}
}
} catch (Exception e) {
e.printStackTrace();
}
//验证,这两个容器是否初始化成功
System.out.println("servletMapping= " + servletMapping);
System.out.println("servletUrlMapping= " + servletUrlMapping);
}
}