计算机网络(13)

时间:2022-05-16 14:30:25

java nio手动实现简单的http服务器

   需求分析

   最近在学习HTTP协议,还是希望动手去做一做,所以就自己实现了一个http服务器,主要功能是将http请求封装httpRequest,通过解析web.xml,用不同的handler处理不同的uri,然后再将封装好的httpResponse还原成http响应返回浏览器。

  代码

  使用java nio实现监听,完成服务器监听线程

package com.cszjo.com.http.server;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

import org.apache.log4j.Logger;

import com.cszjo.com.http.handler.HttpHandler;
import com.cszjo.com.http.utils.XMLUtil;

/**
* @Title: Server.java
* @Description: 打开服务
*
@author: Han
* @date: 2016年7月12日 下午7:22:47
*/
public class Server implements Runnable {

private boolean interrupted = false;

private Logger logger = Logger.getLogger(Server.class);

public Server(boolean interrupted) {
this.interrupted = interrupted;
}

@Override
public void run() {
try {
//打开一个选择器
Selector selector = Selector.open();
//打开ServerSocketChannel通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//得到ServerSocket对象
ServerSocket serverSocket = serverSocketChannel.socket();
//ServerSocketChannel通道监听server.xml中设置的端口
String portStr = XMLUtil.getRootElement("server.xml").element("port").getText();
serverSocket.setReuseAddress(
true);
try {
serverSocket.bind(
new InetSocketAddress(Integer.parseInt(portStr)));
}
catch (Exception e) {
logger.error(
"绑定端口失败,请检查server.xml中是否设置了port属性");
return;
}
logger.info(
"成功绑定端口" + portStr);
//将通道设置为非阻塞模式
serverSocketChannel.configureBlocking(false);
//将serverSocketChannel注册给选择器,并绑定ACCEPT事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

logger.info(
"服务器启动成功");
while(!interrupted) {
//查询就绪的通道数量
int readyChannels = selector.select();
//没有就绪的则继续进行循环
if(readyChannels == 0)
continue;
//获得就绪的selectionKey的set集合
Set<SelectionKey> keys = selector.selectedKeys();
//获得set集合的迭代器
Iterator<SelectionKey> iterator = keys.iterator();
while(iterator.hasNext()) {
SelectionKey key
= iterator.next();
if(key.isAcceptable()) {
//该key有ACCEPT事件
//将监听得到的channel强转为ServerSocketChannel
ServerSocketChannel server = (ServerSocketChannel) key.channel();
//得到接收到的SocketChannel
SocketChannel socketChannel = server.accept();
if(socketChannel != null) {
logger.info(
"收到了来自" + ((InetSocketAddress)socketChannel.getRemoteAddress()).getHostString()
+ "的请求");
//将socketChannel设置为阻塞模式
socketChannel.configureBlocking(false);
//将socketChannel注册到选择器
socketChannel.register(selector, SelectionKey.OP_READ);
}
}
else if (key.isReadable()) {
//该key有Read事件
SocketChannel socketChannel = (SocketChannel) key.channel();
String requestHeader
= "";
//拿出通道中的Http头请求
try {
requestHeader
= receive(socketChannel);
}
catch (Exception e) {
logger.error(
"读取socketChannel出错");
return;
}
//启动线程处理该请求,if条件判断一下,防止心跳包
if(requestHeader.length() > 0) {
logger.info(
"该请求的头格式为\r\n" + requestHeader);
logger.info(
"启动了子线程..");
new Thread(new HttpHandler(requestHeader, key)).start();
}
}
else if (key.isWritable()) {
//该key有Write事件
logger.info("有流写出!");
SocketChannel socketChannel
= (SocketChannel) key.channel();
socketChannel.shutdownInput();
socketChannel.close();
}
//从key集合中删除key,这一步很重要,就是因为没写这句,Selector.select()方法一直返回的是0
//原因分析可能是不从集合中删除,就不会回到I/O就绪事件中
iterator.remove();
}
}

}
catch (IOException e) {
e.printStackTrace();
}
catch (Exception e) {
e.printStackTrace();
}
}

private String receive(SocketChannel socketChannel) throws Exception {
//声明一个1024大小的缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
byte[] bytes = null;
int size = 0;
//定义一个字节数组输出流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//将socketChannel中的数据写入到buffer中,此时的buffer为写模式,size为写了多少个字节
while ((size = socketChannel.read(buffer)) > 0) {
//将写模式改为读模式
//The limit is set to the current position and then the position is set to zero.
//将limit设置为之前的position,而将position置为0,更多java nio的知识会写成博客的
buffer.flip();
bytes
= new byte[size];
//将Buffer写入到字节数组中
buffer.get(bytes);
//将字节数组写入到字节缓冲流中
baos.write(bytes);
//清空缓冲区
buffer.clear();
}
//将流转回字节数组
bytes = baos.toByteArray();
return new String(bytes);
}
}

    实现MapHandler,解析web.xml,完成uri到对应handler的映射,该handler使用单例完成

package com.cszjo.com.http.handler;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.dom4j.Element;

import com.cszjo.com.http.utils.XMLUtil;

/**
* @Title: HandlerMap.java
* @Description: HandlerMap(单例) 访问路径--->相应解决类
*
@author: Han
* @date: 2016年7月15日 下午4:52:29
*/
public class MapHandler {

//访问路径对应控制类
private static Map<String, Handler> handlerMap = new HashMap<>();

private static MapHandler instance = null;

//将构造器私有化
private MapHandler(){}

//得到HandlerMap对象实例
public static MapHandler getContextMapInstance() {

if(instance == null) {
synchronized (MapHandler.class) {
if(instance == null) {
instance
= new MapHandler();
//得到web.xml的根路径
Element rootElement = XMLUtil.getRootElement("web.xml");
//得到handler的集合
List<Element> handlers = XMLUtil.getElements(rootElement);
for (Element element : handlers) {
Element urlPattenEle
= XMLUtil.getElement(element, "url-patten");
//得到urlPatten(uri)
String urlPatten = XMLUtil.getElementText(urlPattenEle);
Element handlerClazzEle
= XMLUtil.getElement(element, "handler-class");
//得到handler 的class文件路径
String clazzPath = XMLUtil.getElementText(handlerClazzEle);
Class
<?> clazz = null;
try {
//通过反射得到handler实例化对象,然后以键值对的形式存储
clazz = Class.forName(clazzPath);
Handler handler
= (Handler)clazz.newInstance();
instance.getHandlerMap().put(urlPatten, handler);
Logger.getLogger(MapHandler.
class).info("成功添加Handler " + clazzPath);
}
catch (ClassNotFoundException e) {
e.printStackTrace();
}
catch (InstantiationException e) {
e.printStackTrace();
}
catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}

return instance;
}

public Map<String, Handler> getHandlerMap() {
return handlerMap;
}
}

  web.xml

<?xml version="1.0" encoding="UTF-8"?>
<server>
<handler>
<handler-class>com.cszjo.com.http.handler.impl.LogionHandler</handler-class>
<url-patten>/login</url-patten>
</handler>
</server>

  httpHandler,处理一次http请求,通过uri启动不同的handler进行处理

package com.cszjo.com.http.handler;

import java.nio.channels.SelectionKey;

import org.apache.log4j.Logger;

import com.cszjo.com.http.context.Context;
import com.cszjo.com.http.context.impl.HttpContext;
import com.cszjo.com.http.handler.impl.NotFoundHandler;

/**
* @Title: HandlerHttp.java
* @Description: 处理一次Http请求
*
@author: Han
* @date: 2016年7月15日 下午7:07:21
*/
public class HttpHandler implements Runnable {

//就绪的I/O键
private SelectionKey key;
//上下文
private Context context = new HttpContext();
//http请求字符串
private String requestHeader;
//针对uri选择不同的处理器
private Handler handler;
private Logger logger = Logger.getLogger(HttpHandler.class);

public HttpHandler(String requestHeader, SelectionKey key) {
this.key = key;
this.requestHeader = requestHeader;
}

@Override
public void run() {
//初始化上下文
context.setContext(requestHeader, key);
//得到uri
String uri = context.getRequest().getUri();
logger.info(
"得到了uri " + uri);
//得到MapHandler集合(uri-->handler)
handler = MapHandler.getContextMapInstance().getHandlerMap().get(uri);
//找不到对应的handler
if(handler == null) {
//404Handler进行处理
handler = new NotFoundHandler();
}
//初始化handler并执行
handler.init(context);
}
}

  Context上下文抽象类设计

package com.cszjo.com.http.context;

import java.nio.channels.SelectionKey;

/**
* @Title: Context.java
* @Description: Http上下文抽象类
*
@author: Han
* @date: 2016年7月16日 下午2:19:06
*/
public abstract class Context {

protected Request request;
protected Response response;

/**
* 设置当前连接的上下文
*
@param: @return
*
@return: Context
* @Autor: Han
*/
public abstract void setContext(String requestHeader, SelectionKey key);

/**
* 得到Request
*
@param: @return
*
@return: Request
* @Autor: Han
*/
public Request getRequest() {
return request;
}

/**
* 得到Response
*
@param: @return
*
@return: Response
* @Autor: Han
*/
public Response getResponse() {
return response;
}

}

  HttpContext的实现

package com.cszjo.com.http.context.impl;

import java.nio.channels.SelectionKey;

import com.cszjo.com.http.context.Context;
import com.cszjo.com.http.context.Request;
import com.cszjo.com.http.context.Response;

/**
* @Title: HttpContext.java
* @Description: HttpContext http上下文
*
@author: Han
* @date: 2016年7月16日 下午2:20:00
*/
public class HttpContext extends Context {

private Request request;
private Response response;

@Override
public void setContext(String requestHeader, SelectionKey key) {

//初始化request
request = new HttpRequest(requestHeader);
//初始化response
response = new HttpResponse(key);
setRequest();
setResponse();
}

private void setRequest() {
super.request = this.request;
}

private void setResponse() {
super.response = this.response;
}
}

  Request接口设计

package com.cszjo.com.http.context;

import java.util.Map;
import java.util.Set;

/**
* @Title: Request.java
* @Description: 接口设计:Request接口
*
@author: Han
* @date: 2016年7月15日 下午9:21:45
*/
public interface Request {

public static final String POST = "POST";

public static final String GET = "GET";
/**
* 得到参数
*
@param: @return
*
@return: Map<String,Object>
* @Autor: Han
*/
public Map<String, Object> getAttribute();

/**
* 得到请求方式
*
@param: @return
*
@return: String
* @Autor: Han
*/
public String getMethod();

/**
* 得到URI
*
@param: @return
*
@return: String
* @Autor: Han
*/
public String getUri();

/**
* 版本协议
*
@param: @return
*
@return: String
* @Autor: Han
*/
public String getProtocol();

/**
* 得到请求头Map
*
@param: @return
*
@return: String
* @Autor: Han
*/
public Map<String, Object> getHeaders();

/**
* 得到请求头参数集合
*
@param: @return
*
@return: String
* @Autor: Han
*/
public Set<String> getHeaderNames();

/**
* 根据请求头名得到对应的请求头
*
@param: @return
*
@return: String
* @Autor: Han
*/
public Object getHeader(String key);
}

  HttpRequest实现

package com.cszjo.com.http.context.impl;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import com.cszjo.com.http.context.Request;

/**
* @Title: HttpRequest.java
* @Description: HTTP请求(还有很多方法可以写的)
*
@author: Han
* @date: 2016年7月15日 下午9:16:45
*/
public class HttpRequest implements Request {

//参数
private Map<String, Object> attribute = new HashMap<>();

//请求头(Request Header)
private Map<String, Object> headers = new HashMap<>();

//请求方法
private String method;

//uri
private String uri;

//协议版本
private String protocol;

public HttpRequest(String httpHeader) {
init(httpHeader);
}

private void init(String httpHeader) {
//将请求分行
String[] headers = httpHeader.split("\r\n");
//设置请求方式
initMethod(headers[0]);
//设置URI
initURI(headers[0]);
//设置版本协议
initProtocol(headers[0]);
//设置请求头
initRequestHeaders(headers);
}

/**
* 设置请求方法
*
@param: @param str
*
@return: void
* @Autor: Han
*/
private void initMethod(String str) {
method
= str.substring(0, str.indexOf(" "));
}

/**
* 设置request参数
*
@param: @param attr
*
@return: void
* @Autor: Han
*/
private void initAttribute(String attr) {
String[] attrs
= attr.split("&");
for (String string : attrs) {
String key
= string.substring(0, string.indexOf("="));
String value
= string.substring(string.indexOf("=") + 1);
attribute.put(key, value);
}
}

/**
* 设置uri
*
@param: @param str
*
@return: void
* @Autor: Han
*/
private void initURI(String str) {
uri
= str.substring(str.indexOf(" ") + 1, str.indexOf(" ", str.indexOf(" ") + 1));
//如果是get方法,则后面跟着参数 /index?a=1&b=2
if(method.toUpperCase().equals("GET")) {
//有问号表示后面跟有参数
if(uri.contains("?")) {
String attr
= uri.substring(uri.indexOf("?") + 1, uri.length());
uri
= uri.substring(0, uri.indexOf("?"));
initAttribute(attr);
}
}
}

/**
* 初始化请求头
*
@param: @param strs
*
@return: void
* @Autor: Han
*/
private void initRequestHeaders(String[] strs) {
//去掉第一行
for(int i = 1; i < strs.length; i++) {
String key
= strs[i].substring(0, strs[i].indexOf(":"));
String value
= strs[i].substring(strs[i].indexOf(":") + 1);
headers.put(key, value);
}
}

/**
* 设置协议版本
*
@param: @param str
*
@return: void
* @Autor: Han
*/
private void initProtocol(String str) {
protocol
= str.substring(str.lastIndexOf(" ") + 1, str.length());
}

@Override
public Map<String, Object> getAttribute() {
return attribute;
}

@Override
public String getMethod() {
return method;
}

@Override
public String getUri() {
return uri;
}

@Override
public String getProtocol() {
return protocol;
}

@Override
public Map<String, Object> getHeaders() {
return headers;
}

@Override
public Set<String> getHeaderNames() {
return headers.keySet();
}

@Override
public Object getHeader(String key) {
return headers.get(key);
}
}

  Response接口设计

package com.cszjo.com.http.context;

import java.nio.channels.SelectionKey;

import com.cszjo.com.http.utils.XMLUtil;

/**
* @Title: Response.java
* @Description: 接口设计:response接口
*
@author: Han
* @date: 2016年7月16日 下午2:19:25
*/
public interface Response {

//服务器名字
public static final String SERVER_NAME = XMLUtil.getRootElement("server.xml").element("serverName").getText();

public String getContentType();

public int getStatuCode();

public String getStatuCodeStr();

public String getHtmlFile();

public void setHtmlFile(String htmlFile);

public SelectionKey getKey();

public void setContentType(String contentType);

public void setStatuCode(int statuCode);

public void setStatuCodeStr(String statuCodeStr);
}

  httpResponse实现

package com.cszjo.com.http.context.impl;

import java.nio.channels.SelectionKey;

import com.cszjo.com.http.context.Response;

/**
* @Title: HttpResponse.java
* @Description: http响应
*
@author: Han
* @date: 2016年7月16日 下午2:20:41
*/
public class HttpResponse implements Response {

private SelectionKey key;
//内容类型 defalut 为text/html
private String contentType = "text/html";
//响应码 defalut 为200
private int StatuCode = 200;
private String statuCodeStr = "OK";
private String htmlFile = "";

public HttpResponse(SelectionKey key) {
this.key = key;
}

@Override
public String getContentType() {
return contentType;
}

@Override
public int getStatuCode() {
return StatuCode;
}

@Override
public SelectionKey getKey() {
return key;
}

@Override
public String getStatuCodeStr() {
return statuCodeStr;
}

@Override
public String getHtmlFile() {
return htmlFile;
}

@Override
public void setHtmlFile(String htmlFile) {
this.htmlFile = htmlFile;
}

@Override
public void setContentType(String contentType) {
this.contentType = contentType;
}

@Override
public void setStatuCode(int statuCode) {
StatuCode
= statuCode;
}

@Override
public void setStatuCodeStr(String statuCodeStr) {
this.statuCodeStr = statuCodeStr;
}
}

  处理器Handler的接口设计

package com.cszjo.com.http.handler;

import com.cszjo.com.http.context.Context;

/**
* @Title: Handler.java
* @Description: 接口设计:处理器Handler接口
*
@author: Han
* @date: 2016年7月12日 下午7:12:37
*/
public interface Handler {

/**
* 初始化handler
*
@param: @param context
*
@return: void
* @Autor: Han
*/
public void init(Context context);

/**
* handler service(service应该不是这样做的... - -!)
*
@param: @param context
*
@return: void
* @Autor: Han
*/
public void service(Context context);

/**
* Get形式执行该方法
*
@param: @param context
*
@return: void
* @Autor: Han
*/
public void doGet(Context context);

/**
* POST形式执行该方法
*
@param: @param context
*
@return: void
* @Autor: Han
*/
public void doPost(Context context);

/**
* 销毁Handler(并没有销毁... - -!)
*
@param: @param context
*
@return: void
* @Autor: Han
*/
public void destory(Context context);
}

  因为doGet或者doPost只会执行一个,所以中间在写一个抽象类,具体的handler只需要重写该抽象类的方法既可

package com.cszjo.com.http.handler.abs;

import com.cszjo.com.http.context.Context;
import com.cszjo.com.http.context.Request;
import com.cszjo.com.http.handler.Handler;
import com.cszjo.com.http.handler.ResponseHandler;

/**
* @Title: AbstractHandler.java
* @Description: Handler抽象类
*
@author: Han
* @date: 2016年7月16日 下午2:11:57
*/
public class AbstractHandler implements Handler {

protected Context context;

@Override
public void init(Context context) {
this.context = context;
this.service(context);
}

@Override
public void service(Context context) {
//通过请求方式选择具体解决方法
String method = context.getRequest().getMethod();
if(method.equals(Request.GET)) {
this.doGet(context);
}
else if (method.equals(Request.POST)) {
this.doPost(context);
}
sendResponse(context);
}

@Override
public void doGet(Context context) {

}

@Override
public void doPost(Context context) {

}

@Override
public void destory(Context context) {
context
= null;
}

/**
* 通过上下文,返回封装response响应
*
@param: @param context
*
@return: void
* @Autor: Han
*/
private void sendResponse(Context context) {
new ResponseHandler().write(context);
}
}

  Login Handler的实现

package com.cszjo.com.http.handler.impl;

import org.apache.log4j.Logger;

import com.cszjo.com.http.context.Context;
import com.cszjo.com.http.handler.abs.AbstractHandler;

/**
* @Title: LogionHandler.java
* @Description: 解决login业务逻辑
*
@author: Han
* @date: 2016年7月16日 下午2:08:18
*/
public class LogionHandler extends AbstractHandler{

private Logger logger = Logger.getLogger(LogionHandler.class);

@Override
public void doGet(Context context) {
logger.info(
"进入了handler--->LoginHandler");
context.getResponse().setHtmlFile(
"login.html");
}
}

  未找到请求的URI,所以返回404,该处理器处理404错误

package com.cszjo.com.http.handler.impl;

import org.apache.log4j.Logger;

import com.cszjo.com.http.context.Context;
import com.cszjo.com.http.context.Response;
import com.cszjo.com.http.handler.abs.AbstractHandler;

/**
* @Title: NotFoundHandler.java
* @Description: 解决404NotFound响应
*
@author: Han
* @date: 2016年7月16日 下午2:08:44
*/
public class NotFoundHandler extends AbstractHandler {

private Logger logger = Logger.getLogger(NotFoundHandler.class);
private Response response;

@Override
public void doGet(Context context) {
logger.info(
"进入了404Handler");
response
= context.getResponse();

response.setStatuCode(
404);
response.setStatuCodeStr(
"Not Found");
response.setHtmlFile(
"404.html");
}
}

  封装完http请求,下一步就需要还原response响应了

package com.cszjo.com.http.handler;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Date;

import org.apache.log4j.Logger;

import com.cszjo.com.http.context.Context;
import com.cszjo.com.http.context.Request;
import com.cszjo.com.http.context.Response;

/**
* @Title: ResponseHandler.java
* @Description: 封装response响应
*
@author: Han
* @date: 2016年7月16日 下午2:09:45
*/
public class ResponseHandler {

private Request request;
private Response response;
private String protocol;
private int statuCode;
private String statuCodeStr;
private ByteBuffer buffer;
private String serverName;
private String contentType;
private SocketChannel channel;
private Selector selector;
private SelectionKey key;
private Logger logger = Logger.getLogger(ResponseHandler.class);
private BufferedReader reader;
private String htmlFile;

public void write(Context context) {
//从context中得到相应的参数
request = context.getRequest();
response
= context.getResponse();
buffer
= ByteBuffer.allocate(1024);
protocol
= request.getProtocol();
statuCode
= response.getStatuCode();
statuCodeStr
= response.getStatuCodeStr();
serverName
= Response.SERVER_NAME;
contentType
= response.getContentType();
key
= response.getKey();
selector
= key.selector();
channel
= (SocketChannel)key.channel();
htmlFile
= response.getHtmlFile();

//得到响应正文内容
String html = setHtml(context);

StringBuilder sb
= new StringBuilder();
//状态行
sb.append(protocol + " " + statuCode + " " + statuCodeStr + "\r\n");
//响应头
sb.append("Server: " + serverName + "\r\n");
sb.append(
"Content-Type: " + contentType + "\r\n");
sb.append(
"Date: " + new Date() + "\r\n");
if(reader != null) {
sb.append(
"Content-Length: " + html.getBytes().length + "\r\n");
}

//响应内容
sb.append("\r\n");
sb.append(html);

buffer.put(sb.toString().getBytes());
//从写模式,切换到读模式
buffer.flip();
try {
logger.info(
"生成相应\r\n" + sb.toString());
channel.register(selector, SelectionKey.OP_WRITE);
channel.write(buffer);
}
catch (IOException e) {
e.printStackTrace();
}
}

private String setHtml(Context context) {
StringBuilder html
= null;
if(htmlFile != null && htmlFile.length() > 0) {

html
= new StringBuilder();

try {
reader
= new BufferedReader(new FileReader(new File(htmlFile)));
String htmlStr;
htmlStr
= reader.readLine();
while(htmlStr != null) {
html.append(htmlStr
+ "\r\n");
htmlStr
= reader.readLine();
}

}
catch (FileNotFoundException e) {
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
}

return html.toString();
}
}

  程序启动入口

package com.cszjo.com.http.server;

/**
* @Title: Solution.java
* @Description: 启动Web服务器入口
*
@author: Han
* @date: 2016年7月12日 下午7:11:15
*/
public class Solution {

//启动方法
public static void main(String[] args) {
new Thread(new Server(false)).start();
}
}

  XMLUtils

package com.cszjo.com.http.utils;

import java.io.File;
import java.util.List;

import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

/**
* @Title: XMLUtil.java
* @Description: 解决XML读取问题
*
@author: Han
* @date: 2016年7月15日 下午4:55:28
*/
public class XMLUtil {

private static Logger logger = Logger.getLogger(XMLUtil.class);
private static SAXReader reader = new SAXReader();

/**
* 得到根节点
*
@param: @param xmlPath
*
@param: @return
*
@return: Element
* @Autor: Han
*/
public static Element getRootElement(String xmlPath) {
Document document
= null;;
try {
document
= reader.read(new File(xmlPath));
}
catch (DocumentException e) {
logger.error(
"找不到指定的xml文件的路径" + xmlPath + "!");
return null;
}
return document.getRootElement();
}

/**
* 得到该节点下的子节点集合
*
@param: @param element
*
@param: @return
*
@return: List<Element>
* @Autor: Han
*/
@SuppressWarnings(
"unchecked")
public static List<Element> getElements(Element element) {
return element.elements();
}

/**
* 得到该节点下指定的节点
*
@param: @param name
*
@param: @return
*
@return: Element
* @Autor: Han
*/
public static Element getElement(Element element, String name) {
Element childElement
= element.element(name);
if(childElement == null) {
logger.error(element.getName()
+ "节点下没有子节点" + name);
return null;
}
return childElement;
}

/**
* 得到该节点的内容
*
@param: @param element
*
@param: @return
*
@return: String
* @Autor: Han
*/
public static String getElementText(Element element) {
return element.getText();
}
}

  该项目需要用到的两个包:log4j,dom4j

  其余配置文件和静态文件

  log4j.properties

 ### \u8BBE\u7F6E###
log4j.rootLogger = debug,stdout,D,E

### \u8F93\u51FA\u4FE1\u606F\u5230\u63A7\u5236\u62AC ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

### \u8F93\u51FADEBUG \u7EA7\u522B\u4EE5\u4E0A\u7684\u65E5\u5FD7\u5230=E://logs/error.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = E://logs/log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n

### \u8F93\u51FAERROR \u7EA7\u522B\u4EE5\u4E0A\u7684\u65E5\u5FD7\u5230=E://logs/error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =E://logs/error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n

  server.xml

<?xml version="1.0" encoding="UTF-8"?>
<server>
<port>8089</port>
<serverName>Han`s Server</serverName>
<!-- 默认编码为UTF-8 -->
<charset>UTF-8</charset>
</server>

  login.html

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="Generator" content="EditPlus®">
<meta name="Author" content="">
<meta name="Keywords" content="">
<meta name="Description" content="">
<title>han登录</title>
</head>
<body>
用户名:
<input type="text" name="userName"><br/>
密码:
<input type="text" name="userName"><br/>
</body>
</html>

  404.html

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="Generator" content="EditPlus®">
<meta name="Author" content="">
<meta name="Keywords" content="">
<meta name="Description" content="">
<title>Document</title>
</head>
<body>
<h1>404 NOT Found</h1>
<strong style="color:red;">来自Han服务器</strong>
</body>
</html>

测试

  启动服务

  计算机网络(13)

  在浏览器中输入http://localhost:8089/login之后

  计算机网络(13)

  计算机网络(13)

  浏览器显示

  计算机网络(13)

  在浏览器中输入http://localhost:8089/lo之后

  计算机网络(13)

  OK,成功!

  代码已经成功上传至  GitHub