乞丐版servlet容器第1篇

Posted 放着,让我来!!

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了乞丐版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篇的主要内容,如果未能解决你的问题,请参考以下文章

乞丐版servlet容器第3篇

C#实现乞丐版IOC容器

乞丐版JAVA扫雷

Word2Vec的PyTorch实现(乞丐版)

Java实现QQ微信轰炸机1.2(斗图乞丐版)

通学智能合约系列(二十二)--乞丐版众筹项目实践