浅析Tomcat(2)

浅析Tomcat(二)
4.现在创建response对象。这里的response类很简单,只需要输出功能
package com.arz.server;

import java.io.IOException;
import java.io.OutputStream;

public class Response {

    private OutputStream outStream;
    protected  Response(OutputStream outStream) {
        this.outStream = outStream;
    }
    
    public void print(String info){
        try{
            outStream.write(info.getBytes());
        }catch(IOException e){
            e.printStackTrace();
        }
    }
    
    public void println(String info){
        try{
            outStream.write((info+"\r\n").getBytes());
        }catch(IOException e){
            e.printStackTrace();
        }
    }
}

5.创建servlet类,这个servlet是个抽象类,因为在实际开发中这个类里面的service方法是我们来实现的所以也简单
package com.arz.servlet;

import com.arz.server.Request;
import com.arz.server.Response;

public abstract class Servlet {
    
    public abstract void service(Request request,Response response);
}

6.创建ServletContext这个类也是和java中的ServletContext对应。其实就是一个servlet的容器,里面是键-值对的形式保存在map中。这可以对应到整个web.xml。因为web.xml里面放着很多一对一对的servlet元素
package com.arz.servlet;

import java.util.HashMap;
import java.util.Map;

public class ServletContext {
    private Map<String,Servlet>context = new HashMap<String, Servlet>();

    public Map<String, Servlet> getContext() {
        return context;
    }

    public void setContext(Map<String, Servlet> context) {
        this.context = context;
    }
    
    public Servlet getServlet(String servletName){
        if(context != null){
            return context.get(servletName);
        }
        return null;
    }
}

7.下面是ServletMapping这个是存放web.xml中的<servlet-mapping>元素的对应关系
package com.arz.servlet;

public class ServletMapping {

    private String servletName;
    private String urlPattern;
    public String getServletName() {
        return servletName;
    }
    public void setServletName(String servletName) {
        this.servletName = servletName;
    }
    public String getUrlPattern() {
        return urlPattern;
    }
    public void setUrlPattern(String urlPattern) {
        this.urlPattern = urlPattern;
    }
    
    public ServletMapping(String str,String url){
        this.servletName = str;
        this.urlPattern = url;
    }
}

8.下面的类是解析web.xml并且将servlet-name,servlet-calssdui对应起来保存在ServletContext里面。将servlet-name,url-pattern对应起来保存在ServletMapping这样当知道是什么url时就可以找到对应的class来进行。这里解析xml使用的是jdom方式,你也可以使用其他方法解析。我会将jdom包放在附件中service
package com.arz.servlet;

import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import org.jdom.xpath.XPath;

public class WebApp {

    private static String WEB_ROOT = "/home";
    private ServletContext servletContext;
    private Map<String, ServletMapping> servletMappings = new HashMap<String, ServletMapping>();
    
    private WebApp(){
        parseWebXml();
    }
    private static WebApp webApp;
    
    public static WebApp getWebApp(){
        if(webApp == null){
            webApp = new WebApp();
        }
        return webApp;
    }
    
    private void parseWebXml(){
        //使用jdom解析XML
        SAXBuilder builder = new SAXBuilder();
        try{
            Document doc = builder.build(new File(WEB_ROOT+"/WEB_INFO/web.xml"));
            XPath xpath = XPath.newInstance("//servlet");
            servletContext = new ServletContext();
            List list = xpath.selectNodes(doc.getRootElement());
            
            //将xml中的servlet-name对应到class类
            for(Iterator i = list.iterator(); i.hasNext();){
                Element e = (Element)i.next();
                String servletname = e.getChildText("servlet-name");
                String servletClass = e.getChildText("servlet_class");
                Servlet servlet = (Servlet)Class.forName(servletClass).newInstance();
                servletContext.getContext().put(servletname, servlet);
            }
            
            //将xml中的servlet-name对应到url-pattern(地址栏的请求路径)
            xpath = XPath.newInstance("//servlet-mapping");
            List mappings = xpath.selectNodes(doc.getRootElement());
            for(Iterator i = mappings.iterator(); i.hasNext(); ){
                Element e =(Element)i.next();
                String servletName = e.getChildText("servlet-name");
                String urlPattern = e.getChildText("url-pattern");
                ServletMapping mapping = new ServletMapping(servletName,urlPattern);
                servletMappings.put(urlPattern,mapping);
            }
            
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    public ServletContext getServletContext() {
        return servletContext;
    }

    public void setServletContext(ServletContext servletContext) {
        this.servletContext = servletContext;
    }

    public Map<String, ServletMapping> getServletMappings() {
        return servletMappings;
    }

    public void setServletMappings(Map<String, ServletMapping> servletMappings) {
        this.servletMappings = servletMappings;
    }
}

9.现在写一个测试的Servlet。然后将这个Servlet配置在web.xml中等待请求
package com.arz.servlet;

import java.text.SimpleDateFormat;
import java.util.Date;

import com.arz.server.Request;
import com.arz.server.Response;

public class TestServlet extends Servlet {

    private static SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    @Override
    public void service(Request request, Response response) {
        // TODO Auto-generated method stub
        String html = "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html;charset=gbk\"/>"+
            "</head>"+
            "<body>"+
            "<h2>通信测试.....</h2>"+
            "<h2>服务器当前时间是:"+SDF.format(new Date())+"</h2>"+
            "</body></html>";
        response.print(html);
    }

}

这样就大功告成,现在在浏览器中输入http://127.0.0.1:8080/项目名/配置的servlet名(url-pattern)这样就可以看到你输入的html了。
10.简单对上面的过程描述一下
A.“Tomcat”启动,监听着本机端口8080
B.浏览器发送请求:http://127.0.0.1:8080/项目名/配置的servlet名(url-pattern)。没有携带参数被Tomcat捕捉
C.通过socket对象Tomcat封装好HttpServletRequest、HttpServletResponse为执行Servlet的service方法提供参数。并且构造好HttpContext和HttpServlet等对象也就是那六个对象
D.通过解析web.xml调用对应的Servlet类。
E.剩下的事就是我们程序员做的,实例这个类重写service方法或者是doGet()/doPost()
在这里我们应当注意我们使用的Servlet的各种类都tomcat创建的。java只是提供了接口。真正实现的是Tomcat