1. 程式人生 > >乞丐版servlet容器第1篇

乞丐版servlet容器第1篇

編程 需要 stop 參數 nal 問題 工廠類 int 編寫

本系列參照pkpk1234大神的BeggarServletContainer,具體請訪問:https://github.com/pkpk1234/BeggarServletContainer。
一步一步從無到有寫一個servlet容器。
一開始不會涉及復雜的部分,中間會進行多次重構,直到完成復雜的功能。

1. Server接口編寫

Maven開發環境搭建好了,可以開始寫代碼了。
但是應該怎麽寫呢,完全沒頭緒。
還是從核心基本功能入手,Servlet容器,說白了就是一HTTP服務器嘛,能支持Servlet
別人要用你的服務器,總是需要啟動的,用完了,是需要關閉的。
那麽先定義一個Server,用來表示服務器,提供啟動和停止功能。

大師們總說面向接口編程,那麽先定義一個Server接口:

public interface Server {
    /**
    * 啟動服務器
    */
    void start();

    /**
    * 關閉服務器
    */
    void stop();
}

大師們還說,要多測試,所以再添加一個單元測試類。但是現在只有接口,沒實現,沒法測,沒關系,先寫個輸出字符串到標準輸出的實現再說。

public class SimpleServer implements Server {
    @Override
    public void start() {
        System.out.println("Server start");
    }

    @Override
    public void stop() {
        System.out.println("Server stop");
    }
}

有了這個實現,就可以寫出單元測試了。

public class TestServer {
    private static final Server SERVER = new SimpleServer();

    @Test
    public void testServerStart() {
        SERVER.start();
    }

    @Test
    public void testServerStop() {
        SERVER.stop();
    }
}

先不管這個SimpleServer啥用沒有,看看上面的單元測試,裏面出現了具體實現SimpleServer,大師很不高興,如果編寫了ComplicatedServer,這裏代碼豈不是要改。重構一下,添加一個工廠類。

public class ServerFactory {
    /**
    * 返回Server實例
    * @return
    */
    public static Server getServer() {
        return new SimpleServer();
    }
}

單元測試重構後如下:這樣就將接口和具體實現隔離開了,代碼更加靈活。

public class TestServer {
    private static final Server SERVER = ServerFactory.getServer();

    @BeforeClass

    @Test
    public void testServerStart() {
        SERVER.start();
    }

    @Test
    public void testServerStop() {
        SERVER.stop();
    }
}

再看單元測試,沒法寫assert斷言啊,難道要用客戶端請求下才知道Server的啟停狀態?Server要自己提供狀態查詢接口。
重構Server,添加getStatus接口,返回Server狀態,狀態應是枚舉,暫定STARTED、STOPED兩種,只有調用了start方法後,狀態才會變為STARTED。
Server重構後如下:

public class SimpleServer implements Server {
    private ServerStatus serverStatus = ServerStatus.STOPED;

    @Override
    public void start() {
        this.serverStatus = ServerStatus.STARTED;
        System.out.println("Server start");
    }

    @Override
    public void stop() {
        this.serverStatus = ServerStatus.STOPED;
        System.out.println("Server stop");
    }

    @Override
    public ServerStatus getStatus() {
        return serverStatus;
    }
}

再為單元測試添加斷言:

public class TestServer {
    private static final Server SERVER = ServerFactory.getServer();

    @Test
    public void testServerStart() {
        SERVER.start();
        assertTrue("服務器啟動後,狀態是STARTED",SERVER.getStatus().equals(ServerStatus.STARTED));
    }

    @Test
    public void testServerStop() {
        SERVER.stop();
        assertTrue("服務器關閉後,狀態是STOPED",SERVER.getStatus().equals(ServerStatus.STOPED));
    }
}

再繼續看Server接口,要接受客戶端的請求,需要監聽本地端口,端口應該作為構造參數傳入,並且Server應該具有默認的端口。再繼續重構。

public class SimpleServer implements Server {

    private ServerStatus serverStatus = ServerStatus.STOPED;
    public final int DEFAULT_PORT = 18080;
    private final int PORT;

    public SimpleServer(int PORT) {
        this.PORT = PORT;
    }

    public SimpleServer() {
        this.PORT = DEFAULT_PORT;
    }

    @Override
    public void start() {
        this.serverStatus = ServerStatus.STARTED;
        System.out.println("Server start");
    }

    @Override
    public void stop() {
        this.serverStatus = ServerStatus.STOPED;
        System.out.println("Server stop");
    }

    @Override
    public ServerStatus getStatus() {
        return serverStatus;
    }

    public int getPORT() {
        return PORT;
    }
}

問題又來了,ServerFactory沒法傳端口,最簡單的方法是修改ServerFactory.getServer()方法,增加一個端口參數。但是以後要為Server指定管理端口怎麽辦,又加參數?大師說NO,用配置類,為配置類加屬性就行了。

public class ServerConfig {

    public static final int DEFAULT_PORT = 18080;
    private final int port;

    public ServerConfig(int PORT) {
        this.port = PORT;
    }

    public ServerConfig() {
        this.port = DEFAULT_PORT;
    }

    public int getPort() {
        return port;
    }
}

Server重構,修改構造函數

public class SimpleServer implements Server {

    private ServerStatus serverStatus = ServerStatus.STOPED;
    private final int port;

    public SimpleServer(ServerConfig serverConfig) {
        this.port = serverConfig.getPort();
    }

    @Override
    public void start() {
        this.serverStatus = ServerStatus.STARTED;
        System.out.println("Server start");
    }

    @Override
    public void stop() {
        this.serverStatus = ServerStatus.STOPED;
        System.out.println("Server stop");
    }

    @Override
    public ServerStatus getStatus() {
        return serverStatus;
    }

    @Override
    public int getPort() {
        return port;
    }
}

ServerFactory重構

public class ServerFactory {
    /**
    * 返回Server實例
    * @return
    */
    public static Server getServer(ServerConfig serverConfig) {
        return new SimpleServer(serverConfig);
    }
}

單元測試重構

public class TestServer {
    private static Server server;

    @BeforeClass
    public static void init() {
        ServerConfig serverConfig = new ServerConfig();
        server = ServerFactory.getServer(serverConfig);
    }

    @Test
    public void testServerStart() {
        server.start();
        assertTrue("服務器啟動後,狀態是STARTED", server.getStatus().equals(ServerStatus.STARTED));
    }

    @Test
    public void testServerStop() {
        server.stop();
        assertTrue("服務器關閉後,狀態是STOPED", server.getStatus().equals(ServerStatus.STOPED));
    }

    @Test
    public void testServerPort() {
        int port = server.getPort();
        assertTrue("默認端口號", ServerConfig.DEFAULT_PORT == port);
    }
}

跑下測試:
OK,經過多輪重構,Server接口編寫暫時完成。
下一步開始實現真正有用的功能。

乞丐版servlet容器第1篇