国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

一步一步實(shí)現(xiàn)Tomcat之二——實(shí)現(xiàn)一個(gè)簡(jiǎn)單的Servlet容器

dayday_up / 754人閱讀

摘要:注本文使用規(guī)范是規(guī)范中的一個(gè)接口,我們可以自己實(shí)現(xiàn)這個(gè)接口在方法中實(shí)現(xiàn)自己的業(yè)務(wù)邏輯。我們只是實(shí)現(xiàn)一個(gè)簡(jiǎn)單的容器示例,所以和其他方法留待以后實(shí)現(xiàn)。運(yùn)行一下實(shí)現(xiàn)首先編寫(xiě)一個(gè)自己的實(shí)現(xiàn)類。

前言

經(jīng)過(guò)上一篇文章《一步一步實(shí)現(xiàn)Tomcat——實(shí)現(xiàn)一個(gè)簡(jiǎn)單的Web服務(wù)器》,我們實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的Web服務(wù)器,可以響應(yīng)瀏覽器請(qǐng)求顯示靜態(tài)Html頁(yè)面,本文更進(jìn)一步,實(shí)現(xiàn)一個(gè)Servlet容器,我們不只能響應(yīng)靜態(tài)頁(yè)面請(qǐng)求,還能響應(yīng)Servlet請(qǐng)求,雖然現(xiàn)在我們只能在自己的Servlet中打印出“Hello World!”,但是我們離Tomcat服務(wù)器更近了一步。

基礎(chǔ)知識(shí)

相信大家應(yīng)該對(duì)Java EE編程比較熟悉,故在此只簡(jiǎn)單的描述一下基本概念。

1. Java Servlet

Java Servlet 是運(yùn)行在 Web 服務(wù)器或應(yīng)用服務(wù)器上的程序,也可以說(shuō)是一組規(guī)范,只要按照規(guī)范實(shí)現(xiàn)自己的類,就可以在相應(yīng)的Servlet服務(wù)器(Tomcat、Jetty等)中運(yùn)行,響應(yīng)瀏覽器請(qǐng)求,動(dòng)態(tài)生成內(nèi)容。

注:本文使用Servlet 2.3規(guī)范

2. javax.servlet.Servlet

是Servlet規(guī)范中的一個(gè)接口,我們可以自己實(shí)現(xiàn)這個(gè)接口在service方法中實(shí)現(xiàn)自己的業(yè)務(wù)邏輯。
service方法簽名如下:

public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
3. javax.servlet.ServletRequest

表示一次請(qǐng)求的接口,由服務(wù)器生成相應(yīng)的實(shí)現(xiàn)類并傳送給上面的service(ServletRequest req, ServletResponse res)使用,用戶在實(shí)現(xiàn)自己的Servlet類是可以使用傳入的ServletRequest實(shí)現(xiàn)類中的各種方法,如請(qǐng)求地址,獲取請(qǐng)求參數(shù),獲取cookie等。

4. javax.servlet.ServletResponse

表示一次相應(yīng)的接口,由服務(wù)器生成相應(yīng)的實(shí)現(xiàn)類并傳送給上面的service(ServletRequest req, ServletResponse res)使用,用戶在實(shí)現(xiàn)自己的Servlet類是可以使用傳入的ServletRequest實(shí)現(xiàn)類中的各種方法,如設(shè)置http相應(yīng)頭部,向?yàn)g覽器打印數(shù)據(jù),跳轉(zhuǎn)頁(yè)面等。

用代碼說(shuō)話

【圖一】

如圖所示,一個(gè)簡(jiǎn)單的Servlet容器處理流程非常簡(jiǎn)單,我們只需要在上篇文章中代碼基礎(chǔ)上稍加改動(dòng),就可以實(shí)現(xiàn)我們想要的功能。

接收http請(qǐng)求工作我們已經(jīng)知道如何實(shí)現(xiàn)了,我們先從后兩項(xiàng)工作開(kāi)始。

1. 實(shí)現(xiàn)ServletRequest和ServletResponse類

上篇文章我們也抽象了一個(gè)Request和Response類,但是這兩類并沒(méi)有繼承ServletRequestServletResponse接口,所以Servlet無(wú)法使用,所以我們需要分別繼承相應(yīng)的接口。

1. 新Request類

原來(lái)Request中的方法都沒(méi)有變化,因?yàn)閷?shí)現(xiàn)了ServletRequest接口,所以必須實(shí)現(xiàn)接口中定義的方法,但是現(xiàn)在我們還無(wú)需具體實(shí)現(xiàn),大多都是返回null或留白。

/**
 * 表示請(qǐng)求值
 */
public class Request implements ServletRequest {

    private InputStream input;
    private String uri;

//    private String request;

    public Request(InputStream input) {
        this.input = input;
    }

    public void parse() {
        StringBuilder request = new StringBuilder(2048);
        int i;
        byte[] buffer = new byte[2048];
        try {
            i = input.read(buffer);
        }
        catch (IOException e) {
            e.printStackTrace();
            i = -1;
        }
        for (int j=0; j index1)
                return requestString.substring(index1 + 1, index2);
        }
        return null;
    }

    public String getUri() {
        return uri;
    }

    @Override
    public Object getAttribute(String name) {
        return null;
    }

    @Override
    public Enumeration getAttributeNames() {
        return null;
    }

    @Override
    public String getCharacterEncoding() {
        return null;
    }
    //其他方法省略...
 
}
2. 新Response類

同新Request類一樣,新Response類也保留了原來(lái)的方法只是實(shí)現(xiàn)了ServletResponse接口,除了getWriter()方法因?yàn)樯院笠枚鴮?shí)現(xiàn)了,其他ServletResponse接口方法均返回null或留白。

/**
 * 表示返回值
 */
public class Response implements ServletResponse {
    private static final int BUFFER_SIZE = 1024;
    private Request request;
    private OutputStream output;

    public Response(OutputStream output) {
        this.output = output;
    }

    public void setRequest(Request request) {
        this.request = request;
    }

    public void sendStaticResource() throws IOException {
        byte[] bytes = new byte[BUFFER_SIZE];
        //讀取訪問(wèn)地址請(qǐng)求的文件
        File file = new File(Constants.WEB_ROOT, request.getUri());
        try (FileInputStream fis = new FileInputStream(file)){
            if (file.exists()) {
                //如果文件存在
                //添加相應(yīng)頭。
                StringBuilder heads=new StringBuilder("HTTP/1.1 200 OK
");
                heads.append("Content-Type: text/html
");
                //頭部
                StringBuilder body=new StringBuilder();
                //讀取響應(yīng)主體
                int len ;
                while ((len=fis.read(bytes, 0, BUFFER_SIZE)) != -1) {
                    body.append(new String(bytes,0,len));
                }
                //添加Content-Length
                heads.append(String.format("Content-Length: %d
",body.toString().getBytes().length));
                heads.append("
");
                output.write(heads.toString().getBytes());
                output.write(body.toString().getBytes());
            } else {
                response404(output);
            }
        }catch (FileNotFoundException e){
            response404(output);
        }
    }


    private void response404(OutputStream output) throws IOException {
        StringBuilder response=new StringBuilder();
        response.append("HTTP/1.1 404 File Not Found
");
        response.append("Content-Type: text/html
");
        response.append("Content-Length: 23
");
        response.append("
");
        response.append("

File Not Found

"); output.write(response.toString().getBytes()); } @Override public PrintWriter getWriter() throws IOException { return new PrintWriter(output,true); } @Override public String getCharacterEncoding() { return null; } //省略其他方法。 }

這里需要注意是new PrintWriter(output,true)方法,閱讀方法注釋,摘錄如下內(nèi)容:

autoFlush – A boolean; if true, the println, printf, or format methods will flush the output buffer

也就是說(shuō)調(diào)用print方法不會(huì)輸出到瀏覽器頁(yè)面。原書(shū)中說(shuō)這是一個(gè)問(wèn)題需要解決。

我又閱讀了Servlet API文檔getWriter()相關(guān)內(nèi)容(傳送門(mén)),摘錄如下內(nèi)容:

Returns a PrintWriter object that can send character text to the client. The PrintWriter uses the character encoding returned by getCharacterEncoding(). If the response"s character encoding has not been specified as described in getCharacterEncoding (i.e., the method just returns the default value ISO-8859-1), getWriter updates it to ISO-8859-1.

Calling flush() on the PrintWriter commits the response.

我理解此方法返回的PrintWriter是需要調(diào)用flush()才會(huì)刷新,所以我對(duì)所有的打印方法println();printf();print()等是否需要每次都自動(dòng)刷新產(chǎn)生了疑惑,姑且先到這,看書(shū)中后面的處理能否能答疑解惑。

我們只是實(shí)現(xiàn)一個(gè)簡(jiǎn)單的Servlet容器示例,所以ServletRequestServletResponse其他方法留待以后實(shí)現(xiàn)。

2. 運(yùn)行用戶的Servlet

上篇文章我們直接讀取靜態(tài)Html文件,然后將內(nèi)容直接返回給瀏覽器,其實(shí)處理Servlet也差不多,只不過(guò)我們面對(duì)的class文件,我們需要利用ClassLoader將類加載進(jìn)虛擬機(jī),然后利用反射原理生成Servlet類的對(duì)象,然后就可以調(diào)用相應(yīng)service()方法,運(yùn)行編寫(xiě)Servlet類程序員的代碼了。

1. 處理Servlet的方法
/**
 * Servlet的處理類
 */
public class ServletProcessor {

    /**
     * Servlet處理方法。
     *
     * @param request
     * @param response
     */
    public void process(Request request, Response response) {
        //解析Servlet類名
        String uri = request.getUri();
        String servletName = uri.substring(uri.lastIndexOf("/") + 1);
        URLClassLoader loader = null;

        try {
            // create a URLClassLoader
            URL[] urls = new URL[1];
            URLStreamHandler streamHandler = null;
            File classPath = new File(Constants.WEB_ROOT);
            //類加載器加載路徑
            String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString() ;
            urls[0] = new URL(null, repository, streamHandler);
            loader = new URLClassLoader(urls);
        }
        catch (IOException e) {
            throw new IllegalStateException(e);
        }
        Class clazz = null;
        try {
            //加載Servlet類
            clazz = loader.loadClass(servletName);
        }
        catch (ClassNotFoundException e) {
            throw new IllegalStateException(e);
        }
        try {
            //初始化Servlet類
            Servlet servlet = (Servlet) clazz.newInstance();
            //寫(xiě)入響應(yīng)頭部,否則瀏覽器無(wú)法解析。
            PrintWriter writer=response.getWriter();
            writer.print("HTTP/1.1 200 OK
");
            writer.print("Content-Type: text/html
");
            writer.print("
");
            //print方法不會(huì)自動(dòng)刷新。
            writer.flush();
            //調(diào)用Servlet類中service方法。
            servlet.service(request,response);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}    

注意這這三行代碼,書(shū)中原始代碼沒(méi)有相應(yīng)邏輯。

 writer.print("HTTP/1.1 200 OK
");
 writer.print("Content-Type: text/html
");
 writer.print("
");

和上篇文章一樣,也需要加響應(yīng)頭部,否則瀏覽器無(wú)法解析,不過(guò)這個(gè)添加頭部的方法十分不簡(jiǎn)陋,以后我們會(huì)優(yōu)雅的實(shí)現(xiàn)。

2. 有沒(méi)有發(fā)現(xiàn)“壞味道”

注意這行代碼:servlet.service(request,response);我們將Request類和Response類直接傳入了service方法,如果熟悉這個(gè)容器的程序員就可以在自己的Servlet使用這兩個(gè)內(nèi)部類和他的方法。

public class HelloWorldServlet implements Servlet {
    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        Request request=(Request)req;
        Response response=(Response)res;
        request.parse();
    }
}

parse()方法并不是ServletRequest接口方法,我們不想暴露給程序員,但也不能parse()改成private因?yàn)槿萜髦衅渌愐残枰褂谩?br>Tomcat用了一個(gè)非常巧妙的外觀模式(Facade)解決了這個(gè)問(wèn)題。

3. RequestResponse的外觀模式

既然是因?yàn)?b>Request和Response向上轉(zhuǎn)換類型后傳輸出現(xiàn)了問(wèn)題,我們就從這兩個(gè)類入手改造,引入RequestFacadeResponseFacade兩個(gè)類,這兩個(gè)類和RequestResponse一樣需要實(shí)現(xiàn)ServletRequestServletResponse接口。

- RequestFacade類

【圖二】

public class RequestFacade implements ServletRequest {

  private ServletRequest request = null;

  public RequestFacade(Request request) {
    this.request = request;
  }

  //實(shí)現(xiàn)ServletRequest中方法
  public Object getAttribute(String attribute) {
    return request.getAttribute(attribute);
  }

  public Enumeration getAttributeNames() {
    return request.getAttributeNames();
  }

  public String getRealPath(String path) {
    return request.getRealPath(path);
  }
  //其他方法省略...

- ResponseFacade類

【圖三】

public class ResponseFacade implements ServletResponse {

  private ServletResponse response;
  public ResponseFacade(Response response) {
    this.response = response;
  }
  //實(shí)現(xiàn)ServletResponse 中方法
  public void flushBuffer() throws IOException {
    response.flushBuffer();
  }

  public int getBufferSize() {
    return response.getBufferSize();
  }

  public String getCharacterEncoding() {
    return response.getCharacterEncoding();
  }
  //其他方法省略...

}

通過(guò)觀察兩個(gè)外觀類,其實(shí)他們什么也沒(méi)有做,所有的接口實(shí)現(xiàn)方法都是調(diào)用內(nèi)部的ServletRequestServletResponse的具體實(shí)現(xiàn)類來(lái)處理的。我們可以這樣改造我們上面ServletProcessor類中的代碼

RequestFacade requestFacade = new RequestFacade(request);
ResponseFacade responseFacade = new ResponseFacade(response);
servlet.service( requestFacade, responseFacade);

傳入Servlet實(shí)現(xiàn)類中service方法的參數(shù)變成了RequestFacadeResponseFacade類型,程序員就不能再代碼中使用類型轉(zhuǎn)換轉(zhuǎn)換為RequestResponse類型,所以RequestFacadeResponseFacade避免了原來(lái)RequestResponse類不希望對(duì)外可見(jiàn)的方法的暴露。

注:
1.其實(shí)從RequestFacadeResponseFacade實(shí)現(xiàn)和類圖上更像是代理模式,但是此處使用場(chǎng)景確實(shí)起到了對(duì)外提供統(tǒng)一接口的作用,所以從功能上講,叫外觀模式也無(wú)可或非。
2.即使采用了外觀類,程序員依然可以在Servlet中使用反射獲取到外觀類中private屬性的內(nèi)部類型,但是和強(qiáng)制轉(zhuǎn)型相同,程序員應(yīng)該按照Servlet協(xié)議編寫(xiě)程序,否則除非清楚自己目的,不然我想不到這樣做的意義。

3. 處理瀏覽器請(qǐng)求
public class HttpServer {
    private static final String SHUTDOWN_COMMAND = "shutdown";
    private boolean shutdown = false;

    public static void main(String[] args) {
        HttpServer httpServer=new HttpServer();
        httpServer.await();
    }

    public void await() {
        try (ServerSocket serverSocket = new ServerSocket(8080)) {
            serverProcess(serverSocket);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void serverProcess(ServerSocket serverSocket) {
        while (!shutdown) {
            try (Socket socket = serverSocket.accept()) {
//                System.out.println(socket.hashCode());
                InputStream input = socket.getInputStream();
                OutputStream output = socket.getOutputStream();
                //創(chuàng)建Request對(duì)象
                Request request = new Request(input);
                request.parse();
                //創(chuàng)建Response對(duì)象
                Response response = new Response(output);
                response.setRequest(request);
                if (request.getUri().startsWith("/servlet/")) {
                    //如果地址以/servlet開(kāi)頭就作為Servlet處理
                    ServletProcessor processor = new ServletProcessor();
                    processor.process(request, response);
                }else {
                    //否則作為靜態(tài)資源使用
                    StaticResourceProcessor processor = new StaticResourceProcessor();
                    processor.process(request, response);
                }
                shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
            } catch (IOException e) {
                e.printStackTrace();
            } catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}

和上篇文章中處理用戶請(qǐng)求類似,我們保留了處理處理靜態(tài)資源的能力(StaticResourceProcessor具體實(shí)現(xiàn)見(jiàn)源碼),又增加了處理Servlet的功能。

4. 運(yùn)行一下 1. 實(shí)現(xiàn)HelloWorldServlet

首先編寫(xiě)一個(gè)自己的Servlet實(shí)現(xiàn)類。

public class HelloWorldServlet implements Servlet {
    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        res.getWriter().println("

Hello World!

"); } //其他方法省略 }

注意,這個(gè)HelloWorldServlet不在任何package下,因?yàn)榧虞d的時(shí)候就是用請(qǐng)求地址攜帶的類名加載,如果添加了包名,反射的時(shí)候會(huì)加載失敗,以后我們會(huì)修復(fù)這個(gè)問(wèn)題。

編譯這個(gè)類,將編譯好的class文件放入D:webRoot文件夾(代碼中定義的路徑)。

2. 用瀏覽器發(fā)送請(qǐng)求

在瀏覽器地址欄輸入http://localhost:8080/servlet/HelloWorldServlet,瀏覽器會(huì)打印出Hello World!。

后記

至此我們實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的Servlet容器,雖然我們的功能非常簡(jiǎn)陋,但是通過(guò)兩篇文章的講解,大家應(yīng)該能理解一個(gè)瀏覽器請(qǐng)求是如何經(jīng)過(guò)服務(wù)器處理最終返回可以顯示頁(yè)面的大致流程。是不是很有成就感,簡(jiǎn)單的幾行代碼就能演示我們?nèi)粘J褂玫腡omcat服務(wù)器的基本功能。不過(guò)我們只看到了冰山一角,今后的文章會(huì)逐步一覽全貌。

源碼

文中源碼地址:https://github.com/TmTse/tiny...

參考

《深入剖析Tomcat》

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/77016.html

相關(guān)文章

  • 步一實(shí)現(xiàn)Tomcat之一——實(shí)現(xiàn)一個(gè)簡(jiǎn)單Web服務(wù)器

    摘要:原書(shū)中主要內(nèi)容是一步一步實(shí)現(xiàn)一個(gè)類似于的容器。圖一協(xié)議處于協(xié)議棧的應(yīng)用層,傳遞的內(nèi)容是報(bào)文,報(bào)文就相當(dāng)于語(yǔ)言中的短語(yǔ)和句子用來(lái)表明意圖。類表示一次客戶端請(qǐng)求解析請(qǐng)求待實(shí)現(xiàn)解析待實(shí)現(xiàn)類表示返回值發(fā)送靜態(tài)頁(yè)面的相應(yīng)報(bào)文待實(shí)現(xiàn)。 前言 最近在讀《How Tomcat Works》,收獲頗豐,在編寫(xiě)書(shū)中示例的過(guò)程中也踩了不少坑。不知你有沒(méi)有體會(huì),編程就一門(mén)是不試不知道,一試嚇一跳的實(shí)踐藝術(shù)。所...

    yearsj 評(píng)論0 收藏0
  • Hello World -- Java Web版(Java Web 入門(mén)教程)

    摘要:在中運(yùn)行,輸出如下圖,則說(shuō)明安裝成功下載本文使用的是最新穩(wěn)定版并解壓到任意目錄。設(shè)置環(huán)境變量為解壓后的目錄,該目錄中應(yīng)包含以下文件。運(yùn)行打開(kāi)工具,依次運(yùn)行兩個(gè)命令的目錄注意將替換成具體的路徑。 在閱讀本文之前,你一定知道如何用Java語(yǔ)言寫(xiě)出Hello, World!了。那么,用Java語(yǔ)言如何寫(xiě)出Web版的Hello, World!,使之顯示在瀏覽器中呢?本文將一步一步演示如何寫(xiě)出J...

    james 評(píng)論0 收藏0
  • 使用 Docker 搭建簡(jiǎn)易 Java Web 環(huán)境 (二)

    摘要:創(chuàng)建一個(gè)環(huán)境最近公司正在使用開(kāi)發(fā)網(wǎng)站應(yīng)用,所以有必要了解下如何使用創(chuàng)建對(duì)應(yīng)的環(huán)境。還好,提供了文檔的形式來(lái)組合多個(gè)容器來(lái)搭建開(kāi)發(fā)環(huán)境。下一步我們將使用來(lái)構(gòu)建更加復(fù)雜的開(kāi)發(fā)環(huán)境。 showImg(https://segmentfault.com/img/remote/1460000011106825); 從《從最簡(jiǎn)單的入手學(xué)習(xí) Docker (一)》一文中,可以簡(jiǎn)單的了解 Docker ...

    Tamic 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<