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>
测试
启动服务
在浏览器中输入http://localhost:8089/login之后
浏览器显示
在浏览器中输入http://localhost:8089/lo之后
OK,成功!
代码已经成功上传至 GitHub