如何从头开始以正确的面向对象方式创建Java Web Framework

您如何用Java设计Web应用程序? 您安装了Spring,阅读了手册,创建了控制器 ,创建了一些视图,添加了一些注释 ,它就可以工作了。 如果没有Spring (Ruby中没有Ruby on Rails,PHP中没有Symphony,也没有…等),您将怎么办? 让我们尝试从头开始创建一个Web应用程序,从一个纯Java SDK到一个功能齐全的Web应用程序(由单元测试覆盖)结束。 几周前,我录制了第42号网络研讨会 ,但本文应该对此进行更详细的说明。


蒂芙尼早餐(Blake Edwards,1961年)

首先,我们必须创建一个HTTP服务器,该服务器将打开服务器套接字,侦听传入的连接,读取他们必须说的所有内容(HTTP请求)并返回任何Web浏览器想要的信息(HTTP响应)。 您知道HTTP的工作原理吧? 如果您不这样做,这里有个简短的提醒:

Web浏览器向服务器发送请求,该请求看起来像这样(这是纯文本数据):

GET /index.html HTTP/1.1
Host: www.example.com

服务器必须阅读此文本,准备答案(必须是浏览器可读HTML页面),然后像这样返回:

HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Content-Length: 26<html>Hello, world!</html>

而已。 这是一个非常简单的原始协议。 用Java实现Web服务器也不是那么复杂。 这是一个非常简单的形式:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.Arrays;
public class Main {public static void main(String... argv) {try (ServerSocket server = new ServerSocket(8080)) {server.setSoTimeout(1000);while (true) {try (Socket socket = server.accept()) {try (InputStream input = socket.getInputStream();OutputStream output = socket.getOutputStream()) {byte[] buffer = new byte[10000];int total = input.read(buffer);String request = new String(Arrays.copyOfRange(buffer, 0, total));String response = "HTTP/1.1 200 OK\r\n\r\nHello, world!";output.write(response.getBytes());}} catch (SocketTimeoutException ex) {if (Thread.currentThread().isInterrupted()) {break;}}}}}
}

尝试运行它,它应该可以工作。 您应该能够在浏览器中打开http://localhost:8080页面,然后看到Hello, world!Hello, world! 文本。

它还不是Web应用程序,而是一个框架,它可以将HTTP请求简单地分配到HTTP响应中。 尽管其中没有严重的面向对象的问题。 这是相当程序化的方法,但确实可行。 现在,我们应该关注一个更重要的问题:如何为Web应用程序添加更多功能,并使其能够处理不同的页面,呈现更大的内容并处理错误? 上面代码段中的request变量应该以某种方式转换为response

最简单的方法是1)将请求转换为内部包含所有详细信息的DTO ,然后2)将其发送到知道如何处理DTO数据的“控制器”,然后3)接收响应DTO从控制器中取出数据并呈现响应。 这就是春天和 所有其他框架都可以做到。 但是,我们不会走这条路,我们将尝试做到无DTO且纯粹面向对象。

我不得不说,可能有多种设计,全部都是OOP风格。 现在,我仅向您显示这些选项之一。 您无疑会知道我们几年前诞生的Takes框架-它具有自己的设计,也面向对象。 但是我现在建议的那个似乎更好。 您可能还会提出其他建议,因此不要犹豫,在下面的评论中发表您的想法,甚至创建GitHub存储库并在那里分享您的想法。

我建议我们引入两个接口: ResourceOutputResource是服务器端实体,它根据传入的请求参数而发生变化。例如,当我们只知道请求是GET / ,它就是一种资源。 但是,如果我们也知道该请求具有例如Accept: text/plain ,则可以更改该请求并创建一个新请求,该请求将传递纯文本。 这是界面:

interface Resource {Resource refine(String name, String value);
}

这是我们创建和变异的方法:

Resource r = new DefaultResource().refine("X-Method", "GET").refine("X-Query", "/").refine("Accept", "text/plain");

注意:每次调用.refine()返回一个新的接口Resource实例。 它们都是不可变的,就像对象必须是一样 。 由于这种设计,我们不会将数据与处理器分开。 资源是数据和处理器。 每个资源都知道如何处理数据,并且仅接收应该接收的数据。 从技术上讲,我们只是以面向对象的方式实现请求调度

然后,我们需要将资源转换为响应。 我们赋予资源使其能够响应的能力。 我们不希望数据以某种DTO的形式泄漏资源。 我们希望该资源打印响应。 如何为资源提供其他方法print()

interface Resource {Resource refine(String name, String value);void print(Output output);
}

然后,接口Output看起来像这样:

interface Output {void print(String name, String value);
}

这是Output的原始实现:

public class StringBuilderOutput implements Output {private final StringBuilder buffer;StringBuilderOutput(StringBuilder buf) {this.buffer = buf;}@Overridepublic void print(String name, String value) {if (this.buffer.length() == 0) {this.buffer.append("HTTP/1.1 200 OK\r\n");}if (name.equals("X-Body")) {this.buffer.append("\r\n").append(value);} else {this.buffer.append(name).append(": ").append(value).append("\r\n");}}
}

要构建HTTP响应,我们可以这样做:

StringBuilder builder = new StringBuilder();
Output output = new StringBuilderOutput(builder);
output.print("Content-Type", "text/plain");
output.print("Content-Length", "13");
output.print("X-Body", "Hello, world!");
System.out.println(builder.toString());

现在,让我们创建一个类,该类使用Resource的实例作为调度程序 ,以接收传入的请求String并生成响应String

public class Session {private final Resource resource;Session(Resource res) {this.resource = res;}String response(String request) throws IOException {Map<String, String> pairs = new HashMap<>();String[] lines = request.split("\r\n");for (int idx = 1; idx < lines.length; ++idx) {String[] parts = lines[idx].split(":");pairs.put(parts[0].trim(), parts[1].trim());if (lines[idx].empty()) {break;}}String[] parts = lines[0].split(" ");pairs.put("X-Method", parts[0]);pairs.put("X-Query", parts[1]);pairs.put("X-Protocol", parts[2]);App.Resource res = this.resource;for (Map.Entry<String, String> pair : pairs.entrySet()) {res = res.refine(pair.getKey(), pair.getValue());}StringBuilder buf = new StringBuilder();res.print(new StringBuilderOutput(buf));return buf.toString();}
}

首先,我们解析请求,将其标头分成几行,并忽略请求的主体。 您可以使用X-Body作为键,修改代码以解析主体并将其传递给refine()方法。 目前,上面的代码无法做到这一点。 但是你明白了。 片段的解析部分准备了可以在请求中找到的对,并将它们一对一地传递给封装的资源,对其进行变异直到最终形式。 始终返回文本的简单资源可能如下所示:

class TextResource implements Resource {private final String body;public TextResource(String text) {this.body = text;}@Overridepublic Resource refine(String name, String value) {return this;}@Overridepublic void print(Output output) {output.print("Content-Type", "text/plain");output.print("Content-Length", Integer.toString(this.body.length()));output.print("X-Body", this.body);}
}

根据查询的路径,关注查询字符串并将请求分派给其他资源的资源可能看起来像这样:

new Resource() {@Overridepublic Resource refine(String name, String value) {if (name.equals("X-Query")) {if (value.equals("/")) {return new TextResource("Hello, world!");} else if (value.equals("/balance")) {return new TextResource("256");} else if (value.equals("/id")) {return new TextResource("yegor");} else {return new TextResource("Not found!");}} else {return this;}}@Overridepublic void print(final Output output) {throws IllegalStateException("This shouldn't happen");}
}

我希望你有主意。 上面的代码很粗略,并且大多数用例都没有实现,但是如果您感兴趣,可以自己做。 该代码位于yegor256 / jpages存储库中。 请毫不犹豫地为请求请求做出贡献,并使这个小型框架成为现实。

翻译自: https://www.javacodegeeks.com/2019/03/how-to-create-a-java-web-framework-from-scratch-the-right-object-oriented-way.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/342597.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

工业级光纤收发器使用“避坑”指南

工业级光纤收发器在使用中有很多的注意事项&#xff0c;往往这些注意事项经常被忽略。下面飞畅科技就整理了一些工业级光纤收发器使用“避坑”指南&#xff0c;大家可以仔细看看&#xff0c;引起重视。 使用工业级光纤收发器连接不同的设备时&#xff0c;必须注意使用的端口不…

物联网安全有哪些注意事项

然而&#xff0c;将网络边缘推向物联网及其他领域&#xff0c;通过显着增加犯罪分子可以利用的攻击媒介的数量和种类来侵入重要系统&#xff0c;从而增加了安全威胁- 它扩大了攻击面。最高奖项是存储系统中“静止”的数据以及跨网络和处理资源“运动”的数据&#xff0c;更不用…

【渝粤教育】国家开放大学2019年春季 0320-22T学前教育学 参考试题

科目编号&#xff1a;0320 座位号 2018-2019学年度第二学期期末考试 学前教育学 试题 2019年 7月 单选题&#xff08;本大题共7小题&#xff0c;每小题4分&#xff0c;共计28分&#xff09; 1.学前教育的基本内容是&#xff08; &#xff09;。 A.游戏活动 B.保育和教育活动 …

【渝粤教育】国家开放大学2018年春季 3912T★汽车底盘构造与维修 参考试题

试卷代号&#xff1a;3912 &#xff08;中央广播电视大学&#xff09;2018年春季学期“开放专科”期末考试 汽车底盘构造与维修试题 2018年7月 一、单项选择题&#xff08;下列各题的备选答案中&#xff0c;只有一个选项是正确的&#xff0c;请把正确答案的序号填写在括号内。1…

通信距离与哪些因素相关?为什么模块通信距离和厂家宣传的不一样?

现如今很多无线模块厂家都会提供一些样品给客户测试使用&#xff0c;但有些客户拿回去测试出来的距离并不理想&#xff0c;远远没有达到厂商测试的距离&#xff0c;这是为什么呢&#xff1f;下面本文就介绍一下在实际应用中影响无线模块通讯距离的因素以及如何确定实际距离。 其…

【渝粤教育】国家开放大学2018年秋季 1021t劳动与社会保障法 参考试题

试卷代号&#xff1a;1043 国民经济核算 试题&#xff08;开卷&#xff09; 2019年1月 一、单项选择题&#xff08;以下每小题各有四项备选答案&#xff0c;其中只有一项是正确的。每小题2分&#xff0c;共20分&#xff09; 1&#xff0e;机构单位可以分为两类( )。 A&#xf…

工业级光纤收发器一般能正常使用多长时间?

无论是厂商还是购买商在生产、选购工业级光纤收发器的时候&#xff0c;重要的参考指标就是它的使用寿命。那么&#xff0c;工业级光纤收发器的正常使用寿命大概有多长时间呢&#xff1f;接下来飞畅科技就来和大家简单聊一聊工业级光纤收发器的使用寿命&#xff0c;一起来看看吧…

2020-09-22

射频常用单位术语简介 1、dBm和dBW dBm是一个考征功率绝对值的值&#xff0c;是以1mW的功率做为参考&#xff0c;直接读作dBm或者dB毫 计算公式:10lg&#xff08;毫瓦标示的实际功率/1mW&#xff09;, 例如&#xff1a;0dBm1mW&#xff0c;10dBm10mW&#xff0c;17dBm50mW&…

【渝粤教育】国家开放大学2018年秋季 1049t金融法规 参考试题

试卷代号&#xff1a;1067 知识产权法 试题 2019年1月 一、单项选择题&#xff08;每小题2分&#xff0c;共20分&#xff09; 1&#xff0e;知识产权只在授予其权利的国家或者确认其权利的国家产生&#xff0c;并且只能在该国范围内发生法律效力受法律保护&#xff0c;这是知识…

红帽 jboss_红帽正式宣布发布JBoss BPM Suite 6和JBoss BRMS 6

红帽 jboss红帽公司刚刚宣布了这些期待已久的产品的全面上市 &#xff01; 要花费大量精力将社区代码转换为企业质量的软件&#xff0c;客户和最终用户可以在Red Hat支持的生产环境中使用这些代码。 现在是现在和潜在客户了解该产品&#xff0c;让合作伙伴开始使用它并学习基本…

关于延长物联网设备的生命周期

Telit IoT Platforms 首席信息安全官兼产品管理总监 Mihai Voicu 博士说&#xff0c;物联网在另一个关键方面不同于传统网络&#xff1a;它的设备预计具有更长的使用寿命&#xff0c;可能会延长超过十年或更长时间。这意味着他们将需要安全的双向端到端通信&#xff0c;并且能够…

工业交换机的几大“择机”标准,你学会了吗?

工业交换机现在是越来越普及了&#xff0c;很多相对较苛刻、复杂的工作环境基本都靠工业交换机来进行数据网络通信。但是&#xff0c;市场上形形色色的工业交换机非常多&#xff0c;如果企业安排你去采购一批交换机&#xff0c;你会选吗&#xff1f;就这个问题&#xff0c;下面…

Zigbee 联盟更名为连接标准联盟

新品牌标志着合作的标志&#xff0c;并为物联网行业创造信任标准 ZigBee联盟&#xff0c;数百家企业创建&#xff0c;维护&#xff0c;并为物联网&#xff08;IOT&#xff09;互联网提供开放的全球标准的组织&#xff0c;今天宣布其组织品牌重塑的连接标准联盟&#xff08;CSA …

程序员的前20个搜索和排序算法面试问题

大家好&#xff0c;如果您正在准备编程工作面试或正在寻找新工作&#xff0c;那么您知道这不是一个容易的过程。 在您职业的任何阶段&#xff0c;您都必须幸运地接到电话并进行第一轮面试&#xff0c;但是在初学者方面&#xff0c;当您寻找第一份工作时就更加困难。 这就是为什…

提高物联网安全性的简单方法

从产品开发到将设备插入家庭网络&#xff0c;物联网安全需要始终放在首位&#xff0c;以避免出现漏洞。 在物联网&#xff08;IOT&#xff09;的互联网是无处不在的&#xff0c;这是难以置信的&#xff0c;很方便。但随着这项技术的普及&#xff0c;风险敞口也越来越大。如今&a…

物联网应用领域-物联网智能安全始于产品开发

安全始于产品开发 为什么围绕物联网设备存在如此多的安全问题&#xff0c;我们可以采取哪些简单的步骤来提高这些设备的安全性&#xff1f;我相信这些问题的答案在于产品开发的各个阶段。 项目有许多阶段&#xff0c;根据所遵循的方法&#xff08;例如&#xff0c;瀑布式、螺旋…

工业交换机性能中的“自适应”该如何理解?

工业交换机诸多性能指标中&#xff0c;我们常常看见有“自适应”这个指标。它到底是什么意思呢&#xff1f;接下来飞畅科技就来给大家详细讲解一下&#xff0c;一起来看看吧&#xff01; 自适应也叫自动匹配、自协商&#xff0c;以太网技术发展到100M速率以后&#xff0c;出现…

【渝粤教育】国家开放大学2018年春季 7218-22T医学伦理学(本) 参考试题

编号&#xff1a;7218 座位号 2017-2018年第二学期期末考试 医学伦理学&#xff08;本&#xff09;试题 2018年7月 一、单选题&#xff08;每题只有一个正确答案&#xff0c;每题3分&#xff0c;共计30分&#xff09; 1.符合医学伦理学研究的是( ) A&#xff0e;研究人与人之间…

【无线lora模块星型组网】lora无线模块专利技术 跳频扩频 支持200节点并发

E70 (433NW30S)采用星型组网模块&#xff0c;工作在433MHz频段&#xff0c;模块集协调器、终端为一体&#xff0c;具有长距离、高速率两种传输模式&#xff0c;一个协调器支持多达200个节点与其通讯&#xff0c;所有操作配置采用行业标准AT指令&#xff0c;极大简化用户操作&am…

【渝粤教育】国家开放大学2018年秋季 0314-21T兽医基础 参考试题

试卷代码&#xff1a;0341 2018-2019学年度第一学期期末考试 高级英语听力&#xff08;2&#xff09;试题 2019年1月 注 意 事 项 一、将你的准考证号、学生证号、姓名及分校&#xff08;工作站&#xff09;名称填写在答题纸规定栏内。 考试结束后&#xff0c;把试卷和答题纸放…