园区网站建设调研报告,泉州建设银行网站,建筑工程网架,品牌营销网站建设流程大部分Java应用都是Web或网络应用#xff0c;MVC框架在Java框架中有着举足轻重的地位#xff0c;一开始的Web应用并不现在这样子的#xff0c;一步一步走来#xff0c;每一步都经历了无数的血和泪的教训#xff0c;以史为镜可以知兴替。
1. 草莽时代
早期的Java服务端技…大部分Java应用都是Web或网络应用MVC框架在Java框架中有着举足轻重的地位一开始的Web应用并不现在这样子的一步一步走来每一步都经历了无数的血和泪的教训以史为镜可以知兴替。
1. 草莽时代
早期的Java服务端技术主要是Servlet和JSPServlet的灵感应该是来自于CGICommon 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;charsetutf-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参数值是一个数组
新手需要注意的两个问题:
字符编码通过Content-Type设置所以首先要能读取Content-Type但读取Content-Type需要知道编码本质上是一个先有鸡还是先有蛋的问题。主流的解决方案是解析Content-Type时使用Servlet容器(如Tomcat)默认的编码默认值是ISO-8859-1。目前URL、HTTP头、Cookie都是通过默认编码解析的。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的基础上提供了ServletContextServlet上下文)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 languagejava contentTypetext/html; charsetUTF-8 pageEncodingUTF-8%
% taglib urihttp://java.sun.com/jsp/jstl/core prefixc %
!DOCTYPE html
html body %-- 注释 --%% String name John Doe; % %-- if语句 --%% if (age 18) { % pThis person is an adult./p % } % %-- for循环 --%ul % for (String hobby : hobbies) { % li% hobby %/li % } % /ul %-- 使用JSTL c:if --% c:if test${empty name} pName is not provided./p /c:if %-- 使用JSTL c:forEach遍历列表 --% ul c:forEach varhobby items${hobbies} li${hobby}/li /c:forEach /ul
/body
/html
从JSP这一小节我得到的启示:
技术一定是方便使用的方向演进的发辫使用意味着开发效率从Servlet到JSP从Spring到SpringBoot从MapReduce到Hive概莫不如是了解一些编译原理学习一个词法和语法分析生成器(如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接收用户的请求详细的工作流程如下图
在应用启动时Servlet容器会加载web.xml初始化Struts的ActionServlet并解析struts-config.xml用户发起请求Servlet容器根据web.xml找到对应的servlet-mapping如果是ActionServlet将控制权转交ActionServlet根据请求url查询struts-config.xml的配置找到对应的ActionMapping根据配置创建对应的ActionForm填充数据并调用validate方法完成参数校验根据ActionMapping将请求转发给对应的Action并调用execute方法返回ActionForwardActionServlet处理Action的返回根据ActionForward展示对应的JSP试图 我们来看一下最简单的Struts 1.x的示例首先需要在web.xml中配置ActionServlet让所有待处理的请求经过ActionServlet转发指定structs-config.xml的位置
servletservlet-nameacton/servlet-nameservlet-classorg.apache.struts.action,ActionServlet/servlet-classinit-paramparam-nameconfig/param-nameparam-value/WEB-INF/struts-config.xml/param-value/init-param
/servletservlet-mappingservlet-nameaction/servlet-nameurl-pattern*.do/url-pattern
/servlet-mapping
struts-config.xml用来配置哪些路径是有哪个action来处理的下面是一个极简的示例主要关注的是form-beans、action-mappings
?xml version1.0 encodingUTF-8?
!DOCTYPE struts-config PUBLIC-//Apache Software Foundation//DTD Struts Configuration 1.4//ENhttp://struts.apache.org/dtds/struts-config_1_4.dtd
struts-configform-beansform-bean namelogonForm typeapp.LogonForm//form-beansaction-mappingsaction path/welcome forward/pages/welcome.jsp/action path/login typeapp.LoginAction namelogonForm scoperequest validatetrue input/pages/welcome.jspforward namesuccess path/pages/logon.jsp/forward namefailure path/pages/welcome.jsp//action/action-mappingsmessage-resources parameterresources.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它主要的价值是:
基于Model 2是MVC架构的早期实现者之一统一了开发规范简化了Web开发封装底层的HTTP请求和响应处理提供了Struts标签库
4. Spring MVC
一开始人们都认为Struts 1.x是足够好的直到Struts 2.x(WebWork)和Spring MVC出世人们意识到了问题Spring MVC的优势是:
代码无侵入设计架构层层演进模块化清晰提供丰富的自定义和扩展能力天然和Spring无缝集成不强耦合Servlet API不强耦合框架代码方便单元测试编写自主选择表示层技术整合比JSP更具表现力、学习成本更低的模板
应该承认的是在Spring MVC刚刚出现的时候和Struts 1.x对比不存在决定生死的优势。因为Spring本身的流行加上Spring MVC确实在表现比Struts 1.x略好慢慢Spring MVC开始流行了。下图是Spring MVC处理一个请求的流程除了部分扩展点整体上和Struts 1.x还是相似的:
Struts里的ActionServlet对应DispatcherServletStruts里的structs-config.xml对应-servlet.xmlStruts里的ActionMapping对应HandlerMappingSturts里的Action对应HandlerStruts里的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. 未来展望