当前位置: 首页 > news >正文

JavaWeb编年史(青铜时代)

上一节呢,我们简单阐述了远古时期的JavaWeb开发方案,简单来说就是一个Servlet打天下,不管你送什么请求过来,我都要给你返回一整个页面。整个页面都用java拼接出来,可想而知代码是有多么复杂,多么难以维护。要我说,这个时代的程序员都是全才,说话又好听,我超喜欢跟他们打交道的。因为你既要懂html页面,css美工,还要会java,数据库,所有的活都是他一个人包了,可见有多牛。

但是,如果这时候老板说要改页面,做一些特效和动态效果,不知道程序员看着全部混在一起的代码作何感想?

为了弥补全用Servlet的弊端,Sun公司推出了一个叫做JSP的东西,从此进入JavaWeb编年史的青铜时代。

青铜时代(前期)纯JSP开发

JSP技术,全称是Java Server Page,JSP中采用HTML语言直接生成界面,还可以在界面中使用<% %>脚本标识嵌入Java代码,揪其本质也是最终生成一个Servlet类来编译解析。

说得简单一些,当时的人普遍觉得在Servlet里面写一大堆out.println实在是有点秀。急切地需要一个模板工具来改变这一惨状,JSP正好符合了需求。

所谓地JSP,本质还是一个Servlet,但是它看起来是一个HTML,而且你可以在HTML中混淆一些Java代码。

这就有趣多了,之前在Servlet完成的业务逻辑,现在可以直接在JSP页面中搞。

说白了,就是省去了写一大堆out.println的麻烦,你在jsp中写html,会自动解析成out.println输出语句。

让我们把昨天的项目复制一份改个名字web2

导入idea,在webapp目录下新建一个book.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>网上书城</title>
</head>
<body>
    
</body>
</html>

这怎么看都是一个普通的HTML有没有,不要急,我们再去看一个东西,你就知道咋回事了。

找到Tomcat的实际目录,里面有一个work文件夹

打开,是Catalina,再打开,直到找到发布的项目名称。

里面有一个book_jsp.java,打开一看

哇哦,这不就是一个Servlet吗?所以,JSP的本质就是一个Servlet。还有所谓的JSP九大隐式对象,就是这个Servlet里面的变量罢了。

嗯,如果我们用JSP来重写原来的代码,画风就变成了这样。

<%
    //模拟书本列表信息
    List<String> books = new ArrayList<String>(){{
        add("五年高考三年模拟");
        add("王后雄教案");
    }};
%>

<h1>欢迎来到网上书城!</h1>
<ul>
    <%  for (String book : books) { %>
        <li><%=book %></li>
    <%  } %>
</ul>

访问http://localhost:8080/web2_war/book.jsp

实现了一样的效果。

但是,从Servlet完全转到JSP只是一定程度上缓解了编写Servlet的困难,并不能解决前后端代码完全混在一起的弊端。

前端开发人员需要看大量他看不懂的后端代码; 同样,servlet开发人员也要在复杂的前端代码中找到其能写servlet代码的地方。

青铜时代(中期)JSP+servlet+JavaBean

为了解决纯JSP开发的痛点,人们的一次有效尝试是进行职责分离,可以看作是MVC模式。

简单来说,MVC就是JSP纯粹当作一个模板引擎来使用(View),Servlet只管请求的转发还有页面跳转的操作(Controller),JavaBean负责数据和业务的提供(Model)。

该模型基于MVC模式,完全实现了页面显示和逻辑分离。模型层为JavaBean实现数据的表示和业务逻辑,视图层为JSP页面,只负责显示功能,控制器为Servlet,负责接收用户的请求,设置JavaBean属性,调用JavaBean完成业务处理,最后将处理结果交给JSP页面显示。

在此模型中,Servlet分担了JSP的大部分工作,将JSP从请求接收和流程控制中解放出来,业务逻辑也交给了JavaBean完成,这种方式充分利用了JSP和Servlet两种技术的优点,JSP更适合前台页面的开发,而Servlet更擅长服务器端程序的编写。

JavaBean并非是一个单纯的存储数据的类,它还提供了一系列用来操作自身数据的API方法。

为了演示MVC的优点,我们对之前的代码进行升级--web3

首先是JavaBean,我们把书本单独封装为一个类。

package model;

import java.util.ArrayList;
import java.util.List;

public class Book {
    private String name;

    /**
     * JavaBean必须有一个空构造方法
     */
    public Book(){
        
    }

    public Book(String name){
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Book> listBooks(){
        return new ArrayList<Book>(){{
            add(new Book("五年高考三年模拟"));
            add(new Book("王后雄教案"));
        }};
    }
}

这就说MVC中的M(Model)。

然后是Servlet,只做业务处理和跳转

/**
 * Servlet只做页面跳转和业务逻辑
 * @param request
 * @param response
 * @throws ServletException
 * @throws IOException
 */
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取数据
        Book book = new Book();
        List<Book> books = book.listBooks();
        //将数据(M)嵌入到页面(V),jsp就可以通过EL表达式来获取了
        request.setAttribute("books",books);
        //请求转发
        request.getRequestDispatcher("book.jsp").forward(request,response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request,response);
    }
}

这就说MVC中的C(Controller)。

最后是MVC中的V(View)

<h1>欢迎来到网上书城!</h1>
<ul>
    <c:forEach items="${books}" var="item">
        <li>${item.name}</li>
    </c:forEach>
</ul>

注意,引入EL表达式需要添加两个依赖:

<!--jstl依赖-->
<dependency>
  <groupId>jstl</groupId>
  <artifactId>jstl</artifactId>
  <version>1.2</version>
</dependency>
<dependency>
  <groupId>taglibs</groupId>
  <artifactId>standard</artifactId>
  <version>1.1.2</version>
</dependency>

jsp页面的头部要加上这个标签。

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>

如果EL表达式不生效,page标签要加上这个属性:isELIgnored="false"

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>

验证地址:http://localhost:8080/web3_war/BookServlet

在这种开发模式下,JSP页面中就可以不用任何的<%%>语句了,包括<%=%>全部用EL表达式来代替,列表的遍历和条件判断等(Java中的for循环和if语句)也可以通过JSTL来代替。 这样的话视图层相比较之前的开发模式来说要薄得多的多,JSP中不涉及任何的业务逻辑,前端人员修改样式也十分方便。这里可以理解为JSP为MVC设计模式中的V,即视图。

控制层通过Servlet来实现,获取前台传的参数、控制页面跳转,封装对象、向前台传输对象或者参数。并且可以由自己设计,设法用一个Servlet类实现该模块的所有功能的页面跳转。这里可以理解为Servlet为MVC设计模式中的C,即控制器。

青铜时期(后期)经典三层架构

虽然上面的结构已经有了MVC的雏形,但还不是标准的MVC,因为JavaBean过于臃肿,并不能完全作为M层存在。

所以后来又有了经典的三层架构。和MVC时期不同的是,M又做了细化,分化出service层和dao层。

service层用于业务逻辑的处理,dao层则只用于跟数据库打交道,而原来的model层则变薄了,仅仅就是一些属性和get,set方法而已。

很多初学者容易把三层架构和MVC搞混,以为是一个东西,其实不是的哈。

看下三层架构的示意图

三层架构来源于后端开发的一种分层的思想。

引用自百科的解释:

三层架构(3-tier architecture)通常意义上的三层架构就是将整个业务应用划分为:界面层(User Interface layer)、业务逻辑层(Business Logic Layer)、数据访问层(Data access layer)。

区分层次的目的即为了“高内聚低耦合”的思想。在软件体系架构设计中,分层式结构是最常见,也是最重要的一种结构。微软推荐的分层式结构一般分为三层,从下至上分别为:数据访问层、业务逻辑层(又或称为领域层)、表示层。

我之前的《文章发布系统》系列教程,采用的就是三层架构模式。

各层的作用如下:

表示层(Controller):主要对用户的请求接受,以及数据的返回,为客户端提供应用程序的访问。

业务逻辑层(Service):主要负责对数据层的操作。也就是说把一些数据层的操作进行组合。

数据访问层(dao):主要看数据层里面有没有包含逻辑处理,实际上它的各个函数主要完成各个对数据文件的操作。而不必管其他操作。

相信各位在初学JavaWeb的时候,对这三层架构都不陌生。当时我们一般就写那种特别简单的crud项目,这就导致我们很不理解为什么明明在controller层就能做完的事情,非要去service层和dao层走一遍。

其实这真的只是因为我们在学习的时候,做的项目太简单了。对于大型的复杂项目,分层是必须的。

让我们来看如何从JSP+servlet+JavaBean时代演变到三层架构时代。

创建service层,新建BookService

创建dao层,新建BookDao

BookDao代码(模拟SQL获取):

public class BookDao {

    public List<Book> listBooks(){
        return new ArrayList<Book>(){{
            add(new Book("五年高考三年模拟"));
            add(new Book("王后雄教案"));
        }};
    }
}

其实就是把JavaBean的数据获取逻辑搬了过来。 BookService代码:

BookService代码:

public class BookService {
    BookDao bookDao = new BookDao();

    public List<Book> listBooks(){
        return bookDao.listBooks();
    }
}

因为业务比较简单,所以就直接调用dao层的同名方法即可。

这个时候Book类就是一个纯净的JavaBean了,只用来装配数据,没有了业务逻辑。

至于BookServlet,就需要BookService的协助,和原来不同的是,把JavaBean换成了BookSerive:

BookService bookService = new BookService();

/**
 * Servlet只做页面跳转和业务逻辑
 * @param request
 * @param response
 * @throws ServletException
 * @throws IOException
 */
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    //获取数据
    Book book = new Book();
    List<Book> books = bookService.listBooks();
    //将数据(M)嵌入到页面(V),jsp就可以通过EL表达式来获取了
    request.setAttribute("books",books);
    //请求转发
    request.getRequestDispatcher("book.jsp").forward(request,response);
}

最后再来一张全图:

至此,页面的表现由jsp实现,转发控制由servlet实现,业务逻辑写在业务逻辑层,操作数据库部分写在持久化层(dao层),封装数据放在bean层, 分工明确,各司其职。从servlet一直到三层架构的转变,其实都是为了实现高内聚,低耦合。一步一步将各个功能分配到不同的地方实现。

即便是到了今天,任何javaweb项目中都离不开三层架构的影子,可见其重要性。

可是呢,基于JSP+servlet+JavaBean的三层架构依然存在问题,因为一个项目中可能涉及成百上千个路由,如果每一个路由都配置一个Servlet,会导致servlet过多,转向频繁的问题,流程,配置也不易集中管理。有一个折中的办法,就是在一个Servlet中写多个方法,然后通过携带参数的方式去判断走哪一个方法。但是这个方案并不完美,比如它不太方便做权限控制。为了解决这个问题,就迎来了下一个时代:框架时代。

欲知后事如何,且听下回分解。

相关文章:

  • 机器学习笔记 - 模式识别的应用场景之一简单车牌识别
  • Selenium基础 — 多窗口操作
  • Nginx之动静分离
  • 怎么成为稚晖君?
  • 离线数仓 (四) --------- 用户行为数据采集模块
  • HACKTHEBOX——Valentine
  • 哈佛结构和冯诺依曼结构
  • 【黄啊码】MySQL入门—7、这些函数运用得好,高级工程师都直呼内行
  • UnityPackageManager相关
  • 数字图像处理——基本运算
  • Cosmos模块化功能链 走向亿级用户的超级Dapp时代
  • 【黑马程序员名师pink老师讲HTML】HTML很容易忘记?有它我不慌的
  • .NetCore项目nginx发布
  • 主流开源OLAP对比分析
  • 二叉搜索树的基本操作 || TreeMap和TreeSet介绍
  • [译]Python中的类属性与实例属性的区别
  • 《剑指offer》分解让复杂问题更简单
  • 【React系列】如何构建React应用程序
  • Android路由框架AnnoRouter:使用Java接口来定义路由跳转
  • JavaScript 奇技淫巧
  • Javascript 原型链
  • Java程序员幽默爆笑锦集
  • jQuery(一)
  • js算法-归并排序(merge_sort)
  • Mysql优化
  • OpenStack安装流程(juno版)- 添加网络服务(neutron)- controller节点
  • Sublime text 3 3103 注册码
  • 包装类对象
  • - 概述 - 《设计模式(极简c++版)》
  • 关于Java中分层中遇到的一些问题
  • 京东美团研发面经
  • 我看到的前端
  • 我有几个粽子,和一个故事
  • ​二进制运算符:(与运算)、|(或运算)、~(取反运算)、^(异或运算)、位移运算符​
  • ​总结MySQL 的一些知识点:MySQL 选择数据库​
  • #我与Java虚拟机的故事#连载05:Java虚拟机的修炼之道
  • #我与Java虚拟机的故事#连载11: JVM学习之路
  • (13)Latex:基于ΤΕΧ的自动排版系统——写论文必备
  • (a /b)*c的值
  • (Redis使用系列) Springboot 使用redis实现接口Api限流 十
  • (每日持续更新)jdk api之FileReader基础、应用、实战
  • (四)Controller接口控制器详解(三)
  • (转)AS3正则:元子符,元序列,标志,数量表达符
  • (转)树状数组
  • (轉貼) UML中文FAQ (OO) (UML)
  • .NET MAUI学习笔记——2.构建第一个程序_初级篇
  • .net 调用php,php 调用.net com组件 --
  • .NET/ASP.NETMVC 深入剖析 Model元数据、HtmlHelper、自定义模板、模板的装饰者模式(二)...
  • .Net7 环境安装配置
  • .net的socket示例
  • .Net通用分页类(存储过程分页版,可以选择页码的显示样式,且有中英选择)
  • .ui文件相关
  • [@Controller]4 详解@ModelAttribute
  • [2016.7 day.5] T2
  • [20161101]rman备份与数据文件变化7.txt