1.首先我们要监听一个端口,当浏览器发送请求的时候,我们通过监听端口获取socket对象,然后进行相应的==操作==。(定义Tomcat类)
2.一旦有浏览器发送请求,我们就会进行操作,考虑会有多个浏览器发送请求的情况,我们要把操作类实现多线程,具体来说,每当有浏览发送请求我们要开启线程完成相应的操作,具体的操作有:要获取浏览器发送的request请求的相关数据、根据得到的数据作为key去相关的容器获取实例对象,然后调用实例对象对应的service方法,(定义RequestHandler类)
3.tomcat会对http响应和请求进行包装得到httpServletRequest和HttpServletResponse对象,通过着两个对象我们可以获取浏览器http请求中携带的数据信息并且将相关的信息返回给浏览器。这里我们自己定义两个类,分别是HttpRequest和HttpResponse,HttpRequest这个类持有与socket关联的输入流,因此可以获取浏览器发送过来的信息,我们在这个类中实现一些方法来返回浏览器发送过来的数据,比如getMthod、getUri、getPatameters,通过这些方法,我们可以在处理的时候直接通过HttpRequest的实例来获取相关的信息。HttpResponse这个类持有与socket关联的文件输出流,同时我们需要设置一个response头信息,这样的话我们后面如果需要将自己的数据返回给浏览器,我们只需要将自己的信息与这个response头信息进行拼接即可。(定义HttpRequest和HttpResponse类
4.servlet规范,通过观察类的结构关系图我们可以知道,servlet规范可以分为三个模块,第一部分主要是一个Servlet接口,会定义一些抽象方法;第二个部分是HttpServlet抽象类,这个类实现了模板设计模式,具体就是实现了service方法,并且将HttpRequest对象和HttpResponse对象作为形参传进来,通过获取响应的方法,我们调用对应的get方法或者post方法;这里的doGet方法和doPost方法定义为抽象方法,具体的实现我们是在自己的servlet程序中实现实际的业务逻辑。(定义Myservlet接口、MyHttpServlet抽象类、MyCalServlet类)
补充:
TomCat会维持两个容器,一个容器对应web.xml文件的servlet标签,他的key存放servlerName,value用来存放对应的实例;另外一个容器对应servlet-mapping标签,key用来存储url,value用来存servletName
这个过程我们是利用DOM4j技术来实现的
RequestHandler的操作:
首先我们要获取浏览器的uri,因此我们要调用HttpRequest中的geturl方法,这个过程我们要创建HttpRequest实例,并且要把socket关联的inputStream传进去;有了uri之后,我们需要对uri进行处理,得到浏览器请求的资源,如果访问的是静态资源的话,我们直接获取静态资源并且利用HttpResponse返回给浏览器;如果请求的是servlet资源,我们需要去访问TomCat下的两个容器,根据uri中的资源名去找对应的实例对象,最后我们通过得到的实例对象调用service()方法。
Tomcat类代码
package com.lewisdeu.tomcat;
import com.lewisdeu.handler.RequestHandler;
import com.lewisdeu.servlet.MyHttpServlet;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
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;
/**
* @author lewis
* @version 1.0
*/
//存放容器servletMapping
// key 存放ServletName
// value 存放对应的实例
public class TomcatV3 {
public static final ConcurrentHashMap<String,MyHttpServlet>servletHapping
= new ConcurrentHashMap<>();
public static final ConcurrentHashMap<String,String>servletUrlMapping = new ConcurrentHashMap<>();
public static void main(String[] args) {
TomcatV3 tomcatV3 = new TomcatV3();
tomcatV3.init();
tomcatV3.run();
}
public void run(){
try {
ServerSocket serverSocket = new ServerSocket(8080);
System.out.println("tomcatV3在监听...");
while (!serverSocket.isClosed()){
Socket socket = serverSocket.accept();
RequestHandler requestHandler = new RequestHandler(socket);
new Thread(requestHandler).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 对两个容器进行初始化
public void init(){
//读取web。xml文件 dom4j
String path = TomcatV3.class.getResource("/").getPath();
// System.out.println(path);
//读取dom4j
SAXReader saxReader = new SAXReader();
try {
Document document = saxReader.read(new File(path+"web.xml"));
//得到根元素
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 servlet_class = element.element("servlet-class");
servletHapping.put(servletName.getText(),(MyHttpServlet) Class.forName(servlet_class.getText().trim()).newInstance());
}else if("servlet-mapping".equalsIgnoreCase(element.getName())){
//这是一个servlet-mapping的配置
// System.out.println("发现servlet-,apping");
Element servletName1 = element.element("servlet-name");
Element url_pattern = element.element("url-pattern");
System.out.println("通过dom4j获取的uri:"+url_pattern.getText());
System.out.println("uri对应的servletname在容器中:" +servletName1.getText());
servletUrlMapping.put(url_pattern.getText(),servletName1.getText());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
RequestHandler代码
package com.lewisdeu.handler;
import com.lewisdeu.http.HttpRequest;
import com.lewisdeu.http.HttpResponse;
import com.lewisdeu.servlet.MyHttpServlet;
import com.lewisdeu.tomcat.TomcatV3;
import java.io.*;
import java.net.Socket;
/**
* @author lewis
* @version 1.0
*/
public class RequestHandler implements Runnable{
//定义socket
private Socket socket;
public RequestHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
InputStream inputStream = null;
try {
inputStream = socket.getInputStream();
HttpRequest httpRequest = new HttpRequest(inputStream);
String method = httpRequest.getMethod();
String num1 = httpRequest.getParameter("num1");
String num2 = httpRequest.getParameter("num2");
System.out.println("method" + method);
System.out.println("num1 = " + num1 + "\tnum2 = "+ num2);
System.out.println(httpRequest);
HttpResponse httpResponse = new HttpResponse(socket.getOutputStream())
//得到uri
String uri = httpRequest.getUri();
System.out.println("handler中展示:"+uri);
//这里要判断是否是静态资源
if(uri.contains(".") && !"/favicon.ico".equalsIgnoreCase(uri)){
//如果uri中含有.结尾的,我们默认是静态资源/login.html
String substring = uri.substring(1);
String path = TomcatV3.class.getResource("/").getPath();
BufferedReader bufferedReader = new BufferedReader(new FileReader(path+substring));
String content;
String all = "";
OutputStream outputStream0 = httpResponse.getOutputStream();
while((content = bufferedReader.readLine()) != null){
all+= content;
}
outputStream0.write((HttpResponse.respHeader+all).getBytes());
outputStream0.close();
}else{
String servletName = TomcatV3.servletUrlMapping.get(uri);
System.out.println("根据输入的uri在容器中找到的servletname:"+servletName);
//进一步获取servlet的实例
// servlet的运行类型是MyCalServlet
if(servletName != null) {
MyHttpServlet servlet = TomcatV3.servletHapping.get(servletName);
if(servlet != null){
servlet.service(httpRequest,httpResponse);
}else {
//没有servlet
OutputStream outputStream1 = httpResponse.getOutputStream();
outputStream1.write((HttpResponse.respHeader+"<h1>404 not found<h1>").getBytes());
outputStream1.close();
}
}else {
OutputStream outputStream1 = httpResponse.getOutputStream();
outputStream1.write((HttpResponse.respHeader+"<h1>404 not found<h1>").getBytes());
outputStream1.close();
}
}
socket.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
//确保socket关闭,否则会出现阻塞
if(socket != null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
HttpRequest类和HttpResonse
package com.lewisdeu.http;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
/**
* @author lewis
* @version 1.0
*和原始的http请求关联的对象,模仿httpservletRequest
* HttpRequest封装http请求的数据
* 比如:方法(get/post)、uri、数据
* 数据格式:get /myCalServlet?num1=10&num2=20
*/
public class HttpRequest {
private String method;
private String uri;
private HashMap<String,String>parametersHashMapping = new HashMap<>();
private InputStream inputStream = null;
public HttpRequest(InputStream inputStream){
//完成对http请求的封装
this.inputStream = inputStream;
encapHttpRequest();
}
//将http的相关数据进行封装,然后提供相关的方法获取参数
public void encapHttpRequest(){
BufferedReader bufferedReader = null;
try {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream,"utf-8"));
String s = bufferedReader.readLine();
System.out.println(s);
// GET /calServlet?num1=20&num2=20 HTTP/1.1
String[] s1 = s.split(" ");
method = s1[0];
int index = s1[1].indexOf("?");
// uri = s1[1].substring(0,index);
//如果?的索引是-1,说明没有参数列表
if(index == -1){
uri = s1[1];
}else{
uri = s1[1].substring(0,index);
//获取参数列表放到hashmap中
String parameters = s1[1].substring(index + 1);
String[] param = parameters.split("&");
//防止用户提交的时候:/mycalServlet?
if(param != null &&!"".equals(param)){
for (int i = 0; i < param.length; i++) {
String[] split = param[i].split("=");
if(split.length == 2){
// System.out.println(split[0]);
// System.out.println(split[1]);
parametersHashMapping.put(split[0],split[1]);
}
}
}
}
// inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public String getMethod(){
return method;
}
public String getUri(){
return uri;
}
public String getParameter(String parameters){
if(parametersHashMapping.containsKey(parameters)){
String s = parametersHashMapping.get(parameters);
return s;
}else{
return null;
}
}
@Override
public String toString() {
return "HttpRequest{" +
"method='" + method + '\'' +
", uri='" + uri + '\'' +
", parametersHashMapping=" + parametersHashMapping +
'}';
}
}
package com.lewisdeu.http;
import java.io.OutputStream;
/**
* @author lewis
* @version 1.0
* 1.HttpResonse可以封装outputstream
*/
public class HttpResponse {
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";
//在创建httpresponse对象时,传入的outputstream是和socket关联的
public HttpResponse(OutputStream outputStream){
this.outputStream = outputStream;
}
//当我们要返回数据的时候,可以通过HttpResponse的输出流
public OutputStream getOutputStream(){
return outputStream;
}
}
servlet规范
package com.lewisdeu.http;
import java.io.OutputStream;
/**
* @author lewis
* @version 1.0
* 1.HttpResonse可以封装outputstream
*/
public class HttpResponse {
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";
//在创建httpresponse对象时,传入的outputstream是和socket关联的
public HttpResponse(OutputStream outputStream){
this.outputStream = outputStream;
}
//当我们要返回数据的时候,可以通过HttpResponse的输出流
public OutputStream getOutputStream(){
return outputStream;
}
}
package com.lewisdeu.servlet;
import com.lewisdeu.http.HttpRequest;
import com.lewisdeu.http.HttpResponse;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
/**
* @author lewis
* @version 1.0
*/
public abstract class MyHttpServlet implements MyServlet{
public void service(HttpRequest request, HttpResponse response){
if("GET".equalsIgnoreCase(request.getMethod())){
this.doGet(request, response);
}else if("POST".equalsIgnoreCase(request.getMethod())){
this.doPost(request, response);
}
}
//使用模板设计模式
public abstract void doGet(HttpRequest request,HttpResponse response);
public abstract void doPost(HttpRequest request,HttpResponse response);
}
package com.lewisdeu.servlet;
import com.lewisdeu.http.HttpRequest;
import com.lewisdeu.http.HttpResponse;
import com.lewisdeu.utils.WebUtils;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.io.OutputStream;
/**
* @author lewis
* @version 1.0
*/
public class MyCalServlet extends MyHttpServlet {
@Override
public void doGet(HttpRequest request, HttpResponse response) {
doPost(request, response);
}
@Override
public void doPost(HttpRequest request, HttpResponse response) {
//业务代码,完成计算
String num1 = request.getParameter("num1");
int num11 = WebUtils.toNum(num1,0);
String num2 = request.getParameter("num2");
int num22 = WebUtils.toNum(num2,0);
int sum = num11 + num22;
String res = HttpResponse.respHeader + "<h1>"+num1+"+"+num2+"="+sum+"</h1>";
OutputStream outputStream = response.getOutputStream();
try {
outputStream.write(res.getBytes());
outputStream.flush();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void init(ServletConfig var1) throws ServletException {
}
@Override
public void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException {
}
@Override
public void destroy() {
}
}
配置文件:web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>MyCalServlet</servlet-name>
<servlet-class>com.lewisdeu.servlet.MyCalServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MyCalServlet</servlet-name>
<url-pattern>/myCalServlet</url-pattern>
</servlet-mapping>
</web-app>
html文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>计算器</title>
</head>
<body>
<h1>计算器</h1>
<form action="/myCalServlet" method="get">
num1:<input type="text" name="num1"><br/>
num2:<input type="text" name="num2"><br/>
<input type="submit" value="提交">
</form>
</body>
</html>
运行结果:
要注意的是,本文中访问的servlet文件或者静态文件要放到target文件夹,如下图所示: