标签:
1.目的:
利用网络和线程的知识编写实现自己的Servlet容器,以Tomcat为模板
2.需求:
a)一个servlet容器,可以提供页面访问服务和servlet的服务.。
=> 根据uri请求的地址寻找文件并以流的方式输出
=> 动态load servlet字节码,并运行它( 按生命周期)
b)servlet容器它来控制servlet的生命周期
c)Servlet类必须要实现一个接口 Servlet , 提供所有的Servlet都要有的方法(生命周期)
d)对于要处理的资源有两种:静态资源/动态资源. 定义一个接口,写两个实现.
动态资源: http://localhost:8888/servlet/Hello
GET /servlet/hello HTTP/1.1
静态资源: http://localhost:8888/form.html
GET /index.html HTTP/1.1
=> 将这种处理定义成一个接口 Processor ( process() ) -> StaticProcessor
-> DynamicProcessor
3.运行流程:
a)Servlet运行流程
第一次访问:构造方法-> init() -> service() -> doGet()/doPost()
第二次访问: -> service() -> doGet()/doPost()
b)容器运行流程:
1.等待http请求,接收请求,做一些解析 -> uri (静态资源/动态资源)
2. 解析http请求,构造成一个 HttpServletRequest对象, HttpServletResponse对象.
3. 判断请求的资源的类型静态的资源/动态的资源 ,静态的资源 -> StaticProcessor类
动态资源 -> DynamicProcessor类, 必须要有 Request和Response对象
4. 动态加载Servlet的字节码,并调用service() -> 判断请求的方法,调用对应的 Servlet中的doGet()/doPost()
4.流程示意:
6.所需接口和其实现类:
由以下的类和接口组成:
HttpServer
ServerService
ServletRequest接口 -> HttpServletRequest类
ServletResponse接口 -> HttpServletResponse类
Processor接口 ( process( Request, Response) ) -> 静态资源 : StaticProcessor类
动态资源 : DynamicProcessor类
Servlet接口: 定义生命周期方法
TomcatConstants类:
7.源码(顺序根据程序运行流程)
HttpTomcat.java
package com.yhj.servlet.container; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public class HttpTomcat { private int port=8888; public static void main(String[] args) { HttpTomcat tomcat=new HttpTomcat(); tomcat.startServer(); } public void startServer(){ ServerSocket ss=null; try { // 其实就是一个套接字的服务器 ss=new ServerSocket(port); System.out.println("服务器开启"); } catch (IOException e) { e.printStackTrace(); } while(true){//为了满足多个网站的访问,这里设计为一个死循环,并且在每一次接收到socket之后,开启一个新的线程来处理这个请求 Socket s; try { s = ss.accept(); Thread t=new Thread(new ServerService(s));//把接收到的Socket传到ServerService中,它实现了Runnable接口 t.start(); } catch (IOException e) { e.printStackTrace(); } } } }
package com.yhj.servlet.container; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.util.HashMap; public class ServerService implements Runnable{ private Socket s; private InputStream iis; private OutputStream oos; private HashMap<String ,String >Session=null; public ServerService(Socket s) { super(); this.s = s;//接收外部的套接字, } @Override public void run() { try{ this.iis=this.s.getInputStream();//取到此套接字的输入输出流, this.oos=this.s.getOutputStream(); }catch(IOException e){ e.printStackTrace(); return; } try { //创建请求对象 ServletRequest request=new HttpServletRequest(this.iis);//请求获取输入流iis request.parse();//调用request中的解析请求头的方法 //创建响应对象 ServletResponse response=new HttpServletResponse(request,this.oos);//响应获取输出流oos,并传入request(已经维护好) //定义一个处理器 Processor processor=null; //工厂模式 if(request.getUri()!=null&&request.getUri().startsWith("/servlet/")){ processor=new DynamicProcessor();//判断uri是否以/servlet/开头,是的,则表明是动态的请求 }else{ processor=new StaticProcessor();//否则实例化静态资源 } //调用处理器的处理方法 //回调 processor.process(response, request); //http协议是一个无状态的,基于请求响应 this.s.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
package com.yhj.servlet.container; import java.util.HashMap; public interface ServletRequest { /** * 获取请求发来的参数 * @param key 键名 * @return 返回一个key对应的String字符串 */ public String getParameter(String key); /** * Http: GET /hello?name=yhj HTTP/1.1 * ***************************************** * POST /hello HTTP/1.1 * * name=yhj * **************************************** * */ public void parse(); /** * 获得uri * @return */ public String getUri(); /** * 获取提交方式 Get Post * @return */ public String getMethod(); public HashMap<String ,String> getSession(); }
package com.yhj.servlet.container; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; public class HttpServletRequest implements ServletRequest { /** * URL是Internet上用来描述信息资源的字符串,主要用在各种WWW客户程序和服务器程序上。采用URL可以用一种统一的格式来描述各种信息资源,包括文件、服务器的地址和目录等。 URL的格式由下列三部分组成: 协议(或称为服务方式); 存有该资源的主机IP地址(有时也包括端口号); 主机资源的具体地址。如目录和文件名等。 URI是以某种统一的(标准化的)方式标识资源的简单字符串, 一般由三部分组成: 访问资源的命名机制。 存放资源的主机名。 资源自身的名称,由路径表示。 */ private String uri; /**一个请求头示例 * POST /examples/default.jsp HTTP/1.1 Accept: text/plain; text/html Accept-Language: en-gb Connection: Keep-Alive Host: localhost User-Agent: Mozilla/4.0 Content-Length: 33 Content-Type: application/x-www-form-urlencoded Accept-Encoding: gzip, deflate lastName=Franks&firstName=Michael */ private InputStream iis; private String method; private Map<String ,String>parameter=new Hashtable<String ,String>();//形成request.getParamter("key")的效果 /** * 构造方法,传入一个输入流 * @param uri * @param iis */ public HttpServletRequest( InputStream iis) { super(); this.iis = iis; } public HttpServletRequest() { super(); } /** * 解析协议:最重要的就是取出uri */ public void parse(){ //从input中读出所有的内容(http请求协议=》protocal) String protocal=null; //TODO:从iis流中取出protocal StringBuffer sb=new StringBuffer(1024*10);//10k大小,一次性读取 int length=-1; byte[] bs=new byte[1024*10]; try { length=this.iis.read(bs); } catch (IOException e) { e.printStackTrace(); //读取发生了错误 length=-1;//如果发生解析错误,则处理protocal时将其赋值为null,则后续操作将无法执行,记住对于null的操作 } for(int j=0;j<length;j++){ sb.append((char)bs[j]); protocal=sb.toString(); } //从protocal中取出uri if(protocal==null||"".equals(protocal)){ return ; } this.uri=parseUri(protocal);//解析出uri this.method=parseMethod(protocal);//解析出method this.parameter=parseParameter(protocal);//解析出所有的参数 } /** * 解析参数 * @param protocal * @return */ // GET /servlet/hello?name=yhj&age=21 HTTP/1.1 // POST /servlet/hello // name=yhj&age=20 private Map<String, String> parseParameter(String protocal) { String parameterString=null; //取出参数部分,存到parameterString中取 if(this.method.equals(TomcatConstants.REQUEST_METHOD_GET)){//如果是Get方式,则paramter接在uri后面,从protocal中取出第一个空格到第二个空格之间的字符串 String[] ss=protocal.split(" "); int questionindex=ss[1].indexOf("?"); if(questionindex>0){ //只要有?,就截取 parameterString=ss[1].substring(questionindex+1); } }else if(this.method.equals(TomcatConstants.REQUEST_METHOD_POST)){//如果是Post方式,则在请求头的最后 parameterString=protocal.substring(protocal.lastIndexOf("\n")+1); } String [] keyvalues=null; if(parameterString!=null&¶meterString.length()>0){ keyvalues=parameterString.split("&");//取出所有的参数对 for(int i=0;i<keyvalues.length;i++){ String keyvalu=keyvalues[i];//取出每一对 String [] kv=keyvalu.split("=");//键值分开 this.parameter.put(kv[0], kv[1]);//存到map中,返回 } } return parameter; } private String parseMethod(String protocal) { String method=protocal.substring(0, protocal.indexOf(" "));//请求头是固定的,由之前给出的示例可以看出method的位置 return method; } public String parseProtocal(String iis){ String protocal=null; return protocal; } public String parseUri(String protocal){ //TODO:从protocal中解析出uri if(protocal==null||"".equals(protocal)){ return null;//防止之前protocal为空,将不执行任何操作 } String [] ss=protocal.split(" "); if(validateProtocal(ss)!=200){//校验的操作,这里不做实现,默认验证通过 return null; } if(ss[1].indexOf("?")>0){ this.uri=ss[1].substring(0,ss[1].indexOf("?"));//如果有参数,则先把参数去掉, }else{ this.uri=ss[1]; } return this.uri; } public int validateProtocal(String[] s){ return 200; } public String getUri(){ return uri; } @Override public String getMethod() { return this.method; } @Override public String getParameter(String key) { return this.parameter.get(key); } @Override public HashMap<String, String> getSession() { HashMap<String, String> temp=new HashMap<String,String>(); //temp.get(key) return null; } }
package com.yhj.servlet.container; import java.io.PrintWriter; public interface ServletResponse { /** * 获取PrintWriter * @return */ public PrintWriter getWriter(); public void sendRedirect(); }
package com.yhj.servlet.container; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; public class HttpServletResponse implements ServletResponse{ private String webroot=System.getProperty("user.dir")+File.separator+"webapps"; private ServletRequest request; /**一个响应头的示例 * HTTP/1.1 200 OK Server: IBM/4.0 Date: Sat, 6 Nov 2013 13:13:00 GMT Content-Type: text/html Last-Modified: Sat, 5 Jan 2013 13:13:12 GMT Content-Length: 112 */ private OutputStream out; /** * 构造方法 * @param request * @param out */ public HttpServletResponse(ServletRequest request,OutputStream out) { super(); this.request=request; this.out = out; } public void sendRedirect(){ /** * 1.从request中取出uri * 2.判断是否在本地存在uri指代的文件 * 3.没有404,有 * 4.以输入流读取这个文件 * 5.以输出流将文件写到客户端,要加入响应的协助 */ String uri=request.getUri(); if(uri==null){ return; } File f=new File(webroot,uri); String responseprotocal=null; if(!f.exists()){ //文件不存在,返回404 String httpBody=readFile(new File(webroot,"404.html")); responseprotocal=gen404(httpBody.getBytes().length); responseprotocal+=httpBody; }else{ String httpBody=readFile(f); responseprotocal=gen200(httpBody.getBytes().length); responseprotocal+=httpBody; } try{ out.write(responseprotocal.getBytes()); out.flush(); }catch(Exception e){ //输出500的错误 } } private String gen200(int length) { String protocal200="HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: "+length+"\r\n\r\n"; return protocal200; } private String gen404(int length) { String protocal404="HTTP/1.1 404 File Not Found\r\nContent-Type: text/html\r\nContent-Length: "+length+"\r\n\r\n"; return protocal404; } private String readFile(File f) { FileInputStream fis=null; StringBuffer sb=new StringBuffer(); try { fis=new FileInputStream(f); byte[] bs=new byte[1024]; int length=-1; while( (length=fis.read(bs,0,bs.length))!=-1 ){ sb.append(new String(bs,0,length)); } } catch (Exception e) { e.printStackTrace(); }finally{ if(fis!=null){ try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } return sb.toString(); } private PrintWriter output; @Override public PrintWriter getWriter() { this.output=new PrintWriter(out); return this.output; } }
package com.yhj.servlet.container; /** * 用来处理静态或动态的请求 * @author Administrator * */ public interface Processor { /** * 处理的方法 * @param response * @param request */ public void process( ServletResponse response,ServletRequest request ); }
StaticProcessor.java
package com.yhj.servlet.container; public class StaticProcessor implements Processor { @Override public void process(ServletResponse response, ServletRequest request) { response.sendRedirect(); } }
package com.yhj.servlet.container; import java.io.PrintWriter; import java.net.URL; import java.net.URLClassLoader; public class DynamicProcessor implements Processor { @Override public void process(ServletResponse response, ServletRequest request) { //取出uri /servlet/servlet类名 String uri=request.getUri(); //取出servletname String servletName=uri.substring(uri.lastIndexOf("/")+1); //3.leijiazai ->仍要通过url地址来加载servlet字节码 // URLClassLoader URLClassLoader loader=null; //4.通过字节码反射成一个对象, Servlet servlet = null; try { URL url=new URL("file",null,TomcatConstants.BASE_PATH); URL [] urls=new URL[]{url}; //创建了一个类加载器,这个加载器从项目下的webapps中读取servlet loader=new URLClassLoader(urls); //加servletname Class c=loader.loadClass(servletName);//因为是没有打包的java,类加载器默认加载的位置在bin下,所以可以加载到 servlet = (Servlet)c.newInstance(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } try{ if(servlet!=null&&servlet instanceof Servlet){ //控制生命周期=>init()=>service() servlet.init(); servlet.service(response, request); } }catch(Exception e){ String body=e.getMessage(); body=gen500(body.getBytes().length)+body; PrintWriter out=response.getWriter(); out.println(body); out.flush(); } } private String gen500(int length) { String protocal404="HTTP/1.1 500 Server Internal ERROR\r\nContent-Type: text/html\r\nContent-Length: "+length+"\r\n\r\n"; return protocal404; } }
package com.yhj.servlet.container; /** * 自定义的Servlet接口,可以用回调来控制生命周期 * @author Administrator * */ public interface Servlet { /** * init方法 */ public void init(); /** * Service方法,判断method为Get或者为POST,再对应调用 * @param response * @param request */ public void service( ServletResponse response,ServletRequest request ); /** * GET方式调用doGet * @param response * @param request */ public void doGet( ServletResponse response,ServletRequest request ); /** * POST方式采用doPost * @param response * @param request */ public void doPost( ServletResponse response,ServletRequest request ); }
import java.io.PrintWriter; import com.yhj.servlet.container.Servlet; import com.yhj.servlet.container.ServletRequest; import com.yhj.servlet.container.ServletResponse; public class Hello implements Servlet { public Hello(){ System.out.println("constructor"); } @Override public void doGet(ServletResponse response, ServletRequest request) { String uname=request.getParameter("uname"); int age=Integer.parseInt(request.getParameter("age")); String body="<html><head></head><body>姓名:"+uname+"年龄:"+age+"</body></html>"; String protocal="HTTP/1.1 200 OK\r\nContent-Type: text/html;charset=utf-8;\r\nContent-Length: "+body.getBytes().length+"\r\n\r\n"+body; PrintWriter out=response.getWriter(); out.println(protocal); out.flush(); } @Override public void doPost(ServletResponse response, ServletRequest request) { String uname=request.getParameter("uname"); String age=request.getParameter("age"); System.out.println(uname+"\t"+age); String body="{\"uname\":\""+uname+"\",\"age\":"+age+"}"; String protocal="HTTP/1.1 200 OK\r\nContent-Type: application/json;charset=utf-8;\r\nContent-Length: "+body.getBytes().length+"\r\n\r\n"+body; PrintWriter out=response.getWriter(); out.println(protocal); out.flush(); // PrintWriter out= } @Override public void init() { System.out.println("init()"); } @Override public void service(ServletResponse response, ServletRequest request) { //判断method是什么,调用doGet或者doPost方法 if(request.getMethod().equals("GET")){ doGet(response,request); }else if(request.getMethod().equals("POST")){ doPost(response,request); } } }
<!doctype html> <html> <head> <meta charset="utf-8"> <title>学生填表</title> <style> input{ margin:5px; } </style> <script src="js/jquery-1.11.3.min.js"></script> </head> <body> <form> <label>姓名:</label><input type="text" id="uname" name="uname"/><br/> <label>学号:</label><input type="text" id="age" name="age"/><br/> <!--<input url="/servlet/Hello" type="submit" value="提交" />--> <input type="button" value="提交" onClick="doSubmit()"/> </form> <script> function doSubmit(){ var uname=$("#uname").val(); var age=$("#age").val(); $.ajax({ url:'/servlet/Hello', type:'POST', dataType:'JSON', data:{'uname':uname,'age':age}, success:function(data){ if(data!=null&&data!=""){ document.write("<html><head></head><body>姓名:"+data.uname+"年龄:"+data.age+"</body></html>"); } } }); } </script> </body> </html>
a)浏览器访问:127.0.0.1:8888/form.html (静态访问)
b)浏览器访问( jquery ajax异步提交请求,Post方式):127.0.0.1:8888/form.html -->填写页面信息并提交
c)浏览器访问(Get方式):127.0.0.1:8888/servlet/Hello?uname=yhj&age=22(结果同上)
标签:
原文地址:http://blog.csdn.net/pkyyhh/article/details/51225120