Java Web这一路走来

news2024/11/25 19:47:32

大部分Java应用都是Web或网络应用,MVC框架在Java框架中有着举足轻重的地位,一开始的Web应用并不现在这样子的,一步一步走来,每一步都经历了无数的血和泪的教训,以史为镜可以知兴替。

1. 草莽时代

早期的Java服务端技术主要是Servlet和JSP,Servlet的灵感应该是来自于CGI(Common Gateway Interface),Java通过Servlet定义了一组接口和类,用来抽象这个过程,核心抽象就3个,分别是输入、处理、输出

  • 输入,ServletRequest是客户端输入的抽象,提供请求的内容读取的工具方法
  • 处理,Servlet定了模板方法,开发者可以覆盖service方法,使用ServletRequest读取请求,使用ServletResponse响应输出
  • 输出,ServletResponse是客户端响应的抽象,提供响应内容的工具方法

1.1 Servlet

我们来看一下ServletRequest和ServletResponse的接口定义,我们讲一下核心的方法,不需要记忆,只要对它们的能力边界有一个概念即可

1. 输入

分组

方法

解释

服务端

getProtocol()

返回请求使用的协议名和版本号,通常是"HTTP/1.1"

isSecure()

是否是HTTPS的,true表示是,false表示否

getServerName()

接收请求的服务器主机名,应用服务器支持自定义,Tomcat用server.xml的Host.name来设置,默认取机器的主机名(hostname命令)

getServerPort()

接收请求的端口,服务器监听并等待客户端连接的端口

getLocalName()

一般等同于getServerName,复杂环境(负载均衡、集群时)可能有独立值

getLocalAddr()

当前请求绑定的服务器IP地址,TCP连接中对应的IP

getLocalPort()

一般等同于getServerPort,处理请求的服务器端口,请求被转发会有差异

客户端

getRemoteHost()

使用getRemoteAddr()返回的IP地址,通过DNS反解析,解析不到直接返回IP

getRemoteAddr()

请求的客户端IP地址,TCP连接的IP,局域网内是局域网IP,公网是公网IP

getRemotePort()

请求的客户端端口

元信息

getCharacterEncoding()

通过HTTP头浏览器设置请求编码,如Content-Type: application/json;charset=utf-8

Servlet容器会用这个编码解析请求体

getContentType()

数据的MIME类型,通过HTTP头Content-Type指定

getContentLength()

请求体的长度,只包含body部分,不包括url、Head、Cookie

getLocale()

客户端的区域设置,有语言代码+国家/地区代码组成,比如zh_CN表示中文_中国大陆,通过解析HTTP头Accept-Language获取

请求体

getInputStream()

请求体的输入流,只能被读取一次,返回ServletInputStream对象

getReader()

请求体的输入流,读取的是字符,使用Content-Type里指定的编码,如果没指定则使用容器设置的默认编码,没设置则默认iso-8859-1

getParameterNames()

请求参数的名称,可以是URL查询字符串参数或表单参数

getParameter(String pname)

获取指定参数名对应的参数值

getParameterValues(String pname)

获取指定参数名对应的参数值,参数值是一个数组,应对同一个参数有多个值的情况

getParameterMap()

获取参数名-参数值的Map,参数值是一个数组

新手需要注意的两个问题:

  1. 字符编码通过Content-Type设置,所以首先要能读取Content-Type,但读取Content-Type需要知道编码,本质上是一个先有鸡还是先有蛋的问题。主流的解决方案是解析Content-Type时使用Servlet容器(如Tomcat)默认的编码,默认值是ISO-8859-1。目前URL、HTTP头、Cookie都是通过默认编码解析的。
  2. ServletRequest的输入流只能读取一次,Servlet规范定义了HttpServletRequestWrapper,通过扩展Wrapper实现,如Spring就提供了ContentCachingRequestWrapper。
2. 输出

ServletReponse的定义要相对简单的多

分组

方法

解释

元信息

setContentType(String)

设置响应给客户端内容的MINE类型

setCharacterEncoding(String)

设置响应给客户端内容的编码

setLocal(Locale)

响应的区域设置

输出流

getOutputStream()

获取输出流,用于输出客户端内容,需要自己将响应内容转换为byte数组

getWriter()

获取输出流,直接输出字符,使用setCharacterEncoding指定的编码转换为字节数组

3. 处理

Servlet是整个Java Web服务的核心,它负责读取输入,处理业务逻辑,最终生成输出内容。我们来看一下Servlet的继承结构

  • Servlet定义一个service方法,接收ServletRequest参数用来读取输入,通过ServletResponse向客户端响应数据。
  • GenericServlet在Servlet的基础上,提供了ServletContext(Servlet上下文),Servlet配置信息。
  • HttpServlet则进一步将service方法安装HTTP METHOD的值,拆分为GET、POST、PUT等等。

1.2 JSP

从Servlet的定义来看,一开始的设计还是相当的直观和简单的,只是定义了输入-处理-输出这3个实体,这也印证了架构是一个逐步演进的过程,需求才是架构的主要驱动力。HTML本身是比较复杂的,需要大量的模板代码,通过Java代码来拼接,代码会显得十分拖沓。JSP的目标就是让HTML的生成变得简单,可以认为它是现代模板(如Thymeleaf)的前身,通过将Java代码、自定义标签(JSTL)内嵌到HTML代码中来生成最终的响应内容。

当客户端请求JSP时,WebServer容器会调用JSP引擎(JSP Engine),将JSP编译为Servlet,再由Servlet引擎(Servlet Engine)执行Servlet,处理输入,响应输出,一起看起来都那么的顺利成章

我们来看一个简单的JSP示例,好让我们对JSP有一个直观的感受,可以说JSP已经具备所有必须的基本能力。

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>  
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>  
<!DOCTYPE html>  
<html>  

<body>  
    <%-- 注释 --%>
    <%  
        String name = "John Doe";  

    %>  
  
    <%-- if语句 --%>
    <% if (age >= 18) { %>  
        <p>This person is an adult.</p>  

    <% } %>  
    <%-- for循环 --%>


    <ul>  
        <% for (String hobby : hobbies) { %>  
            <li><%= hobby %></li>  
        <% } %>  
    </ul>  
  
    <%-- 使用JSTL c:if --%>  
    <c:if test="${empty name}">  
        <p>Name is not provided.</p>  
    </c:if>  

  
    <%-- 使用JSTL c:forEach遍历列表 --%>  
    <ul>  
        <c:forEach var="hobby" items="${hobbies}">  
            <li>${hobby}</li>  
        </c:forEach>  
    </ul>  
</body>  
</html>

从JSP这一小节我得到的启示:

  1. 技术选型的采用率 = 易用性 * 能力 ,易用意味着开发效率,从Servlet到JSP,从Spring到SpringBoot,从MapReduce到Hive,概莫不如是。
  2. 从第1点提取模式,价值 = 界面友好 * 内核强大,对软件工程师来说,你的技术能力就是你的内核,你的沟通表达、自我驱动力等软技能就是你的界面,强强结合才能最大化价值
  3. 了解一些编译原理,学习一个词法和语法分析生成器(如JavaCC),能够拓展软件工程师的能力边界

2. Model 1 & Model 2

使用Servlet和JSP已经完成功能开发了,Model 1和Model 2其实是使用Servlet和JSP过程中总结的最佳实践。随着业务的发展,业务的复杂性增长,JSP遇到了两个问题:

1. 内嵌大量的Java代码,业务逻辑和展现层逻辑混合,可读性差,不好理解

2. 代码复用困难,功能重复编写,可维护性差,后续升级困难

3. 调试困难,业务逻辑都在JSP中,所有的HTML、Javascript和Java都混在一起

Model 1中JSP负责接收用户请求,业务流程控制,组装展现层数据,返回响应结果。从Model 1开始已经萌发了将部分业务逻辑转移到JavaBean的观念。

Model 2往前再迈了一步,结合Servlet和JSP的有点,Servlet做为一个Controller,处理请求并构造JSP需要的数据;JSP做为表示层,不再处理业务逻辑.

3. Struts 1.x

Struts 1.x实际上是基于Model 2实现的一个MVC框架,通过Actionservlet接收用户的请求,详细的工作流程如下图

  1. 在应用启动时,Servlet容器会加载web.xml,初始化Struts的ActionServlet,并解析struts-config.xml
  2. 用户发起请求,Servlet容器根据web.xml,找到对应的servlet-mapping,如果是ActionServlet,将控制权转交
  3. ActionServlet根据请求url,查询struts-config.xml的配置,找到对应的ActionMapping
  4. 根据配置创建对应的ActionForm,填充数据并调用validate方法完成参数校验
  5. 根据ActionMapping将请求转发给对应的Action,并调用execute方法,返回ActionForward
  6. ActionServlet处理Action的返回,根据ActionForward展示对应的JSP试图

我们来看一下最简单的Struts 1.x的示例,首先需要在web.xml中配置ActionServlet,让所有待处理的请求经过ActionServlet转发,指定structs-config.xml的位置

<servlet>
    <servlet-name>acton</servlet-name>
    <servlet-class>org.apache.struts.action,ActionServlet</servlet-class>
    <init-param>
        <param-name>config</param-name>
        <param-value>/WEB-INF/struts-config.xml</param-value>
    </init-param>
</servlet>

<servlet-mapping>
    <servlet-name>action</servlet-name>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>

struts-config.xml用来配置哪些路径是有哪个action来处理的,下面是一个极简的示例,主要关注的是form-beans、action-mappings

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts-config PUBLIC
      "-//Apache Software Foundation//DTD Struts Configuration 1.4//EN"
      "http://struts.apache.org/dtds/struts-config_1_4.dtd">
<struts-config>
    <form-beans>
        <form-bean name="logonForm" type="app.LogonForm"/>
    </form-beans>
    <action-mappings>
        <action path="/welcome" forward="/pages/welcome.jsp"/>
        <action path="/login" type="app.LoginAction" name="logonForm" scope="request" validate="true" input="/pages/welcome.jsp">
            <forward name="success" path="/pages/logon.jsp"/>
            <forward name="failure" path="/pages/welcome.jsp"/>
        </action>
    </action-mappings>
    <message-resources parameter="resources.application"/>
</struts-config>

form-bean里配置的元素需要继承ActionForm,我们看一个极简的示例

public class LogonForm extends ActionForm {

    private String userName;
    private String password;

    
    public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {
        return null; // 数据验证,验证失败返回ActionErrors
    }

    public void reset(ActionMapping mapping, HttpServletRequest request) {
        // 数据初始化
    }
}

action可以直接配置JSP,也可以配置Action实现类,下面是一个Action的实现类

public class LoginAction extends Action {
    public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
        LogonForm loginForm = (LogonForm) form;
        // 业务逻辑处理

    }
}

在当时Struts 1.x也是霸主级的存在,当初技术公司使用的技术栈基本都是SSH,而这里的第一个S就是代指Struts 1.x,它主要的价值是:

  1. 基于Model 2,是MVC架构的早期实现者之一,统一了开发规范
  2. 简化了Web开发,封装底层的HTTP请求和响应处理,提供了Struts标签库

4. Spring MVC

一开始人们都认为Struts 1.x是足够好的,直到Struts 2.x(WebWork)和Spring MVC出世,人们意识到了问题,Spring MVC的优势是:

  1. 代码无侵入设计,架构层层演进,模块化清晰,提供丰富的自定义和扩展能力
  2. 天然和Spring无缝集成
  3. 不强耦合Servlet API,不强耦合框架代码,方便单元测试编写
  4. 自主选择表示层技术,整合比JSP更具表现力、学习成本更低的模板

应该承认的是,在Spring MVC刚刚出现的时候,和Struts 1.x对比,不存在决定生死的优势。因为Spring本身的流行,加上Spring MVC确实在表现比Struts 1.x略好,慢慢Spring MVC开始流行了。下图是Spring MVC处理一个请求的流程,除了部分扩展点,整体上和Struts 1.x还是相似的:

  • Struts里的ActionServlet对应DispatcherServlet;
  • Struts里的structs-config.xml对应-servlet.xml
  • Struts里的ActionMapping对应HandlerMapping
  • Sturts里的Action对应Handler
  • Struts里的JSP对应View

Spring MVC提供了额外扩展能力,让用户可以自定义部分逻辑:

  • 自定义HandlerMapping来处理请求和Handler的关系
  • 自定义HandlerAdapter调用自定义的Handler类型
  • 自定义HttpMessageConverter自定义数据类型转换
  • 自定义DataBinder完成数据转换、格式化、验证
  • 自定义ViewResolver自定义视图名到View的转换
  • 自定义View实现Model到向客户端输出内容的转换

5. Spring Boot

应该说对于一个没有去了解过动态语言(Python、Ruby等),没有了解过现代Web框架的人来说,Spring MVC已经够好了。然而即使是开发一个最简单的Web应用,你还是需要配置web.xml,确定HandlerMapping,打包部署,经历一整套复杂的流程。得益于内嵌式Servlet容器,Spring Boot提把Servlet容器整合到同一个fat jar中,让Spring Boot能够自启动。

--未完待续--

6. 未来展望

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

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

相关文章

使用ollydbg还原密码程序的密码

前言&#xff1a;我们上次用ida pro破解了程序密码这次我们换个工具来还原程序密码 第一步 和上次一样把找到的程序拖到ollydbg里面 第二步 右键点击查找所有引用字符串 第三步 找到报错代码双击进入 第五步 找到上面的jnz双击将其改成jz 第六步 将所修改的保存点击空白…

FPGA(Verilog)实现uart传输协议传输数据(含仿真)

目录 实现功能&#xff1a; 1.接收uart串行数据&#xff0c;输出并行数据(1byte)。 2.输入并行数据(1byte)&#xff0c;输出uart串行数据。 3.完成uart传输的1次环回。 uart协议的1帧数据传输 模块封装-port设置 Verilog代码实现 1.uart接收模块:接收串行数据,输出并行数…

基于Java+SpringBoot+Vue企业员工管理系统(源码+文档+部署+讲解)

一.系统概述 随着社会的发展&#xff0c;系统的管理形势越来越严峻。越来越多的用户利用互联网获得信息&#xff0c;但各种信息鱼龙混杂&#xff0c;信息真假难以辨别。为了方便用户更好的获得信息&#xff0c;因此&#xff0c;设计一种安全高效的员工管理系统极为重要。 为设计…

swiftui macOS实现加载本地html文件

import SwiftUI import WebKitstruct ContentView: View {var body: some View {VStack {Text("测试")HTMLView(htmlFileName: "localfile") // 假设你的本地 HTML 文件名为 index.html.frame(minWidth: 100, minHeight: 100) // 设置 HTMLView 的最小尺寸…

【攻防世界】php_rce (ThinkPHP5)

进入题目环境&#xff0c;查看页面信息&#xff1a; 页面提示 ThinkPHP V5&#xff0c;猜测存在ThinkPHP5 版本框架的漏洞&#xff0c;于是查找 ThinkPHP5 的攻击POC。 构造 payload: http://61.147.171.105:50126/?sindex/think\app/invokefunction&functioncall_user_f…

续写Groq

这章写点Groq干货,理性的分析。 首先是Articical Analysis的关于Mixtral8*7B的吞吐比较 上图是有Mixtral 8*7BPaaS服务的AI服务商,Mistral自己居然排倒数第三 ,Groq是真的遥遥领先啊。 另外这个图是比较每100万tokens的cost,无论是推理速度还是cost,Groq都是遥遥领先…

如何把1G多的视频压缩到500兆以内?3个方法轻松减小文件内存~

微信已经成为了我们上班交流沟通时必不可少的通讯工具之一&#xff0c;在使用微信时&#xff0c;常常会遇到系统提示发送的word、ppt、pdf文件、视频、压缩包等文件超过1G&#xff0c;无法发送。有没有什么办法可以缩小文件的体积呢&#xff1f;今天给大家介绍几款可以用于视频…

Python中Python-docx 包的run介绍

先对run做一个简单地介绍。每个paragraph对象都包含一个run对象的列表。举例&#xff1a; 这是一个简短的段落。 from docx import Document doc Document("1.docx") #上面这段话保存在1.docx中 print("这一段的run个数是&#xff1a;",len(doc.paragr…

leetcode热题100.划分字母区间

Problem: 763. 划分字母区间 文章目录 题目思路复杂度Code 题目 给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段&#xff0c;同一字母最多出现在一个片段中。 注意&#xff0c;划分结果需要满足&#xff1a;将所有划分结果按顺序连接&#xff0c;得到的字符串仍…

基于Leaflet.js和Turf.js的等值线区间自定义及颜色自适应实践

目录 前言 一、Turf.js等值线相关制作 1、生成方法 2、主要参数 二、实际案例开发 1、新建展示页面 2、等值线生成 3、基于Leaflet的再优化 总结 前言 在气象方面的GIS应用当中&#xff0c;会根据实际的工作需要建立不同的监测站点。气象监测站的主要功能包括&#xff1…

如何快速识别陶瓷件的外观缺陷吗?

陶瓷件由陶瓷材料制成的物品或零部件&#xff0c;通常用于装饰、日常生活用品、工艺品或工业应用。陶瓷是一种非金属材料&#xff0c;具有耐高温、耐磨损、绝缘、化学稳定等特性&#xff0c;因此在许多领域得到广泛应用。 本案针对陶瓷件尺寸长25mm*宽11mm*高2mm的产品的外观检…

物联网实战--驱动篇之(五)TEA和AES加密算法

目录 一、前言 二、TEA算法 三、AES算法 四、加解密测试 五、安全性保障 一、前言 物联网的安全性是经常被提及的一个点&#xff0c;如果你的设备之间通讯没有加密的话&#xff0c;那么攻击者很容易就能获取并解析出报文的协议&#xff0c;从而根据攻击者的需要进行设备操…

运行gitHub中的vue项目,遇到三个报错解决方案

报错1&#xff1a;解决npm run serve启动报错npm ERR Missing script:"serve" 启动项目的时候用npm run serve发现报了以下的错误 npm ERR! Missing script: "serve" npm ERR! npm ERR! To see a list of scripts, run: npm ERR! npm runnpm ERR! A co…

Linux安装Oracle11g(无图形界面下的静默安装)

Oracle11g安装文档-Linux静默安装 环境准备安装数据库配置监听器创建数据库测试打开防火墙 环境准备 创建组和用户 [rootlocalhost ~]# groupadd oinstall #创建oinstall组 [rootlocalhost ~]# groupadd dba  #创建dba组 [rootlocalhost ~]# useradd -g oinstall -G dba -m…

window2003 中cmd提权到system

打开cmd&#xff0c;输入命令&#xff1a; sc Create syscm2 binPath "cmd /K start" type own type interact 注意&#xff01;等号左边是有空格然后才是引号的&#xff0c;三个等号都是&#xff0c;否则会失败 成功提示&#xff1a;[SC] CreateService 成功 然后…

Junit单元测试基础

目录 配置xml文件&#xff0c;注入依赖 常用注释 1.Test 2.BeforeAll和AfterAll 3.BeforeEach和AfterEach 4.TestMethodOrder 参数化 1.单参数 2.多参数 3.通过方法生成参数 测试用例执行管理 JUnit 是一个流行的 Java 单元测试框架&#xff0c;它为开发者提供了一…

题目 2694: 蓝桥杯2022年第十三届决赛真题-最大数字【暴力解法】

最大数字 原题链接 &#x1f970;提交结果 思路 对于每一位&#xff0c;我我们都要尽力到达 9 所以我们去遍历每一位, 如果是 9 直接跳过这一位 如果可以上调到 9 我们将这一位上调到 9 &#xff0c;并且在a 中减去对应的次数 同样的&#xff0c;如果可以下调到 9&#xff0c;我…

参数仅有0.049M!基于Mamba的医学图像分割新SOTA来了!

目前&#xff0c;基于CNN和Transformer的医学图像分割面临着许多挑战。比如CNN在长距离建模能力上存在不足&#xff0c;而Transformer则受到其二次计算复杂度的制约。 相比之下&#xff0c;Mamba的设计允许模型在保持线性计算复杂度的同时&#xff0c;仍然能够捕捉到长距离的依…

【AIGC】训练数据入库(Milvus)

之前的文章有写如何获取数据、如何补充数据&#xff0c;也有说如何对数据进行清洗、如何使用结构化数据进行训练。但好像没有说如何将训练数据“入库”。这里说的入库不是指 MySQL 数据库&#xff0c;而是指向量检索库 Milvus。 众所周知&#xff0c;人工智能多用向量数据进行…

人民网至顶科技:《开启智能新时代:2024中国AI大模型产业发展报告发布》

3月26日&#xff0c;人民网财经研究院与至顶科技联合发布《开启智能新时代&#xff1a;2024年中国AI大模型产业发展报告》。该报告针对AI大模型产业发展背景、产业发展现状、典型案例、挑战及未来趋势等方面进行了系统全面的梳理&#xff0c;为政府部门、行业从业者以及社会公众…