乞丐版servlet容器第1篇
本系列參照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篇