服务器基本功能实现,实现一套接口
1.注解(此注解作用类似于spring中的RequestMapper)
package com.zj;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE , ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapper {
String path() default "" ;
}
2.服务器需要的接口
1)HttpServlet
package com.inter;
public interface HttpServlet {
void service(HttpServletRequest request , HttpServletResponse response) throws Exception ;
}
2)HttpServletRequest
package com.inter;
import java.util.Map;
public interface HttpServletRequest {
/**
* 获取请求参数
* @param key 请求参数
* @return 请求参数的值
*/
String getParameter(String key) ;
/**
* 设置请求编码
* @param charset 编码类型
*/
void setCharacter(String charset) ;
/**
* 获取请求头参数
* @param key
* @return
*/
String getHeadField(String key) ;
/**
* 获取请求ip
*/
String getRequestIP() ;
/**
* 获取所有请求参数
* @return
*/
Map<String, String> getParameters() ;
}
3)HttpServletResponse
package com.inter;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
public interface HttpServletResponse {
/**
* 设置返回前端编码
**/
void setCharactor(String charset) ;
/**
* 获取浏览器io对象
* @return
*/
OutputStream getOutputStream() ;
/**
* 获取字符流对象
* @throws UnsupportedEncodingException
*/
PrintStream getWriter() throws UnsupportedEncodingException ;
/**
* 设置返回数据类型
* @throws Exception
*/
void setContentTyoe(String type) throws Exception ;
/**
* 设置重定向地址
* @param url
*/
void sendRedirect(String url) ;
}
这套接口是模仿javaweb的
3.接口实现
1)Request
package com.server;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpCookie;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
import org.jboss.com.sun.net.httpserver.Headers;
import org.jboss.com.sun.net.httpserver.HttpExchange;
import com.inter.HttpServletRequest;
/**
* 采用适配器原理将exange对象拆分成Request和Response两个对象
* @author wf
*
*/
public class Request implements HttpServletRequest {
//定义一个Exange对象
private HttpExchange exchange ;
/**
* 保存字符串编码
*/
private String charset = "utf-8" ;
/**
* 请求参数保存
*/
private Map<String, String> query = null ;
/**
* 参数部分字符串
*/
private String paramesString = "" ;
/**
* 保存当前访问对象的cookie
*/
private HttpCookie cookie ;
/**
* 构造方法
* @param exchange 每次请求的exange对象
*/
public Request(HttpExchange exchange){
this.exchange = exchange ;
}
public String getParameter(String key) {
if(query == null){
query = queryFormat() ;
}
return query.get(key);
}
public Map<String, String> getParameters() {
if(query == null){
query = queryFormat() ;
}
return query;
}
public void setCharacter(String charset) {
this.charset = charset ;
query = null ;
queryFormat() ;
}
public String getHeadField(String key) {
Headers header = exchange.getRequestHeaders() ;
return (header.get(key) != null ? header.get(key).toString() : null);
}
public String getRequestIP() {
return exchange.getRemoteAddress().getHostName();
}
/**
* 请求参数拆分
*/
private Map<String, String> queryFormat(){
if(paramesString == null || "".equals(paramesString)){
paramesString = queryGet() ;
}
return getQuerys() ;
}
/**
* 请求参数拆分
*/
private String queryGet(){
//获取请求的地址
String urlString = exchange.getRequestURI().toString() ;
//拿到输出流对象
OutputStream out = null ;
BufferedReader read = new BufferedReader(new InputStreamReader(exchange.getRequestBody())) ;
String line = "" ;
urlString = urlString.contains("?") ? urlString+"&" : urlString + "?" ;
//读取POst请求参数
try{
while((line = read.readLine()) != null){
urlString += line ;
}
}catch(Exception e){
System.out.println(e);
int len = e.toString().getBytes().length ;
exchange.getResponseHeaders().add("Content-Type", "text/html;charset=utf-8");
try {
exchange.sendResponseHeaders(500, len);
out = exchange.getResponseBody() ;
out.write(e.toString().getBytes());
} catch (Exception e2) {
System.out.println(e);
} finally{
if(out != null){
try {
out.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
}finally{
if(read != null){
try {
read.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//拆分参数部分字符串
String parames = "";
if (urlString.contains("?")) {
String[] strs = urlString.split("\\?");
if(strs.length > 1){
parames = strs[1];
}
}
return parames ;
}
/**
* 请求参数拆分
* @param parames
* @return
*/
private Map<String, String> getQuerys() {
Map<String , String> map = new HashMap() ;
if (!paramesString.equals("")) {
String[] strs = null ;
if(!paramesString.contains("&")){
strs = new String[]{paramesString};
}else{
strs = paramesString.split("&");
}
for (String string : strs) {
if (string.contains("=")) {
String[] strs1 = string.split("=") ;
if(strs1.length > 1){
String k = strs1[0];
String v = strs1[1];
try {
v = URLDecoder.decode(v, charset) ;
map.put(k, v);
} catch (UnsupportedEncodingException e) {
map.put(k, v);
}
}
}
}
}
return map;
}
}
2)Response
package com.server;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpCookie;
import java.util.Map;
import java.util.Set;
import org.jboss.com.sun.net.httpserver.Headers;
import org.jboss.com.sun.net.httpserver.HttpExchange;
import com.inter.HttpServletResponse;
public class Response implements HttpServletResponse {
//保存浏览器访问对象
private HttpExchange exchange ;
/**
* 保存返回前端编码
*/
private String charset = "utf-8" ;
/**
* 定义缓冲区大小
*/
private final int SIZE = 1024*1024 ;
/**
* 定义一个缓冲区(采用静态代理)
*/
private MyByteArrayOutputStream byteBuf = new MyByteArrayOutputStream(SIZE) ;
/**
* 请求头是否已经返回
*/
private boolean isSendHead = false ;
/**
* 返回头
*/
private Headers header ;
/**
* 重定向地址
*/
private String reUrl = null ;
/**
* @param exchange
*/
public Response(HttpExchange exchange) {
this.exchange = exchange ;
//初始化headers对象
header = exchange.getResponseHeaders() ;
//默认以text/html返回
header.add("Content-Type", "text/html");
}
public void setCharactor(String charset) {
this.charset = charset ;
}
public OutputStream getOutputStream() {
return byteBuf ;
}
public PrintStream getWriter() throws UnsupportedEncodingException {
return new PrintStream(byteBuf , true , charset);
}
public void setContentTyoe(String type) throws Exception {
if(isSendHead){
throw new Exception("header already send !!!") ;
}
if(header.containsKey("Content-Type")){
header.set("Content-Type", type);
return ;
}
header.add("Content-Type", type);
}
public void sendRedirect(String url) {
reUrl = url ;
}
public String getReUrl(){
return reUrl ;
}
/**
* 以下方法全部设置为私有方法供反射调用
*/
private synchronized void flush(){
try {
if(!isSendHead){
exchange.sendResponseHeaders(200 , 0);
isSendHead = true ;
}
exchange.getResponseBody().write(byteBuf.toByteArray());
} catch (IOException e) {
e.printStackTrace();
}
}
private String cookieFormat(Map<String, String> map){
Set<String> set = map.keySet() ;
StringBuffer buf = new StringBuffer() ;
for (String s : set) {
buf.append(s+"="+map.get(s)+";") ;
}
return buf.replace(buf.length()-1, buf.length(), "").toString() ;
}
/**
* 直接返回
*/
protected synchronized void response(int code , String data){
try {
if(!isSendHead){
Headers head = exchange.getResponseHeaders();
head.add("Content-Type", "text/html;charset="+charset);
exchange.sendResponseHeaders(code, data.getBytes(charset).length);
isSendHead = true ;
}
exchange.getResponseBody().write(data.getBytes(charset));
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 定义一个自己的字节流对象
* @author Administrator
*
*/
class MyByteArrayOutputStream extends ByteArrayOutputStream {
public MyByteArrayOutputStream(int size){
super(size) ;
}
@Override
public void write(byte[] b) throws IOException {
if(this.size() + 1 >= SIZE){
if(!isSendHead){
exchange.sendResponseHeaders(202, 0);
isSendHead = true ;
}
exchange.getResponseBody().write(byteBuf.toByteArray());
this.reset();
}
super.write(b);
}
@Override
public synchronized void write(byte[] b, int off, int len) {
if(this.size() + len >= SIZE){
try {
if(!isSendHead){
exchange.sendResponseHeaders(202, 0);
isSendHead = true ;
}
exchange.getResponseBody().write(byteBuf.toByteArray());
this.reset();
} catch (IOException e) {
e.printStackTrace();
}
}
super.write(b, off, len);
}
@Override
public synchronized void write(int b) {
if(this.size() + 1 >= SIZE){
try {
if(!isSendHead){
exchange.sendResponseHeaders(202, 0);
isSendHead = true ;
}
exchange.getResponseBody().write(byteBuf.toByteArray());
this.reset();
} catch (IOException e) {
e.printStackTrace();
}
}
super.write(b);
}
}
}
4.Server类的初始化
package com.server;
/******************************************************************************
* 对jdk的HttpServer再封装 *
* 实现一套注解(后端路由表&监听器注册) *
* 模仿javaweb的回话跟踪(Session) *
* 此项目的是练习java的设计模式 *
* 使用到了(单例,适配器,观察者,代理) *
******************************************************************************/
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.InetSocketAddress;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.jboss.com.sun.net.httpserver.HttpExchange;
import org.jboss.com.sun.net.httpserver.HttpHandler;
import org.jboss.com.sun.net.httpserver.HttpServer;
import com.inter.HttpServletRequest;
import com.inter.HttpServletResponse;
import com.zj.RequestMapper;
/**
* 对jdk自带的HttpServer类进行二次封装
* @author wf
*/
public class Server {
/**
* 绑定端口
*/
private int port = 9999 ;
/**
* servlet所在包名
*/
private String servlet = "com.servlet" ;
/**
* 保存访问地址的映射
*/
private Map<String , Object> urls = new HashMap() ;
/**
* 保存方法的映射
*/
private Map<Object, Map<String, Method>> murls = new HashMap<Object, Map<String,Method>>() ;
/**
* 404页面内容
*/
private final String S_404 = "<h1>404找不到页面</h1>" ;
/**
* 构造方法
* @param port 绑定端口
* @param servlet servlet所在包名
*/
public Server(int port , String servlet){
this.port = port ;
this.servlet = servlet ;
}
/**
* 服务器初始化
* @throws Exception
*/
public HttpServer init() throws Exception{
//创建HttpServer服务器
HttpServer server = HttpServer.create(new InetSocketAddress(port), 0) ;
//给注解类加上
regist(server) ;
//启动服务器
server.start();
return server ;
}
/**
* 注册实现
* @param server 服务器对象
* @throws Exception
*/
public void regist(HttpServer server) throws Exception{
//遍历servlet包将注解中的类加入Map集合
String packageName = servlet.replaceAll("\\.", "/") ;
packageName = Thread.currentThread().getContextClassLoader().getResource(packageName) != null ? Thread.currentThread().getContextClassLoader().getResource(packageName).getFile() : null ;
//扫描包中的类
if(packageName != null){
File[] files = new File(packageName).listFiles() ;
for (File f : files) {
String className = f.getName().replace(".class", "") ;
Class c = Class.forName(servlet+"."+className) ;
doRegist(c);
}
}
//创建一个核心处理器
server.createContext("/", new HttpHandler() {
//读文件的io对象
public FileInputStream in = null ;
//写的io对象
public OutputStream out = null ;
//处理器代码
@SuppressWarnings("unused") //此注解可以忽略编译器警告
public void handle(HttpExchange exchange) throws IOException {
//对访问地址进行拆分
String urlString = exchange.getRequestURI().toString();
String url = urlString.contains("?") ? urlString.split("\\?" , 2)[0] : urlString ;
//实例化请求对象
Request request = new Request(exchange) ;
Response response = new Response(exchange) ;
//向Servlet发起请求
if(urls.get(url) != null){
try {
String method = request.getParameter("method") ;
if(method != null && murls.get(urls.get(url)) != null && murls.get(urls.get(url)).get(method) != null){
// System.err.println("time:"+new Date().toLocaleString()+" IP:"+request.getRequestIP()+" path:"+url+" params:"+paramepFormat(request));
murls.get(urls.get(url)).get(method).invoke(urls.get(url), request , response) ;
return ;
}else{
Method m = urls.get(url).getClass().getDeclaredMethod("service", HttpServletRequest.class , HttpServletResponse.class) ;
m.setAccessible(true);
if(m != null){
// System.err.println("time:"+new Date().toLocaleString()+" IP:"+request.getRequestIP()+" path:"+url+" params:"+paramepFormat(request));
m.invoke(urls.get(url), request , response) ;
return ;
}else{
// System.err.println("ERROR === time:"+new Date().toLocaleString()+" IP:"+request.getRequestIP()+" path:"+url+" params:"+paramepFormat(request));
response.response(404 , S_404) ;
return ;
}
}
} catch (Exception e) {
response.response(500 , e.toString()) ;
} finally{
if(exchange.getResponseBody() == null){
return ;
}
try {
Method m = response.getClass().getDeclaredMethod("flush") ;
m.setAccessible(true);
m.invoke(response) ;
} catch (Exception e) {}
if(exchange.getResponseBody() != null){
exchange.getResponseBody().close();
}
}
return ;
}
//请求的是文件
try {
writeHtml(exchange , url) ;
// System.err.println("time:"+new Date().toLocaleString()+" IP:"+request.getRequestIP()+" file:"+url);
} catch (Exception e) {
// System.err.println("ERROR === time:"+new Date().toLocaleString()+" IP:"+request.getRequestIP()+" file:"+url);
//System.out.println(e);
response.response(404 , S_404) ;
} finally{
if(this.in != null){
this.in.close();
}
if(exchange.getResponseBody() != null){
exchange.getResponseBody().close();
}
}
}
/**
* 文件的地址
* @param path
* @throws Exception
* @throws Exception
*/
public void writeHtml(HttpExchange exchange , String path) throws Exception{
String filePath = "./WebRoot"+path ;
this.in = new FileInputStream(filePath);
this.out = exchange.getResponseBody() ;
exchange.sendResponseHeaders(200, new File(filePath).length());
byte[] data = new byte[1024] ;
int len = -1 ;
while((len = in.read(data)) != -1){
out.write(data, 0, len);
}
}
public String paramepFormat(HttpServletRequest request){
//返回字符串
StringBuffer buf = new StringBuffer("[") ;
//获取请求全部参数
Map<String, String> params = request.getParameters() ;
//获取参数的建
Set<String> keys = params.keySet() ;
//遍历参数
for (String k : keys) {
buf.append(k+" => "+ params.get(k)+" ,") ;
}
if(buf.length() == 1){
buf.append("]") ;
}else{
buf = buf.replace(buf.length()-1, buf.length(), "]") ;
}
return buf.toString() ;
}
}) ;
}
//注解处理器
public void doRegist(Class<?> c) throws Exception{
//利用反射生成一个对象
Object o ;
try{
o = c.getConstructor().newInstance() ;
}catch(Exception e){
return ;
}
//判断该对象是否写了注解
RequestMapper reMapper = o.getClass().getAnnotation(RequestMapper.class) ;
if(reMapper != null){
System.err.println("time:"+new Date().toLocaleString()+" path:"+reMapper.path()+" file:"+c +" successful");
urls.put(reMapper.path() , o) ;
findMethod(o) ;
}
}
/**
* 遍历方法的找到含有RequestMapper的方法反射
*/
private void findMethod(Object o){
Method[] methods = o.getClass().getDeclaredMethods() ;
Map<String, Method> map = new HashMap<String, Method>() ;
for (Method m : methods) {
//允许私有方法被访问
m.setAccessible(true);
RequestMapper reMapper = m.getAnnotation(RequestMapper.class) ;
if(reMapper != null){
//将实现注解的方法加入murls集合中
Parameter[] fields = m.getParameters() ;
if(fields.length != 2){
throw new RuntimeException("实现注解的方法请传入HttpServletRequest和HttpServletResponse的对象") ;
}else{
if(fields[0].getType() != HttpServletRequest.class || fields[1].getType() != HttpServletResponse.class){
throw new RuntimeException("实现注解的方法请传入HttpServletRequest和HttpServletResponse的对象") ;
}
}
System.err.println("time:"+new Date().toLocaleString()+" path:"+reMapper.path()+" file:"+m +" successful");
map.put(reMapper.path(), m) ;
}
}
murls.put(o, map) ;
}
}
服务器的基本代码已经封装完成
接下来再com.servlet中写一个Servlet做测试
1.UserServlet
package com.servlet;
import java.io.UnsupportedEncodingException;
import com.inter.HttpServlet;
import com.inter.HttpServletRequest;
import com.inter.HttpServletResponse;
import com.zj.RequestMapper;
@RequestMapper(path = "/user.do")
public class UserServlet implements HttpServlet {
public void service(HttpServletRequest request, HttpServletResponse response)
throws Exception {
String name = request.getParameter("name") ;
response.getWriter().print(name);
}
@RequestMapper(path="regist")
public void regist(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException{
System.out.println("正在注册");
response.getWriter().print("Success");
}
}
建一个类启动服务器
Test
import com.server.Server;
public class Test {
public static void main(String[] args) throws Exception {
Server server = new Server(9999, "com.servlet") ; //9999表示监听端口,com.servlet表示等下要扫描这个包中的注解
server.init() ;
}
}
跑起来以后再浏览器中输入 http://127.0.0.1:9999/user.do?name=wf
浏览器会输出wf
输入 http://127.0.0.1:9999/user.do?method=regist
服务器端会在控制台输出“正在注册” , 浏览器会输出success
完整代码已经上传到github:https://github.com/wangffei/HttpServer
目前已经支持cookie的解析,支持session,还有一套类似于javaweb的基本语法(代码非常简单,只是平时练习的作品,需要使用的话可以自己扩充修改)