对HttpServer进行再封装,实现一个自己的服务器

服务器基本功能实现,实现一套接口

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

扫描二维码关注公众号,回复: 6063387 查看本文章
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的基本语法(代码非常简单,只是平时练习的作品,需要使用的话可以自己扩充修改)

猜你喜欢

转载自blog.csdn.net/weixin_43999566/article/details/89552888