本系列参照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篇的更多相关文章

  1. 乞丐版servlet容器第4篇

    6. NIOConnector 现在为Server添加NIOConnector,添加之前可以发现我们的代码其实是有问题的.比如现在的代码是无法让服务器支持同时监听多个端口和IP的,如同时监听 127. ...

  2. 乞丐版servlet容器第3篇

    4 EventListener接口 让我们继续看SocketConnector中的acceptConnect方法: @Override protected void acceptConnect() t ...

  3. 乞丐版servlet容器第2篇

    2. 监听端口接收请求 上一步中我们已经定义好了Server接口,并进行了多次重构,但是实际上那个Server是没啥毛用的东西. 现在要为其添加真正有用的功能. 大师说了,饭要一口一口吃,衣服要一件一 ...

  4. 对Servlet容器的补充和一个问题的请教

    [0]README 0.1)本文是对 一个servlet容器  的补充: 0.2)发这个博文的最终目的是为了请教各位前辈,帮我解决一个问题,问题描述在文末, 谢谢: [1]Servlet容器 1.1) ...

  5. 【串线篇】spring boot使用外置的Servlet容器

    嵌入式Servlet容器:应用打成可执行的jar 优点:简单.便携: 缺点:默认不支持JSP.优化定制比较复杂 (使用定制器[ServerProperties/自定义EmbeddedServletCo ...

  6. 【串线篇】spring boot嵌入式Servlet容器自动配置原理

    EmbeddedServletContainerAutoConfiguration:嵌入式的Servlet容器自动配置? @AutoConfigureOrder(Ordered.HIGHEST_PREC ...

  7. 【串线篇】spring boot嵌入式Servlet容器启动原理;

    什么时候创建嵌入式的Servlet容器工厂?什么时候获取嵌入式的Servlet容器并启动Tomcat: 获取嵌入式的Servlet容器工厂: 1).SpringBoot应用启动运行run方法 2).r ...

  8. 【串线篇】spring boot配置嵌入式servlet容器

    SpringBoot默认使用Tomcat作为嵌入式的Servlet容器 问题? 一.如何定制和修改Servlet容器的相关配置 1.方法1修改和server有关的配置(ServerProperties ...

  9. 深入剖析tomcat之一个简单的servlet容器

    上一篇,我们讲解了如果开发一个简单的Http服务器,这一篇,我们扩展一下,让我们的服务器具备servlet的解析功能. 简单介绍下Servlet接口 如果我们想要自定义一个Servlet,那么我们必须 ...

随机推荐

  1. SDP(1):ScalikeJDBC-基本操作介绍

    简单来说:JDBC是一种开放标准的跨编程语言.跨数据库类型编程API.各类型数据库产品厂商都会按它的标准要求来提供针对自身产品的JDBC驱动程序.最主要的这是一套成熟的工具,在编程人员中使用很普及.既 ...

  2. ItemCF_基于物品的协同过滤_MapReduceJava代码实现思路

    ItemCF_基于物品的协同过滤 1.    概念 2.    原理 如何给用户推荐? 给用户推荐他没有买过的物品--103 3.    java代码实现思路 数据集: 第一步:构建物品的同现矩阵 第 ...

  3. 关于keil5使用注意事项(预定义、路径包含)

    2017.12.17  下午 终于在不用keil5自带添加库的基础上0 error的新建了一个工程.磕磕绊绊搞了快一个下午,各种小问题搞到崩溃. 首先是库文件添加路径的问题 ,只要是你工程中用到的头文 ...

  4. VN问题:error:请求的名称有效,但是找不到请求的类型的

    把url中的jmsjms-pc换成IP地址试试看 IP地址你用的是外网地址,应该用局域网内网地址,改成内网地址再试试看 还有练习架设SVN服务器初期尽量用http协议,不要上来就用https协议,ht ...

  5. 关于Vue的路由、脚手架笔记

    在页面引入vue-router.js文件,开始配置路由 <div id="box"> <ul><li> <a v-link="{ ...

  6. 解决导入MySQL数据库提示"Unknown character set: 'utf8mb4'"错误

    今天老左在准备迁移公司一个客户的网站到另外一台服务器中,根据正常的操作备份最新的网页文件和导出数据库,然后在新服务器中创建站点和数据库wget迁移进去解压.因为数据库比较小,所以直接用PHPMyAdm ...

  7. 谁能教我iCloud怎么用?

    iCloud是苹果公司所提供的云端服务,使用者可以免费储存5GB的资料.你已经开始使用IOS5,并且你很兴奋的着手于将它同步至云服务层.以下就是怎样让你的设备更新至云服务层的非常简单的步骤.在你的iO ...

  8. 【开发技术】refactor 重构----实现文件改名

    当我们要改类名或接口名时,可能会遇到该类(接口)在其它文件中也有使用的情况,如一个个找比较麻烦也容易漏,这里推荐使用右键refactor->rename进行修改.

  9. eclipes快捷键

    本文原创作者:pipi-changing 本文原创出处:http://www.cnblogs.com/pipi-changing/ Ctrl+1 快速修复(最经典的快捷键,就不用多说了) Ctrl+D ...

  10. Maven初步

    Maven初入 maven 是一个项目管理工具, 它包含了一个 项目对象模型(Project Object Model POM), 一组标准集合, 一个项目生命周期(Project Lifecycle ...