Tomcat是非常流行的Web Server,它还是一个满足Servlet规范的容器。那么想一想,Tomcat和我们的Web应用是什么关系?
从感性上来说,我们一般需要把Web应用打成WAR包部署到Tomcat中,在我们的Web应用中,我们
要指明URL被哪个类的哪个方法所处理(不论是原始的Servlet开发,还是现在流行的Spring MVC都必须
指明)。
由于我们的Web应用是运行在Tomcat中,那么显然,请求必定是先到达Tomcat的。Tomcat对于
请求实际上会进行下面的处理:
第一:提供Socket服务
Tomcat的启动,必然是Socket服务,只不过它支持HTTP协议而已!
这里其实可以扩展思考下,Tomcat既然是基于Socket,那么是基于BIO or NIO or AIO呢?
第二:进行请求的分发
要知道一个Tomcat可以为多个Web应用提供服务,那么很显然,Tomcat可以把URL下发到不同的
Web应用。
第三:需要把请求和响应封装成request/response
我们在Web应用这一层,可从来没有封装过request/response的,我们都是直接使用的,这就是因
为Tomcat已经为你做好了!
1 、 MyRequest
package com.ye.myTomcat;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class MyRequest {
private String url;
private String method;
private Map<String, String> allParameter = new HashMap<String, String>();
public String getParameter(String paramName) {
return allParameter.get(paramName);
}
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;
}
@Override
public String toString() {
return "MyRequest [url=" + url + ", method=" + method + "]";
}
public MyRequest(InputStream inputStream) throws IOException {
String httpRequest = "";
byte[] httpRequestBytes = new byte[1024];
int length = 0;
if ((length = inputStream.read(httpRequestBytes)) > 0) {
httpRequest = new String(httpRequestBytes, 0, length);
}
System.out.println(httpRequest);
String httpHead = httpRequest.split("\n")[0];
//get请求带参数
if (httpHead.indexOf("?") > 0) {
String url1 = httpHead.split("\\s")[1];
url = url1.substring(0, url1.indexOf("?"));
String[] param = url1.substring(url1.indexOf("?") + 1).split("&");
for (int i = 0; i < param.length; i++) {
String key = param[i].substring(0, param[i].indexOf("="));
String value = param[i].substring(param[i].indexOf("=") + 1);
//将参数以键值对形式放入map集合
allParameter.put(key, value);
}
//get请求不带参数
} else {
url = httpHead.split("\\s")[1];
}
method = httpHead.split("\\s")[0];
System.out.println("MyRequest---------------------->" + this);
}
}
MyRequest类可以通过输入流,对HTTP协议进行解析,拿到了HTTP请求头的方法,URL以及url参数,http协议通过输入流传过来长如下样子。所以可以根据如上字符串操作获取需要的数据。
2 、 MyResponse
package com.ye.myTomcat;
import java.io.IOException;
import java.io.OutputStream;
public class MyResponse {
private OutputStream outputStream;
public MyResponse(OutputStream outputStream) {
this.outputStream = outputStream;
}
public void write(String content) throws IOException {
//http 响应协议
StringBuffer httpResponse = new StringBuffer();
httpResponse.append("HTTP/1.1 200 OK\n")
.append("Content-Type: text/html;charset=UTF-8\n")
.append("\r\n")
.append("")
.append(content)
.append("");
outputStream.write(httpResponse.toString().getBytes());
outputStream.close();
}
}
MyResponse 基于http协议的格式进行输出写入
前文说Tomcat是满足Servlet规范的容器,那么自然Tomcat需要提供API。这里你看到了Servlet常见的
doGet/doPost/service方法。
1、 父类 MyServlet
package com.ye.myTomcat;
public abstract class MyServlet {
public abstract void doGet(MyRequest myRequest, MyResponse myResponse);
public abstract void doPost(MyRequest myRequest, MyResponse myResponse);
public void service(MyRequest myRequest, MyResponse myResponse) {
if(myRequest.getMethod().equalsIgnoreCase("POST")) {
doPost(myRequest, myResponse);
}else if(myRequest.getMethod().equalsIgnoreCase("get")) {
doGet(myRequest, myResponse);
}
};
}
可以看到里面定义了常用的 get 和 post 方法
2 、 子类FindServlet,HelloWorldServlet(FaviconServlet暂时不贴代码,后面将),两个具体的实现类
①FindServlet
package com.ye.myTomcat;
import java.io.IOException;
public class FindServlet extends MyServlet{
@Override
public void doGet(MyRequest myRequest, MyResponse myResponse) {
// TODO Auto-generated method stub
try {
myResponse.write("GET FindServlet");
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void doPost(MyRequest myRequest, MyResponse myResponse) {
// TODO Auto-generated method stub
try {
myResponse.write("POST FindServlet");
} catch (IOException e) {
e.printStackTrace();
}
}
}
② 、 HelloWorldServlet
package com.ye.myTomcat;
import java.io.IOException;
public class HelloWorldServlet extends MyServlet {
@Override
public void doGet(MyRequest myRequest, MyResponse myResponse) {
String name = myRequest.getParameter("name");
String password = myRequest.getParameter("password");
StringBuffer content = new StringBuffer();
content.append("method GET ")
.append("className HelloWorldServlet ")
.append("name "+ name +" ")
.append("password "+ password);
// TODO Auto-generated method stub
try {
myResponse.write(content.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void doPost(MyRequest myRequest, MyResponse myResponse) {
// TODO Auto-generated method stub
try {
myResponse.write("POST HelloWorldServlet");
} catch (IOException e) {
e.printStackTrace();
}
}
}
3 、 配置servlet
类似与传统 web.xml 中和来进行指定哪个URL交给哪个servlet进行处理
①、 ServletMapping (servletName servlet名字,url url路径,clazz 类路径)
package com.ye.myTomcat;
public class ServletMapping {
private String servletName;
private String url;
private String clazz;
public ServletMapping() {
super();
}
public ServletMapping(String servletName, String url, String clazz) {
super();
this.servletName = servletName;
this.url = url;
this.clazz = clazz;
}
public String getServletName() {
return servletName;
}
public void setServletName(String servletName) {
this.servletName = servletName;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getClazz() {
return clazz;
}
public void setClazz(String clazz) {
this.clazz = clazz;
}
}
② 、 ServletMappingConfig
package com.ye.myTomcat;
import java.util.ArrayList;
import java.util.List;
public class ServletMappingConfig {
public static List<ServletMapping> servletMappingList = new ArrayList<ServletMapping>();
//相当web.xml中通过和来进行指定哪个URL交给哪个servlet进行处理。
static {
servletMappingList.add(new ServletMapping("findServlet", "/find", "com.ye.myTomcat.FindServlet"));
servletMappingList.add(new ServletMapping("helloWorldServlet", "/hello", "com.ye.myTomcat.HelloWorldServlet"));
//servletMappingList.add(new ServletMapping("faviconServlet", "/favicon.ico", "com.ye.myTomcat.FaviconServlet"));
}
}
4 、 tomcat 入口类 MyTomcat
package com.ye.myTomcat;
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;
public class MyTomcat {
private int port = 8080;
private Map<String, String> urlServletMap = new HashMap<String, String>();
public MyTomcat(int port) {
this.port = port;
}
public void start() {
initServletMapping();
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(port);
System.out.println("MyTomcat is start...");
while(true) {
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream();
//完成构造方法后,就可以获取 http协议的方法名,url,以及参数
MyRequest myRequest = new MyRequest(inputStream);
MyResponse myResponse = new MyResponse(outputStream);
dispatch(myRequest,myResponse);
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(serverSocket !=null) {
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void initServletMapping() {
//初始化 ServletMapping
for (ServletMapping servletMapping : ServletMappingConfig.servletMappingList) {
urlServletMap.put(servletMapping.getUrl(), servletMapping.getClazz());
}
}
private void dispatch(MyRequest myRequest, MyResponse myResponse) {
String clazz = urlServletMap.get(myRequest.getUrl());
try {
//通过初始化的ServletMapping获取servlet的类路径,从而通过反射实例化
Class<MyServlet> myServletClass = (Class<MyServlet>) Class.forName(clazz);
MyServlet myServlet = myServletClass.newInstance();
myServlet.service(myRequest, myResponse);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new MyTomcat(8080).start();
}
}
5 、 测试
以我的代码为例,访问http://localhost:8080/hello?name=zhangsan&password=123456
你可能会看到如下结果
/hello成功,你可能会疑惑我又访问/favicon.ioc,他怎么自己访问了,其实/favicon.ioc代表加载图标
如这些东西,他会默认加载你没有就会fail
后台你可能也会报错,ServerSocket会关闭,是空指针,因为你没有配置这个接口当然会是这样
当然你现在知道是什么原因造成肯定也有解决办法,你可以做if判断,如果为/favicon.ioc不做任何处理,也可以在ServletMappingConfig中配置/favicon.ioc,我的是做了配置,新建了一个类
//ServletMappingConfig配置
servletMappingList.add(new ServletMapping("faviconServlet", "/favicon.ico", "com.ye.myTomcat.FaviconServlet"));
修改过后现在访问就不会报错,如图,现在已经用get请求实现简单的前后台交互
post请求如何处理,后续会添加进来,源码里又加了MySQL数据库连接,数据库接口以及登录验证,需要的博友自行去git拉取。
昨天你领完红包就把我删了,我陷入久久地沉思。我想这其中一定有什么含义,原来你是在欲擒故
纵,嫌我不够爱你。无理取闹的你变得更加可爱了,我会坚守我对你的爱的。你放心好啦!今天发工资
了,发了1850,给你微信转了520,支付宝1314,还剩下16。给你发了很多消息你没回。剩下16块我在
小卖部买了你爱吃的老坛酸菜牛肉面,给你寄过去了。希望你保护好食欲,我去上班了爱你~~