到目前为止,我们手写的tomcat已经具备了基本的读取静态资源文件、读取web.xml文件来进行配置的功能,接下来就是对servlet的解析。
<web-app>
<welcome-file-list>
<welcome-file>../index.html</welcome-file>
<welcome-file>index.html</welcome-file>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>helllo</servlet-name>
<servlet-class>Server.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>helllo</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
在web.xml中,把servlet的配置写进去,同时,我也对整个项目的路径进行了一些修改:
我把这几个目录,都统统改在了servlet的目录下
在Server下建一个Servlet父类,定义post和get方法
package Server;
public abstract class Servlet {
public abstract void doGet(Request request, Response response);
public abstract void doPost(Request request, Response response);
public void service (Request request, Response response){
if ("GET".equals(request.getMethod())){
doGet(request,response);
}else if ("POST".equals(request.getMethod())){
doPost(request,response);
}else{
throw new RuntimeException();
}
}
}
接着写一个 HelloServlet 继承于Servlet
package Server.servlet;
import Server.Request;
import Server.Response;
import Server.Servlet;
public class HelloServlet extends Servlet {
@Override
public void doGet(Request request, Response response) {
System.out.println("doGet");
}
@Override
public void doPost(Request request, Response response) {
System.out.println("doPost");
}
}
然后在Resonse中用一个变量存放在web.xml中读取到的servlet,
public static List<Map> servletList = new ArrayList<Map>();
这在tomcat中对应的是Web Application,每个Web Application 由一个或多个Servlet 组成。当一个Web Application 被初始化的时候,它将用自己的ClassLoader 对象载入部署配置文件web.xml 中定义的每个Servlet 类。
之后,在xmlUtil中把servlet的相关配置加载进来
package Server;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class XmlUtil {
/*
* 用Map封装XML实体
* 一个标准的XML节点有3个参数:节点名String,参数名List<Map<String,Object>>,子节点List<Map>
* */
public static Object read(String path) throws Exception {
//SAXReader就是一个管道,用一个流的方式,把xml文件读出来
SAXReader reader = new SAXReader();
File file = new File(path);
Document document = reader.read(file);
Element root = document.getRootElement();
return root;
}
//解析Web.xml analysis 英文解析
public static boolean analysisWeb(){
File directory = new File("src/main/resources/web.xml");
try {
Element root = (Element) read(directory.getAbsolutePath());
if (!"web-app".equals(root.getName())){
return false;
}else{
List<Element> elements = root.elements();
for ( Element element : elements ) {
//welcome-file-list 欢迎文件页配置
if ("welcome-file-list".equals(element.getName())){
List<Element> welcome_file_list = element.elements();
if (welcome_file_list.size() > 0){
for ( Element welcome_file : welcome_file_list ){
if ("welcome-file".equals(welcome_file.getName()))
Response.indexPages.add(welcome_file.getText());
}
}
}
if ("servlet".equals(element.getName())){
List<Element> servlet = element.elements();
if (servlet.size() > 0){
for ( Element servlet_children : servlet ){
if ("servlet-name".equals(servlet_children.getName())) {
//如果servletName不存在,则新建,否则直接put对应的值
List<Map> servletList = Response.servletList;
if (servletList.size() > 0) {
for (int i = 0; i < servletList.size(); i++) {
Map servletMap = servletList.get(i);
if (!servlet_children.getText().equals(servletMap.get("name"))) {
Map map = new HashMap();
map.put("name", servlet_children.getText());
map.put("class", null);
for (Element servlet_children2 : servlet) {
if ("servlet-class".equals(servlet_children2.getName())) {
map.put("class", servlet_children2.getText());
}
}
Response.servletList.add(map);
} else {
servletMap.put("class", null);
for (Element servlet_children2 : servlet) {
if ("servlet-class".equals(servlet_children2.getName())) {
servletMap.put("class", servlet_children2.getText());
}
}
Response.servletList.set(i, servletMap);
}
}
}else {
Map map = new HashMap();
map.put("name", servlet_children.getText());
map.put("class", null);
for (Element servlet_children2 : servlet) {
if ("servlet-class".equals(servlet_children2.getName())) {
map.put("class", servlet_children2.getText());
}
}
Response.servletList.add(map);
}
}
}
}
}
if ("servlet-mapping".equals(element.getName())){
List<Element> servletMapping = element.elements();
if (servletMapping.size() > 0){
for ( Element servletMapping_children : servletMapping ){
if ("servlet-name".equals(servletMapping_children.getName())) {
//如果servletName不存在,则新建
List<Map> servletList = Response.servletList;
if (servletList.size() > 0) {
for (int i = 0; i < servletList.size(); i++) {
Map servletMap = servletList.get(i);
if (!servletMapping_children.getText().equals(servletMap.get("name"))) {
Map map = new HashMap();
map.put("name", servletMapping_children.getText());
map.put("url", null);
for (Element servlet_children2 : servletMapping) {
if ("url-pattern".equals(servlet_children2.getName())) {
map.put("url", servlet_children2.getText());
}
}
Response.servletList.add(map);
} else {
servletMap.put("url", null);
for (Element servlet_children2 : servletMapping) {
if ("url-pattern".equals(servlet_children2.getName())) {
servletMap.put("url", servlet_children2.getText());
}
}
Response.servletList.set(i, servletMap);
}
}
}else {
Map map = new HashMap();
map.put("name", servletMapping_children.getText());
map.put("url", null);
for (Element servlet_children2 : servletMapping) {
if ("url-pattern".equals(servlet_children2.getName())) {
map.put("url", servlet_children2.getText());
}
}
Response.servletList.add(map);
}
}
}
}
}
//error-page 通过错误码来配置error-page
// if ("error-page".equals(elements)){
// List<Element> welcome_file_list = element.elements();
// if (welcome_file_list.size() > 0){
// for ( Element welcome_file : welcome_file_list ){
// if ("welcome-file".equals(welcome_file.getName()))
// Server.Response.Index.add(welcome_file.getText());
// }
// }
// }
}
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
return false;
}
}
我解析servlet配置的思路是这样的:首先判断“Response.servletList”中是否有值,如果没有,那么直接新建一个map用于保存servlet的name、url、class,如果有,那就先取出来遍历,看看serlvet的name存不存在,如果存在,就直接覆盖对应的class或者url,否则新建一个servlet。
把配置中的servlet加载成功后,就在Response做判断,如果是访问的servlet,就根据访问的Url与servlet中的url做对比,找到就通过反射调用对应的POST、GET方法
package Server;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import Server.Servlet.*;
public class Response {
public static final int BUFFER_SIZE = 2048;
//根路径
private static final String RESOURCE = "";
private Request request;
private OutputStream output;
public static List<String> indexPages = new ArrayList<String>();
public static List<Map> servletList = new ArrayList<Map>();
public Response(OutputStream output) {
this.output = output;
}
public void setRequest(Request request) {
this.request = request;
}
public void sendStaticResource() throws IOException {
byte[] bytes = new byte[BUFFER_SIZE];
FileInputStream fis = null;
try {
//====================================访问的如果是首页
if ("/".equals(request.getUrL())) {
if (indexPages.size() > 0) {
for (int i = 0; i < indexPages.size(); i++) {
File file = new File(RESOURCE + "src/main/resources", indexPages.get(i));
if (null != getFile(file)) {
String retMessage = (String) getFile(file);
output.write(retMessage.getBytes());
return;
} else {
continue;
}
}
}
String retMessage = "<h1> 主页被吃了! </h1>";
String returnMessage = "HTTP/1.1 404 File Not Found\r\n" +
"Content-Type: text/html;charset=utf-8\r\n" +
"Content-Length: " + retMessage.getBytes().length + "\r\n" +
"\r\n" +
retMessage;
output.write(returnMessage.getBytes());
return;
}
//====================================首页结束
//如果是serlvet
if (request.getUrL().split("\\.").length == 1){
for ( Map map : servletList){
if (map.get("url").equals(request.getUrL())){
if (map.get("class") != null){
if ("POST".equals(request.getMethod())){
Method aClass = Class.forName((String) map.get("class")).getMethod("doPost", request.getClass(), this.getClass());
aClass.invoke(Class.forName((String) map.get("class")).newInstance(), request, this);
}else if ("GET".equals(request.getMethod())){
Method aClass = Class.forName((String) map.get("class")).getMethod("doGet", request.getClass(), this.getClass());
System.out.println(aClass);
aClass.invoke(Class.forName((String) map.get("class")).newInstance(), request, this);
}
}
}
}
}
//如果是静态资源
if (request.getUrL().endsWith(".html") || request.getUrL().endsWith(".css") || request.getUrL().endsWith(".js")) {
File file = new File(RESOURCE + "src/main/resources", request.getUrL());
String retMessage = (String) getFile(file);
if (null != retMessage) {
output.write(retMessage.getBytes());
} else {
//文件不存在,返回给浏览器响应提示,这里可以拼接HTML任何元素
retMessage = "<h1>" + file.getName() + " Page or directory not exists</h1>";
String returnMessage = "HTTP/1.1 404 Page Not Found\r\n" +
"Content-Type: text/html\r\n" +
"Content-Length: " + retMessage.getBytes().length + "\r\n" +
"\r\n" +
retMessage;
output.write(returnMessage.getBytes());
}
}
} catch (Exception e) {
System.out.println(e.toString());
} finally {
if (fis != null)
fis.close();
}
}
public Object getFile(File file) throws Exception {
FileInputStream fis = null;
String retMessage = null;
//如果文件存在,且不是个目录
if (file.exists() && !file.isDirectory()) {
fis = new FileInputStream(file);
InputStreamReader reader = new InputStreamReader(fis, "UTF-8"); //最后的"GBK"根据文件属性而定,如果不行,改成"UTF-8"试试
BufferedReader br = new BufferedReader(reader);
String line;
StringBuilder readLine = new StringBuilder();
while ((line = br.readLine()) != null) {
readLine.append(line);
}
retMessage = "HTTP/1.1 200 OK\r\n" +
"Content-Type: text/html;charset=utf-8\r\n" +
"Content-Length: " + readLine.toString().getBytes().length + "\r\n" +
"\r\n" +
readLine;
} else {
return null;
}
return retMessage;
}
}
最后贴一张图
至此,一个简陋版的tomcat就完成了。
如果有问题,可以留言交流。