乞丐版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容器第4篇
6. NIOConnector 现在为Server添加NIOConnector,添加之前可以发现我们的代码其实是有问题的.比如现在的代码是无法让服务器支持同时监听多个端口和IP的,如同时监听 127. ...
- 乞丐版servlet容器第3篇
4 EventListener接口 让我们继续看SocketConnector中的acceptConnect方法: @Override protected void acceptConnect() t ...
- 乞丐版servlet容器第2篇
2. 监听端口接收请求 上一步中我们已经定义好了Server接口,并进行了多次重构,但是实际上那个Server是没啥毛用的东西. 现在要为其添加真正有用的功能. 大师说了,饭要一口一口吃,衣服要一件一 ...
- 对Servlet容器的补充和一个问题的请教
[0]README 0.1)本文是对 一个servlet容器 的补充: 0.2)发这个博文的最终目的是为了请教各位前辈,帮我解决一个问题,问题描述在文末, 谢谢: [1]Servlet容器 1.1) ...
- 【串线篇】spring boot使用外置的Servlet容器
嵌入式Servlet容器:应用打成可执行的jar 优点:简单.便携: 缺点:默认不支持JSP.优化定制比较复杂 (使用定制器[ServerProperties/自定义EmbeddedServletCo ...
- 【串线篇】spring boot嵌入式Servlet容器自动配置原理
EmbeddedServletContainerAutoConfiguration:嵌入式的Servlet容器自动配置? @AutoConfigureOrder(Ordered.HIGHEST_PREC ...
- 【串线篇】spring boot嵌入式Servlet容器启动原理;
什么时候创建嵌入式的Servlet容器工厂?什么时候获取嵌入式的Servlet容器并启动Tomcat: 获取嵌入式的Servlet容器工厂: 1).SpringBoot应用启动运行run方法 2).r ...
- 【串线篇】spring boot配置嵌入式servlet容器
SpringBoot默认使用Tomcat作为嵌入式的Servlet容器 问题? 一.如何定制和修改Servlet容器的相关配置 1.方法1修改和server有关的配置(ServerProperties ...
- 深入剖析tomcat之一个简单的servlet容器
上一篇,我们讲解了如果开发一个简单的Http服务器,这一篇,我们扩展一下,让我们的服务器具备servlet的解析功能. 简单介绍下Servlet接口 如果我们想要自定义一个Servlet,那么我们必须 ...
随机推荐
- 使用C#的AssemblyResolve事件动态解析加载失败的程序集
我们知道反射是 依赖注入 模式的基础,依赖注入要求只在项目中引用定义接口的程序集,而不引用接口实现类的程序集,因为接口实现类的程序集应该是通过反射来动态加载的,这样才能保证接口与其实现类之间的松耦合. ...
- mac通过自带的ssh连接Linux服务器并上传解压文件
需求: 1:mac连接linux服务器 2:将mac上的文件上传到linux服务器指定位置 3:解压文件 mac上使用命令,推荐使用 iterm2 .当然,也可以使用mac自带的终端工具. 操作过程: ...
- Java 敏感词过滤,Java 敏感词替换,Java 敏感词工具类
Java 敏感词过滤,Java 敏感词替换,Java 敏感词工具类 =========================== ©Copyright 蕃薯耀 2017年9月25日 http://www ...
- oracle查看表空间下数据文件
下面两个数据字典视图就知道了.查看有哪些表空间:select * from dba_tablespaces;查看有哪些数据文件在哪个表空间中:select * from dba_data_files; ...
- python环境搭建--pycharm的安装及使用
学习网址: --菜鸟教程(2.0和3.0) http://www.runoob.com/python/python-tutorial.html http://www.runoob.com/python ...
- canvas实现倒计时效果示例(vue组件内编写)
前言: 此事例是在vue组件中,使用canvas实现倒计时动画的效果.其实,实现效果的逻辑跟vue没有关系,只要读懂canvas如何实现效果的这部分逻辑就可以了 canvas动画的原理:利用定时器,给 ...
- [one day one question] Vue数组变更不能触发刷新
问题描述:Vue数组变更不能触发刷新,特别是数组的每个元素都是对象的时候,对象中某个属性的值发生变化,根本无法触发Vue的dom刷新,这怎么破? 解决方案:this.$set(array, index ...
- what is yaml ?
what is yaml ? when I fist time meeting it is in java projects she as a system config file to my e ...
- 单KEY业务,数据库水平切分架构实践
本文将以"用户中心"为例,介绍"单KEY"类业务,随着数据量的逐步增大,数据库性能显著降低,数据库水平切分相关的架构实践: 如何来实施水平切分 水平切分后常见的 ...
- 洛谷P1233 [木棍加工]
主要思路: 这道题一眼看过去就可以贪心.. 首先可以按L排序.. 显然排序之后我们就可以抛开L不管了.. 然后就可以愉快的贪心了.. 细节: 这道题可以看成用 最少的合法序列(详见原题) 装下所有木棍 ...