迷你版本的Tomcat1.0
- 达成目标 向浏览器输出字符
工具类
public class HttpProtocolUtil {
/**
* 200的响应头
* @param length
* @return
*/
public static String getHttpHeader200(long length){
return "HTTP/1.1 200 OK"+"\n"+
"Content-Type: text/html;charset=utf-8"+"\n"+
"Content-Length: "+length+ "\n"+
"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36"+"\n"
+"\r\n";
}
/**
* 404的响应头
* @return
*/
public static String getHttpHeader404(){
String str404 = "<h1>404 Not Found</h1>";
return "HTTP/1.1 200 OK"+"\n"+
"Content-Type: text/html;charset=utf-8"+"\n"+
"Content-Length: "+str404.getBytes().length+ "\n"+
"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36"+"\n"
+"\r\n"+str404;
}
}
启动类
public class BootStrap {
private int port = 8080;
public static void main(String[] args) {
BootStrap bootStrap = new BootStrap();
bootStrap.start();
}
/**
* 启动MiniCat
*/
private void start() {
ServerSocket serverSocket=null;
try {
//建立ServerSocket
serverSocket = new ServerSocket(port);
System.out.println("Mini Cat Start...");
while (true){
//一直监听
Socket socket = serverSocket.accept();
//监听到之后就输出
String str="Hello MiniCat!";
String responseStr = HttpProtocolUtil.getHttpHeader200(str.getBytes().length)+str;
socket.getOutputStream().write(responseStr.getBytes());
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 启动后访问 http://localhost:8080/
2.0版本输出静态页面 index.html
- 1.新建静态页面
在Resources目录下新建index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello MiniCat Index</title>
</head>
<body>
<h1>Hello MiniCat Index</h1>
</body>
</html>
-
2 封装Request 与Response
-
将请求方法 请求路径 InputStream封装为 Request
Request URL: https://www.baidu.com/ Request Method: GET
public class Request {
/**
* 请求路径
*/
private String url;
/**
* 请求方法
*/
private String method;
/**
* 输入流
*/
private InputStream inputStream;
public Request(InputStream inputStream) {
this.inputStream = inputStream;
//通过InputStream解析出请求url与method
doParse(inputStream);
}
private void doParse(InputStream inputStream) {
try {
int count = 0;
while(count==0){
//数据可能是空一直在读直到有数据
count=inputStream.available();
}
//缓存区
int available = inputStream.available();
byte [] buffer = new byte[available];
//将数据流读到数组中
inputStream.read(buffer);
//转换为字符串 -请求字符串
/*
GET / HTTP/1.1
Host: www.baidu.com
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
GET / HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,
Accept-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.2
Accept-Encoding: gzip, deflate
Connection: keep-alive
Upgrade-Insecure-Requests: 1
*/
String requestStr = new String(buffer, StandardCharsets.UTF_8);
//按换行符 分隔
String[] split = requestStr.split("\n");
//取第一行的请求url 与 method
String[] header = split[0].split(" ");
this.method=header[0];
this.url=header[1];
System.out.println(method);
System.out.println(url);
} catch (IOException e) {
e.printStackTrace();
}
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
}
- 将OutputStream封装为Response
public class Response {
private OutputStream outputStream;
public Response(OutputStream outputStream) {
this.outputStream = outputStream;
}
/**
* 输出字符串内容
* @param content
*/
public void outputAny(String content){
try {
outputStream.write(content.getBytes("UTF-8"));
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 输出静态资源
* @param path
* @throws IOException
*/
public void outputStatic(String path) throws IOException {
//获取资源的对路径
String absolutePath = StaticResource.getAbsolutePath(path);
File file = new File(absolutePath);
if(file.exists()&&file.isFile()){
//输出静态资源
StaticResource.outputStaticResource(new FileInputStream(file),outputStream);
}else {
//找不到资源
outputAny(HttpProtocolUtil.getHttpHeader404());
}
}
}
- 3 封装工具类
public class StaticResource {
/**
* 获取绝对路径
* @param path
* @return
*/
public static String getAbsolutePath(String path) {
String classedPath = StaticResource.class.getResource("/").getPath();
return classedPath.replaceAll("\\\\","/")+path;
}
public static void outputStaticResource(FileInputStream inputStream, OutputStream outputStream) throws IOException {
int count = 0;
while(count == 0) {
count = inputStream.available();
}
//资源大小
int resourceSize = count;
//请求头-先写请求头200,再写请求体
outputStream.write(HttpProtocolUtil.getHttpHeader200(resourceSize).getBytes());
//已经写的指针
int writeCourse = 0;
int byteSize = 1024;
byte [] buf = new byte[byteSize];
//写过的小于总大小就一直写
while(writeCourse<resourceSize){
//判断是不是写了
if(writeCourse+byteSize>resourceSize){
byteSize=resourceSize-writeCourse;
buf = new byte[byteSize];
}
//开始读文件
inputStream.read(buf);
//写出
outputStream.write(buf);
//刷新
outputStream.flush();
//指针增加
writeCourse+=byteSize;
}
}
}
- 访问
自定义Servlet实现动态
public interface Servlet {
public void init();
public void service(Request request, Response response);
public void destroy();
}
public abstract class HttpServlet implements Servlet{
protected abstract void doGet(Request request, Response response);
protected abstract void doPost(Request request, Response response);
@Override
public void service(Request request, Response response) {
if("GET".equals(request.getMethod())){
doGet(request, response);
}else {
doPost(request, response);
}
}
}
- 实现
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(Request request, Response response) {
response.outputAny("HelloServlet doGet...");
}
@Override
protected void doPost(Request request, Response response) {
response.outputAny("HelloServlet doPost....");
}
@Override
public void init() {
}
@Override
public void destroy() {
}
}
- 在resources目录下新建web.xml
<web-app>
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.liu.me.servlet.me.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
- 改造启动类
public class BootStrap {
private int port = 8080;
private Map<String, HttpServlet> servletMap = new ConcurrentHashMap<>();
public static void main(String[] args) {
BootStrap bootStrap = new BootStrap();
try {
bootStrap.start();
} catch (DocumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 启动MiniCat
*/
private void start() throws DocumentException, IllegalAccessException, InstantiationException, ClassNotFoundException {
//加载web.xml中配置的servlet
loadServelt();
ServerSocket serverSocket = null;
try {
//建立ServerSocket
serverSocket = new ServerSocket(port);
System.out.println("Mini Cat Start...");
while (true) {
//一直监听
Socket socket = serverSocket.accept();
//监听到之后就输出
// String str = "Hello MiniCat!";
String responseStr = HttpProtocolUtil.getHttpHeader200(str.getBytes().length)+str;
socket.getOutputStream().write(responseStr.getBytes());
Request request = new Request(socket.getInputStream());
Response response = new Response(socket.getOutputStream());
if(servletMap.get(request.getUrl())==null) {
//输出静态资源
response.outputStatic(request.getUrl());
}else{
HttpServlet servlet = servletMap.get(request.getUrl());
servlet.service(request, response);
}
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
private synchronized void loadServelt() throws DocumentException, ClassNotFoundException, IllegalAccessException, InstantiationException {
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("web.xml");
//使用dom4j解析
Document document = new SAXReader().read(inputStream);
//获取根标签
Element rootElement = document.getRootElement();
//获取所有Servlet标签
List<Element> elements = rootElement.selectNodes("//servlet");
//循环取出servletName,ServletClass 根据servlet-name去servlet-maping标签中找到一样的对象的url-patten
for (Element element : elements) {
//一个Servelt节点只会有一个Servlet-name 标签
Node nameNode = element.selectSingleNode("servlet-name");
//得到ServletName
String servletName = nameNode.getStringValue();
//获取servlet-class节点
Node classNode = element.selectSingleNode("servlet-class");
//得到ServletClass
String servletClass = classNode.getStringValue();
System.out.println("servletName " + servletName);
System.out.println("servletClass " + servletClass);
//根据servletName去servlet-mapping标签中servelt-name相等的节点
// /bookstore/book[price>35.00] xpath语法
Node MappingNode = rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
String mappingServeltName = MappingNode.selectSingleNode("servlet-name").getStringValue();
String mappingUrlPatten = MappingNode.selectSingleNode("url-pattern").getStringValue();
System.out.println("mappingServeltName " + mappingServeltName);
System.out.println("mappingUrlPatten " + mappingUrlPatten);
/**
* 反射实例化放入容器中
*/
Class<?> clazz = Class.forName(servletClass);
HttpServlet servlet =(HttpServlet) clazz.newInstance();
servletMap.put(mappingUrlPatten, servlet);
}
}
}
- 启动访问
4.0版本使用线程与线程池
- RequestProcessor
public class RequestProcessor extends Thread {
private Socket socket;
private Map<String, HttpServlet> servletMap;
public RequestProcessor(Socket socket, Map<String, HttpServlet> servletMap) {
this.socket = socket;
this.servletMap = servletMap;
}
@Override
public void run() {
Request request = null;
try {
request = new Request(socket.getInputStream());
Response response = new Response(socket.getOutputStream());
if(servletMap.get(request.getUrl())==null) {
//输出静态资源
response.outputStatic(request.getUrl());
}else{
HttpServlet servlet = servletMap.get(request.getUrl());
servlet.service(request, response);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(socket.isConnected()){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
- 改造启动类
public class BootStrap {
private int port = 8080;
private Map<String, HttpServlet> servletMap = new ConcurrentHashMap<>();
public static void main(String[] args) {
BootStrap bootStrap = new BootStrap();
try {
bootStrap.start();
} catch (DocumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 启动MiniCat
*/
private void start() throws DocumentException, IllegalAccessException, InstantiationException, ClassNotFoundException {
//加载web.xml中配置的servlet
loadServelt();
BlockingQueue<Runnable> workQueue= new ArrayBlockingQueue<>(50);
ThreadFactory threadFactory=Executors.defaultThreadFactory();
RejectedExecutionHandler handle=new ThreadPoolExecutor.AbortPolicy();
ThreadPoolExecutor threadPoolExecutor=
new ThreadPoolExecutor(10, 50, 60, TimeUnit.SECONDS,
workQueue,threadFactory,handle);
ServerSocket serverSocket = null;
try {
//建立ServerSocket
serverSocket = new ServerSocket(port);
System.out.println("Mini Cat Start...");
while (true) {
//一直监听
Socket socket = serverSocket.accept();
RequestProcessor requestProcessor = new RequestProcessor(socket,servletMap);
threadPoolExecutor.execute(requestProcessor);
}
} catch (IOException e) {
e.printStackTrace();
}
}
private synchronized void loadServelt() throws DocumentException, ClassNotFoundException, IllegalAccessException, InstantiationException {
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("web.xml");
//使用dom4j解析
Document document = new SAXReader().read(inputStream);
//获取根标签
Element rootElement = document.getRootElement();
//获取所有Servlet标签
List<Element> elements = rootElement.selectNodes("//servlet");
//循环取出servletName,ServletClass 根据servlet-name去servlet-maping标签中找到一样的对象的url-patten
for (Element element : elements) {
//一个Servelt节点只会有一个Servlet-name 标签
Node nameNode = element.selectSingleNode("servlet-name");
//得到ServletName
String servletName = nameNode.getStringValue();
//获取servlet-class节点
Node classNode = element.selectSingleNode("servlet-class");
//得到ServletClass
String servletClass = classNode.getStringValue();
System.out.println("servletName " + servletName);
System.out.println("servletClass " + servletClass);
//根据servletName去servlet-mapping标签中servelt-name相等的节点
// /bookstore/book[price>35.00] xpath语法
Node MappingNode = rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
String mappingServeltName = MappingNode.selectSingleNode("servlet-name").getStringValue();
String mappingUrlPatten = MappingNode.selectSingleNode("url-pattern").getStringValue();
System.out.println("mappingServeltName " + mappingServeltName);
System.out.println("mappingUrlPatten " + mappingUrlPatten);
/**
* 反射实例化放入容器中
*/
Class<?> clazz = Class.forName(servletClass);
HttpServlet servlet =(HttpServlet) clazz.newInstance();
servletMap.put(mappingUrlPatten, servlet);
}
}