java nio手动实现简单的http服务器
需求分析
最近在学习HTTP协议,还是希望动手去做一做,所以就自己实现了一个http服务器,主要功能是将http请求封装httpRequest,通过解析web.xml,用不同的handler处理不同的uri,然后再将封装好的httpResponse还原成http响应返回浏览器。
代码已经成功上传至 GitHub
如果对你学习JavaNIO有帮助的话,记得给个star哦!
代码
使用java nio实现监听,完成服务器监听线程
packagecom.cszjo.com.http.server;importjava.io.ByteArrayOutputStream;importjava.io.IOException;importjava.net.InetSocketAddress;importjava.net.ServerSocket;importjava.nio.ByteBuffer;importjava.nio.channels.SelectionKey;importjava.nio.channels.Selector;importjava.nio.channels.ServerSocketChannel;importjava.nio.channels.SocketChannel;importjava.util.Iterator;importjava.util.Set;importorg.apache.log4j.Logger;importcom.cszjo.com.http.handler.HttpHandler;importcom.cszjo.com.http.utils.XMLUtil;/*** @Title: Server.java
* @Description: 打开服务
*@author: Han
* @date: 2016年7月12日 下午7:22:47*/
public class Server implementsRunnable {private boolean interrupted = false;private Logger logger = Logger.getLogger(Server.class);public Server(booleaninterrupted) {this.interrupted =interrupted;
}
@Overridepublic voidrun() {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(newInetSocketAddress(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 keys =selector.selectedKeys();//获得set集合的迭代器
Iterator 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(newHttpHandler(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) throwsException {//声明一个1024大小的缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);byte[] bytes = null;int size = 0;//定义一个字节数组输出流
ByteArrayOutputStream baos = newByteArrayOutputStream();//将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 newString(bytes);
}
}
实现MapHandler,解析web.xml,完成uri到对应handler的映射,该handler使用单例完成
packagecom.cszjo.com.http.handler;importjava.util.HashMap;importjava.util.List;importjava.util.Map;importorg.apache.log4j.Logger;importorg.dom4j.Element;importcom.cszjo.com.http.utils.XMLUtil;/*** @Title: HandlerMap.java
* @Description: HandlerMap(单例) 访问路径--->相应解决类
*@author: Han
* @date: 2016年7月15日 下午4:52:29*/
public classMapHandler {//访问路径对应控制类
private static Map handlerMap = new HashMap<>();private static MapHandler instance = null;//将构造器私有化
privateMapHandler(){}//得到HandlerMap对象实例
public staticMapHandler getContextMapInstance() {if(instance == null) {synchronized (MapHandler.class) {if(instance == null) {
instance= newMapHandler();//得到web.xml的根路径
Element rootElement = XMLUtil.getRootElement("web.xml");//得到handler的集合
List 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();
}
}
}
}
}returninstance;
}public MapgetHandlerMap() {returnhandlerMap;
}
}
web.xml
com.cszjo.com.http.handler.impl.LogionHandler
/login
httpHandler,处理一次http请求,通过uri启动不同的handler进行处理
packagecom.cszjo.com.http.handler;importjava.nio.channels.SelectionKey;importorg.apache.log4j.Logger;importcom.cszjo.com.http.context.Context;importcom.cszjo.com.http.context.impl.HttpContext;importcom.cszjo.com.http.handler.impl.NotFoundHandler;/*** @Title: HandlerHttp.java
* @Description: 处理一次Http请求
*@author: Han
* @date: 2016年7月15日 下午7:07:21*/
public class HttpHandler implementsRunnable {//就绪的I/O键
privateSelectionKey key;//上下文
private Context context = newHttpContext();//http请求字符串
privateString requestHeader;//针对uri选择不同的处理器
privateHandler handler;private Logger logger = Logger.getLogger(HttpHandler.class);publicHttpHandler(String requestHeader, SelectionKey key) {this.key =key;this.requestHeader =requestHeader;
}
@Overridepublic voidrun() {//初始化上下文
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 = newNotFoundHandler();
}//初始化handler并执行
handler.init(context);
}
}
Context上下文抽象类设计
packagecom.cszjo.com.http.context;importjava.nio.channels.SelectionKey;/*** @Title: Context.java
* @Description: Http上下文抽象类
*@author: Han
* @date: 2016年7月16日 下午2:19:06*/
public abstract classContext {protectedRequest request;protectedResponse response;/*** 设置当前连接的上下文
*@param:@return*@return: Context
* @Autor: Han*/
public abstract voidsetContext(String requestHeader, SelectionKey key);/*** 得到Request
*@param:@return*@return: Request
* @Autor: Han*/
publicRequest getRequest() {returnrequest;
}/*** 得到Response
*@param:@return*@return: Response
* @Autor: Han*/
publicResponse getResponse() {returnresponse;
}
}
HttpContext的实现
packagecom.cszjo.com.http.context.impl;importjava.nio.channels.SelectionKey;importcom.cszjo.com.http.context.Context;importcom.cszjo.com.http.context.Request;importcom.cszjo.com.http.context.Response;/*** @Title: HttpContext.java
* @Description: HttpContext http上下文
*@author: Han
* @date: 2016年7月16日 下午2:20:00*/
public class HttpContext extendsContext {privateRequest request;privateResponse response;
@Overridepublic voidsetContext(String requestHeader, SelectionKey key) {//初始化request
request = newHttpRequest(requestHeader);//初始化response
response = newHttpResponse(key);
setRequest();
setResponse();
}private voidsetRequest() {super.request = this.request;
}private voidsetResponse() {super.response = this.response;
}
}
Request接口设计
packagecom.cszjo.com.http.context;importjava.util.Map;importjava.util.Set;/*** @Title: Request.java
* @Description: 接口设计:Request接口
*@author: Han
* @date: 2016年7月15日 下午9:21:45*/
public interfaceRequest {public static final String POST = "POST";public static final String GET = "GET";/*** 得到参数
*@param:@return*@return: Map
* @Autor: Han*/
public MapgetAttribute();/*** 得到请求方式
*@param:@return*@return: String
* @Autor: Han*/
publicString getMethod();/*** 得到URI
*@param:@return*@return: String
* @Autor: Han*/
publicString getUri();/*** 版本协议
*@param:@return*@return: String
* @Autor: Han*/
publicString getProtocol();/*** 得到请求头Map
*@param:@return*@return: String
* @Autor: Han*/
public MapgetHeaders();/*** 得到请求头参数集合
*@param:@return*@return: String
* @Autor: Han*/
public SetgetHeaderNames();/*** 根据请求头名得到对应的请求头
*@param:@return*@return: String
* @Autor: Han*/
publicObject getHeader(String key);
}
HttpRequest实现
packagecom.cszjo.com.http.context.impl;importjava.util.HashMap;importjava.util.Map;importjava.util.Set;importcom.cszjo.com.http.context.Request;/*** @Title: HttpRequest.java
* @Description: HTTP请求(还有很多方法可以写的)
*@author: Han
* @date: 2016年7月15日 下午9:16:45*/
public class HttpRequest implementsRequest {//参数
private Map attribute = new HashMap<>();//请求头(Request Header)
private Map headers = new HashMap<>();//请求方法
privateString method;//uri
privateString uri;//协议版本
privateString protocol;publicHttpRequest(String httpHeader) {
init(httpHeader);
}private voidinit(String httpHeader) {//将请求分行
String[] headers = httpHeader.split("\r\n");//设置请求方式
initMethod(headers[0]);//设置URI
initURI(headers[0]);//设置版本协议
initProtocol(headers[0]);//设置请求头
initRequestHeaders(headers);
}/*** 设置请求方法
*@param:@paramstr
*@return: void
* @Autor: Han*/
private voidinitMethod(String str) {
method= str.substring(0, str.indexOf(" "));
}/*** 设置request参数
*@param:@paramattr
*@return: void
* @Autor: Han*/
private voidinitAttribute(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:@paramstr
*@return: void
* @Autor: Han*/
private voidinitURI(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:@paramstrs
*@return: void
* @Autor: Han*/
private voidinitRequestHeaders(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:@paramstr
*@return: void
* @Autor: Han*/
private voidinitProtocol(String str) {
protocol= str.substring(str.lastIndexOf(" ") + 1, str.length());
}
@Overridepublic MapgetAttribute() {returnattribute;
}
@OverridepublicString getMethod() {returnmethod;
}
@OverridepublicString getUri() {returnuri;
}
@OverridepublicString getProtocol() {returnprotocol;
}
@Overridepublic MapgetHeaders() {returnheaders;
}
@Overridepublic SetgetHeaderNames() {returnheaders.keySet();
}
@OverridepublicObject getHeader(String key) {returnheaders.get(key);
}
}
Response接口设计
packagecom.cszjo.com.http.context;importjava.nio.channels.SelectionKey;importcom.cszjo.com.http.utils.XMLUtil;/*** @Title: Response.java
* @Description: 接口设计:response接口
*@author: Han
* @date: 2016年7月16日 下午2:19:25*/
public interfaceResponse {//服务器名字
public static final String SERVER_NAME = XMLUtil.getRootElement("server.xml").element("serverName").getText();publicString getContentType();public intgetStatuCode();publicString getStatuCodeStr();publicString getHtmlFile();public voidsetHtmlFile(String htmlFile);publicSelectionKey getKey();public voidsetContentType(String contentType);public void setStatuCode(intstatuCode);public voidsetStatuCodeStr(String statuCodeStr);
}
httpResponse实现
packagecom.cszjo.com.http.context.impl;importjava.nio.channels.SelectionKey;importcom.cszjo.com.http.context.Response;/*** @Title: HttpResponse.java
* @Description: http响应
*@author: Han
* @date: 2016年7月16日 下午2:20:41*/
public class HttpResponse implementsResponse {privateSelectionKey key;//内容类型 defalut 为text/html
private String contentType = "text/html";//响应码 defalut 为200
private int StatuCode = 200;private String statuCodeStr = "OK";private String htmlFile = "";publicHttpResponse(SelectionKey key) {this.key =key;
}
@OverridepublicString getContentType() {returncontentType;
}
@Overridepublic intgetStatuCode() {returnStatuCode;
}
@OverridepublicSelectionKey getKey() {returnkey;
}
@OverridepublicString getStatuCodeStr() {returnstatuCodeStr;
}
@OverridepublicString getHtmlFile() {returnhtmlFile;
}
@Overridepublic voidsetHtmlFile(String htmlFile) {this.htmlFile =htmlFile;
}
@Overridepublic voidsetContentType(String contentType) {this.contentType =contentType;
}
@Overridepublic void setStatuCode(intstatuCode) {
StatuCode=statuCode;
}
@Overridepublic voidsetStatuCodeStr(String statuCodeStr) {this.statuCodeStr =statuCodeStr;
}
}
处理器Handler的接口设计
packagecom.cszjo.com.http.handler;importcom.cszjo.com.http.context.Context;/*** @Title: Handler.java
* @Description: 接口设计:处理器Handler接口
*@author: Han
* @date: 2016年7月12日 下午7:12:37*/
public interfaceHandler {/*** 初始化handler
*@param:@paramcontext
*@return: void
* @Autor: Han*/
public voidinit(Context context);/*** handler service(service应该不是这样做的... - -!)
*@param:@paramcontext
*@return: void
* @Autor: Han*/
public voidservice(Context context);/*** Get形式执行该方法
*@param:@paramcontext
*@return: void
* @Autor: Han*/
public voiddoGet(Context context);/*** POST形式执行该方法
*@param:@paramcontext
*@return: void
* @Autor: Han*/
public voiddoPost(Context context);/*** 销毁Handler(并没有销毁... - -!)
*@param:@paramcontext
*@return: void
* @Autor: Han*/
public voiddestory(Context context);
}
因为doGet或者doPost只会执行一个,所以中间在写一个抽象类,具体的handler只需要重写该抽象类的方法既可
packagecom.cszjo.com.http.handler.abs;importcom.cszjo.com.http.context.Context;importcom.cszjo.com.http.context.Request;importcom.cszjo.com.http.handler.Handler;importcom.cszjo.com.http.handler.ResponseHandler;/*** @Title: AbstractHandler.java
* @Description: Handler抽象类
*@author: Han
* @date: 2016年7月16日 下午2:11:57*/
public class AbstractHandler implementsHandler {protectedContext context;
@Overridepublic voidinit(Context context) {this.context =context;this.service(context);
}
@Overridepublic voidservice(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);
}
@Overridepublic voiddoGet(Context context) {
}
@Overridepublic voiddoPost(Context context) {
}
@Overridepublic voiddestory(Context context) {
context= null;
}/*** 通过上下文,返回封装response响应
*@param:@paramcontext
*@return: void
* @Autor: Han*/
private voidsendResponse(Context context) {newResponseHandler().write(context);
}
}
Login Handler的实现
packagecom.cszjo.com.http.handler.impl;importorg.apache.log4j.Logger;importcom.cszjo.com.http.context.Context;importcom.cszjo.com.http.handler.abs.AbstractHandler;/*** @Title: LogionHandler.java
* @Description: 解决login业务逻辑
*@author: Han
* @date: 2016年7月16日 下午2:08:18*/
public class LogionHandler extendsAbstractHandler{private Logger logger = Logger.getLogger(LogionHandler.class);
@Overridepublic voiddoGet(Context context) {
logger.info("进入了handler--->LoginHandler");
context.getResponse().setHtmlFile("login.html");
}
}
未找到请求的URI,所以返回404,该处理器处理404错误
packagecom.cszjo.com.http.handler.impl;importorg.apache.log4j.Logger;importcom.cszjo.com.http.context.Context;importcom.cszjo.com.http.context.Response;importcom.cszjo.com.http.handler.abs.AbstractHandler;/*** @Title: NotFoundHandler.java
* @Description: 解决404NotFound响应
*@author: Han
* @date: 2016年7月16日 下午2:08:44*/
public class NotFoundHandler extendsAbstractHandler {private Logger logger = Logger.getLogger(NotFoundHandler.class);privateResponse response;
@Overridepublic voiddoGet(Context context) {
logger.info("进入了404Handler");
response=context.getResponse();
response.setStatuCode(404);
response.setStatuCodeStr("Not Found");
response.setHtmlFile("404.html");
}
}
封装完http请求,下一步就需要还原response响应了
packagecom.cszjo.com.http.handler;importjava.io.BufferedReader;importjava.io.File;importjava.io.FileNotFoundException;importjava.io.FileReader;importjava.io.IOException;importjava.nio.ByteBuffer;importjava.nio.channels.SelectionKey;importjava.nio.channels.Selector;importjava.nio.channels.SocketChannel;importjava.util.Date;importorg.apache.log4j.Logger;importcom.cszjo.com.http.context.Context;importcom.cszjo.com.http.context.Request;importcom.cszjo.com.http.context.Response;/*** @Title: ResponseHandler.java
* @Description: 封装response响应
*@author: Han
* @date: 2016年7月16日 下午2:09:45*/
public classResponseHandler {privateRequest request;privateResponse response;privateString protocol;private intstatuCode;privateString statuCodeStr;privateByteBuffer buffer;privateString serverName;privateString contentType;privateSocketChannel channel;privateSelector selector;privateSelectionKey key;private Logger logger = Logger.getLogger(ResponseHandler.class);privateBufferedReader reader;privateString htmlFile;public voidwrite(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= newStringBuilder();//状态行
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();
}
}privateString setHtml(Context context) {
StringBuilder html= null;if(htmlFile != null && htmlFile.length() > 0) {
html= newStringBuilder();try{
reader= new BufferedReader(new FileReader(newFile(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();
}
}returnhtml.toString();
}
}
程序启动入口
packagecom.cszjo.com.http.server;/*** @Title: Solution.java
* @Description: 启动Web服务器入口
*@author: Han
* @date: 2016年7月12日 下午7:11:15*/
public classSolution {//启动方法
public static voidmain(String[] args) {new Thread(new Server(false)).start();
}
}
XMLUtils
packagecom.cszjo.com.http.utils;importjava.io.File;importjava.util.List;importorg.apache.log4j.Logger;importorg.dom4j.Document;importorg.dom4j.DocumentException;importorg.dom4j.Element;importorg.dom4j.io.SAXReader;/*** @Title: XMLUtil.java
* @Description: 解决XML读取问题
*@author: Han
* @date: 2016年7月15日 下午4:55:28*/
public classXMLUtil {private static Logger logger = Logger.getLogger(XMLUtil.class);private static SAXReader reader = newSAXReader();/*** 得到根节点
*@param:@paramxmlPath
*@param:@return*@return: Element
* @Autor: Han*/
public staticElement getRootElement(String xmlPath) {
Document document= null;;try{
document= reader.read(newFile(xmlPath));
}catch(DocumentException e) {
logger.error("找不到指定的xml文件的路径" + xmlPath + "!");return null;
}returndocument.getRootElement();
}/*** 得到该节点下的子节点集合
*@param:@paramelement
*@param:@return*@return: List
* @Autor: Han*/@SuppressWarnings("unchecked")public static ListgetElements(Element element) {returnelement.elements();
}/*** 得到该节点下指定的节点
*@param:@paramname
*@param:@return*@return: Element
* @Autor: Han*/
public staticElement getElement(Element element, String name) {
Element childElement=element.element(name);if(childElement == null) {
logger.error(element.getName()+ "节点下没有子节点" +name);return null;
}returnchildElement;
}/*** 得到该节点的内容
*@param:@paramelement
*@param:@return*@return: String
* @Autor: Han*/
public staticString getElementText(Element element) {returnelement.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
8089
Han`s Server
UTF-8
login.html
han登录密码: