标签:
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