上一节呢,我们简单阐述了远古时期的JavaWeb开发方案,简单来说就是一个Servlet打天下,不管你送什么请求过来,我都要给你返回一整个页面。整个页面都用java拼接出来,可想而知代码是有多么复杂,多么难以维护。要我说,这个时代的程序员都是全才,说话又好听,我超喜欢跟他们打交道的。因为你既要懂html页面,css美工,还要会java,数据库,所有的活都是他一个人包了,可见有多牛。
但是,如果这时候老板说要改页面,做一些特效和动态效果,不知道程序员看着全部混在一起的代码作何感想?
为了弥补全用Servlet的弊端,Sun公司推出了一个叫做JSP的东西,从此进入JavaWeb编年史的青铜时代。
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有没有,不要急,我们再去看一个东西,你就知道咋回事了。
找到Tomcat的实际目录,里面有一个work文件夹
打开,是Catalina,再打开,直到找到发布的项目名称。
里面有一个book_jsp.java,打开一看
哇哦,这不就是一个Servlet吗?所以,JSP的本质就是一个Servlet。还有所谓的JSP九大隐式对象,就是这个Servlet里面的变量罢了。
嗯,如果我们用JSP来重写原来的代码,画风就变成了这样。
<%
//模拟书本列表信息
List books = new ArrayList(){{
add("五年高考三年模拟");
add("王后雄教案");
}};
%>
欢迎来到网上书城!
<% for (String book : books) { %>
- <%=book %>
<% } %>
访问http://localhost:8080/web2_war/book.jsp
实现了一样的效果。
但是,从Servlet完全转到JSP只是一定程度上缓解了编写Servlet的困难,并不能解决前后端代码完全混在一起的弊端。
前端开发人员需要看大量他看不懂的后端代码; 同样,servlet开发人员也要在复杂的前端代码中找到其能写servlet代码的地方。
为了解决纯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 listBooks(){
return new ArrayList(){{
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 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)
欢迎来到网上书城!
- ${item.name}
注意,引入EL表达式需要添加两个依赖:
jstl
jstl
1.2
taglibs
standard
1.1.2
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 listBooks(){
return new ArrayList(){{
add(new Book("五年高考三年模拟"));
add(new Book("王后雄教案"));
}};
}
}
其实就是把JavaBean的数据获取逻辑搬了过来。 BookService代码:
BookService代码:
public class BookService {
BookDao bookDao = new BookDao();
public List 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 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中写多个方法,然后通过携带参数的方式去判断走哪一个方法。但是这个方案并不完美,比如它不太方便做权限控制。为了解决这个问题,就迎来了下一个时代:框架时代。
欲知后事如何,且听下回分解。