java \t怎么从头开始_如何从头开始以正确的面向对象方式创建Java Web Framework

java \t怎么从头开始

您如何用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

java \t怎么从头开始

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

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

相关文章

kl散度的理解_以曲率的视角理解自然梯度优化

一个故事我要讲一个故事:一个你几乎肯定听过的故事&#xff0c;但它的侧重点与你习以为常关注的不同。所有现代深度学习模型都使用梯度下降进行训练。 在梯度下降的每个步骤中&#xff0c;您的参数值从某个起始点开始&#xff0c;然后将它们移动到最大损失减少的方向。 你可以通…

JSP的学习笔记

文章目录概念原理JSP 脚本JSP 内置对象概念 Java Server Pages &#xff1a;Java 服务器端页面 可以理解为&#xff1a;一个特殊的页面&#xff0c;其中既可以指定定义 html 标签&#xff0c;又可以定义 Java 代码 用于简化 Servlet 中的输出页面数据的代码的书写!!! 原理 J…

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

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

御用导航提示页面_UI网站导航设计知识与五个知识案例

导航设计是网站的基石。它确定了至关重要的指标&#xff0c;例如转化率和跳出率&#xff0c;页面停留时间&#xff0c;点击量&#xff0c;并且通常是决定您的访问者拥有良好体验并以比您所说的“用户流量”更快退出网站的决定性因素。但是&#xff0c;好的设计是什么样的呢?一…

Servlet规范中定义的过滤器

文章目录什么是过滤器写过滤器的步骤配置初始化参数过滤器的优先级过滤器的优点过滤器执行过程图解过滤敏感词的示例代码使用 Servlet 来过滤敏感词使用过滤器来过滤敏感词通过配置初始化参数来设置敏感词关于 web.xml 的配置什么是过滤器 Servlet 规范当中定义的一种特殊的类…

Spring Boot错误–创建在类路径资源DataSourceAutoConfiguration中定义的名称为“ dataSource”的bean时出错...

大家好&#xff0c;如果您使用的是Spring Boot&#xff0c;并且遇到诸如“无法为数据库类型NONE确定嵌入式数据库驱动程序类”或“在类路径资源ataSourceAutoConfiguration中定义的名称为dataSource的bean创建错误”之类的错误&#xff0c;那么您来对地方了地点。 在本文中&…

simulink怎么生成vxworks的执行程序_从EPB模型谈谈Simulink代码生成

前段时间有读者朋友问代码生成的例子&#xff0c;说他正在做EPB的仿真模型&#xff0c;但总是和应用串不到一块去&#xff0c;我这里就从一个简单EPB控制模型来谈谈simulink代码生成&#xff0c;总结代码生成方法的同时也顺便回答他的问题。1.EPB模型准备用来举例说明的模型很简…

通过Socket实现文件上传/上传文件

文章目录整个功能的实现步骤客户端的实现步骤服务端的实现步骤示例代码整个功能的实现步骤 1.客户端使用本地的字节输入流读取被上传的文件数据 2.客户端使用网络字节输出流&#xff0c;将读取到的文件数据发送给服务端 3.服务端使用网络字节输入流读取客户端发送过来的文件数…

构造函数调用默认构造函数_显式无参数构造函数与默认构造函数

构造函数调用默认构造函数大多数不熟悉Java的开发人员都会Swift了解到&#xff0c;如果他们没有指定至少一个显式构造函数&#xff0c;则会为Java类隐式创建一个“ 默认构造函数 ”&#xff08; 由javac进行创建&#xff09;。 Java语言规范的 8.8.9节简要指出&#xff1a;“如…

Artifact到底是什么

将项目构建成一个可以在服务器部署的文件包&#xff08;如&#xff1a;war 包或者 exploded 的文件包&#xff09;&#xff0c;或者构建成的 jar 包&#xff08;这些 jar 包通常是某些模块程序文件&#xff0c;也可以是某些插件程序文件&#xff09;&#xff0c;这些都叫 Artif…

python win10 桌面_Python3如何实现Win10桌面自动切换

得空写了个自动切换桌面背景图片的小程序。再不写python就要扔键盘了&#xff0c;对vue还有那么一点好感&#xff0c;天天php真是有够烦。准备工作准备个文件夹放在桌面上&#xff0c;平时看到什么高清好图就拽进去。运行脚本脚本如下&#xff1a;#!/usr/bin/pythonimport ctyp…

java中抽象类继承抽象类_用Java中的抽象类扩展抽象类

java中抽象类继承抽象类示例问题 当我创建Java :: Geci抽象类AbstractFieldsGenerator和AbstractFilteredFieldsGenerator我遇到了一个不太复杂的设计问题。 我想强调一下&#xff0c;这个问题和设计对于某些人来说似乎很明显&#xff0c;但是在我最近与一位初级开发人员&#…

pb90代码如何连接sql2008r2_如何使用 HTTP Headers 来保护你的 Web 应用

众所周知&#xff0c;无论是简单的小网页还是复杂的单页应用&#xff0c;Web 应用都是网络攻击的目标。2016 年&#xff0c;这种最主要的攻击模式 —— 攻击 web 应用&#xff0c;造成了大约 40% 的数据泄露。事实上&#xff0c;现在来说&#xff0c;了解网络安全并不是锦上添花…

Servlet配置初始化参数/配置参数

文章目录给某个Servlet配置初始化参数配置全局的参数ServletConfig给某个Servlet配置初始化参数 也可以通过注解的方式来配置初始化参数&#xff1a; 在 Servlet 中获取针对 Servlet 的参数值&#xff1a; String version getInitParameter("version");配置全局的…

poi动态创建文档_POI创建的文档具有不同条件的灵活样式

poi动态创建文档介绍 这篇文章解释了基于各种标准将样式应用于文档的困难并提供了解决方案。 Java编程中的常见任务之一是根据存储在数据库中的数据创建Excel报告。 在这些情况下&#xff0c;Java程序员使用POI :-)。 这些报告通常对样式和数据格式有严格的规定。 数据通常是SQ…

python编码程序_python 编码

网页的编码方式是简体中文gb2312 (查看网页源码&#xff0c;可以看到)&#xff0c;而python内部的编码方式为unicode&#xff0c;之前的代码是这样的&#xff1a;contentAll urllib.urlopen(urlLink).read()soup BeautifulSoup.BeautifulSoup(contentAll) #生成BeautifulSou…

比特(bit)_二进制数

二进制数位&#xff0c;也叫比特位&#xff0c;其实就是指位置&#xff0c;是指二进制数的位置。例如&#xff0c;0101 这是二进制数&#xff0c;而且是 4 位的二进制数&#xff0c;因为有 4 个位置&#xff0c;每个位置放一个二进制数&#xff0c;确切的说是每个位置放一个二进…

javax线程池超时结束_没有Javax的Jakarta EE:这次世界也不会结束

javax线程池超时结束如果您错过了新闻&#xff0c; Oracle将向Eclipse基金会捐赠Java EE规范 。 这个决定是在规范过程中进行了相当长时间的Hibernate之后&#xff0c;人们理所当然地怀疑Oracle丧失了对Java EE的战略兴趣。 首先&#xff0c;Java EE和更广泛的Java社区很好地满…

Final Cut Pro X如何去视频水印/视频去水印

先剪辑好视频(比如把不要的段切掉)&#xff0c;然后选中视频&#xff0c;把它复制一份到另一个轨道(按住option键&#xff0c;用鼠标左键拖放即可复制)点击右下角的 效果(Effect)→ 模糊(Blur)→ 高斯模糊(Gaussian)&#xff0c;把高斯模糊拖入上边那条轨道再次选择效果(Effect…

为什么线程池里的方法会执行两次_面试官问你java都有哪些线程池,自己是否自定义过线程池...

我还记得大学实习面试时&#xff0c;被问到什么是线程池这个问题&#xff0c;因为这个题我被录取了&#xff0c;原因就是我背出来了&#xff0c;而另外一个面试的没背出来&#xff0c;说实话当时还真不知道它是干什么的&#xff0c;就是看面试题给背下来了&#xff0c;在之后就…