JSP1

Servlet 专栏收录该内容
15 篇文章 0 订阅

Java Server Page1

回顾

前言(Preface)

1.JSP的介绍

JSP对于Servlet的优势

  • 首先,说一下JSP这个词是怎么构成的,那么Servlet,是server和let两个单词合在一起,服务器端的程序片段,那同样,JSP是Java Server Page,是Java在服务器端的页面技术,Java服务器端的动态网页技术,JSP。其实说白了,它和Servlet功能是类似的,都能够做动态网页,不过它的开发方式更为简化,这是JSP。那JSP,它的设计理念和Servlet是,就是开发方式是相反的,那么Servlet为什么麻烦,因为呢,Servlet本身是个类,我们在这个类中呢,要输出网页,我们需要拼标签,类中要拼标签,而标签的话呢,这个一行一行拼,太繁琐了,麻烦之处就在于此啊,在类中拼标签不方便。
  • 那么JSP呢,它的理念和这个刚好相反,JSP是这样的语法,是我们先写标签,在标签之内拼Java,把它反过来,标签之内拼Java,有人说这有什么,这不一样么,这不还汤不换药么,都这点东西么,但是我们开发的时候,这个本质上有很大的区别,我们开发时是这样,在我们写代码之前呢,美工早就把静态网页写好了,它会把静态网页给我,我利用这个静态网页开发出动态网页,我们开发出动态网页之前,是有一个demo,有一个静态网页的模板,参考那个来写动态网页。那如果美工给我的是静态网页,我要开发动态网页的话,如果是JSP就方便了,我可以直接把美工的那个代码,直接粘贴过来,有了HTML,这些结构,里面哪些是动态的逻辑,写java,这就方便了。因为,那个HTML标签呢,是贴过来的,一下就有了,非常方便,那有人说,你用Servlet开发,就不能贴过来么,你看Servlet里这样合适贴么,你怎么贴过来,你贴过来也得一行一行改,是这样吧,没法搞:

class xxxServlet{
	...service(){
		......
		out.println("<html>");
		......
		out.println("</html>");
		......
	}
}

JSP的发展

  • 所以说这个开发方式上,JSP就更方便,是这样的。那再一个呢,这个JSP这项技术,是非常的火的,那当然了,其实到目前为止,已经有了替代的方案,就是我们现在去工作的话,有的企业,它可能会不用JSP,有替代方案,但是呢,还有很多企业会用,曾经呢,这个JSP非常的火,那火到什么程度呢,就是说有些程序员只知道JSP,不知道Java,什么意思呢,就提到Java,就是JSP,别的什么都不知道,就知道有JSP,火到这种程度,你像以前,我有同学,他是学那个,学C啊,C++,然后呢,它C++呢,有优点,就是说它这个竞争力比较,竞争的话,比较少,因为做的人少,但是呢,也有缺点,想跳个槽挺费劲的,因为做C的公司也不多对吧,跳槽费劲,所以说他想学Java,他说,洪鹤,要不你教我学JSP吧,我说,就冲你这句话,我就不能教你,因为java不止是Jsp,但是从这个事能反应什么呢,就jsp给人的印象是什么呢,就是非常重要的。
  • 或者这么讲,在很早以前,大概两千年左右吧,那个时候,尤其是2000年以前的时候,那个时候,如果说啊,你会java基础,基本语法,你会jsp,就会这两个内容,你在这个,这怎么说呢,你在咱们这个圈里头,狂赚了,随便找个工作,也得8000,一万,就那个年代,就这么牛,因为什么呢,那个时候,这东西太厉害了,没有替代品明白吧,没有人和它竞争,很厉害,但现在的话,你说我会个java基础,我会个jsp,会怎么样呢,你会连工作也找不着,因为现在企业要求高了,要求我们还得会什么呢,前端,还得会这个框架,很多内容,而且企业呢,它将来开发时,一定不是简单的只是用jsp,它得用框架明白吧,所以,你不会框架也不行,以前没有框架,就是jsp。
  • 总而言之啊,就是说jsp很火,那么现在也是一项主流的技术,也有很多企业会用,我们需要学会它,然后呢,jsp它的作用和Servlet,一模一样,也是用来处理HTTP协议的,通俗来讲也是用来拼动态网页的,拼动态资源的,所以Servlet有什么用,它就有什么用,作用完全是一样的,这就是jsp。这个JSP,曾经对于Servlet来说,它是一个新的技术,那么它能够对Servlet这个开发有所改善,它的作用和Servlet是一样的,也是做动态网页,往大了说,也是处理 HTTP协议,但是呢,用这项技术做网页,做动态网页,就更方便,然后呢,它的这个理念是我们先写标签,标签内写java,标签里面套着写java,和Servlet呢,刚好相反,那么下面呢,我们说一下,这个jsp,我们在开发的时候,那到底得怎么写,说一下呢这个,它的开发的这个规则。

JSP的开发规则概述

  • 那JSP怎么去开发,怎么去演示,首先呢,这个我们说的jsp呢,它是一个特殊类型的文件,它既不是java文件,它也不是html文件,我们在书写的时候呢,写的是一个以jsp为后缀的文件,然后呢,在这个文件之内啊,可以包含如下的内容,我们在这个文件里,可以写html,css,js,可以写前端的东西,可以有注释,可以写java代码,可以写指令,可以写隐含对象。当然了,有的东西呢,什么指令,什么隐含对象,这是后话,但是呢,最核心的,最主要的,两部分,一个呢,是可以写html,一个呢,是可以写java,而且呢,是html里套这写java。那么,在jsp里,这个html怎么写呢,是直接书写,我们可以直接在这里呢,写任何的标签就可以,甚至呢,你可以把美工呢,给你的那个网页代码贴过来,都可以。

如何编写JSP
- step1,写一个以".jsp"为后缀的文件
- step2,在该文件中,可以包含如下的内容:
- HTML
- 注释
- Java代码
- 指令
- 隐含对象

JSP页面中的HTML代码
- JSP页面中的HTML包括: HTML,CSS,JavaScript
- 像编写HTML页面一样编写即可
- 作用:控制页面在浏览器中的显示效果
- 转译成Servlet时的规则:成为Servlet中service()方法中的out.write语句

JSP页面中的注释
- 语法: 
1.<!-- 注释内容 -->:HTML注释,注释中的内容如果包含Java代码,这些Java代码会被执行
2.<%-- 注释内容 --%>:JSP特有的注释,如果注释的内容出现Java代码,会被忽略

JSP中的Java代码
- JSP页面中的Java代码,包含以下三种:JSP表达式,JSP小脚本,JSP声明
- 编写位置: 页面的任意位置
- 作用:控制页面中可变内容的产生

JSP表达式
- 语法规则: <%= ... ... %>
- 合法内容: 变量、变量加运算符组合的表达式、有返回值的方法
- 转译成Servlet时的规则:在service()方法中用out.print语句输出该变量、表达式、方法的值
- 例如:<p>The square root of 5 is <%=Math.sqrt(5)%></p>
- 转换成:
	out.write("<p>The square root of 5 is");
	out.print(Math.sqrt(5));
	out.write("</p>");

JSP小脚本
- 语法规则: <% ... ... %>
- 合法内容:能够写在方法里的Java代码片段都可以作为小脚本
- 转译成Servlet时的规则:原封不动成为Servlet类的service()方法里面的一段代码
- 例如:
	<%
		String name = request.getParameter("name");
		if(name!=null && !name.equals("")){
	%>
		<p>Your name is <%= name%></p>
	<%
		}
	%>
- 转换成如下代码插入到service方法中
	String name = request.getParameter("name");
	if(name!=null&&!name.equals("")){
		out.write("<p>Your name is");
		out.print(name);
		out.write("</p>");
	}

JSP声明
- 语法规则:<%! ... ... %>
- 合法内容:成员属性或成员方法的声明
- 转译成Servlet时的规则:成为JSP页面转译成的Servlet类中的成员属性或成员方法
- 例如:
	<%!
		public String getResult(){
			//...
		}
	%>
-将代码整体插入到Servlet类中
	public class Index_JSP extends JSPBase{
		public String getResult(){
			//...
		}
		public void service()...
	}

JSP页面中的指令
- 语法规则: <%@指令名	属性=值 %>
- 常用指令: page指令,include指令,taglib指令
- 作用:控制JSP在转译成Servlet类时生成的内容

page指令
- 作用:用于导包、设置页面属性
- 例如: 
<%-- 导包 -->
<%@page import="java.util.*" %>
<%@page import="java.util.*, java.sql.*" %>

<%-- 设置response.setContentType() 方法的参数值 -->
<%@page contentType="image/gif " %>

<%-- 设置容器读取该文件时的解码 -->
<%@page pageEncoding="UTF-8" %>

include指令
- 作用:在JSP页面转换成Servlet时,能够将其他文件包含进来。可以包含JSP文件也可以是静态的HTML文件。
- 通过该语句能方便的在每个JSP页面中包含导航栏、版权声明、logo等。
- 语法:<%@ include file = "url"%>
- 例如:
	<%@include file = "header.html" %>
	<%@include file = "footer.html" %>

JSP页面中的隐含对象
- 什么是隐含对象:容器自动创建,在JSP文件中可以直接使用的对象
- 作用:JSP预先创建的这些对象可以简化对HTTP请求、响应信息的访问
- 隐含对象
 	1.输入输出对象:request,response,out
 	2.作用域通信对象:session,application,pageContext
 	3.Servlet对象:page,config
 	4.异常对象:exception
 - JSP页面中可以使用的隐含对象如下:
 	隐含对象			类型				说明
 	request			HttpServletRequest	请求信息
 	response		HttpServletResponse	响应信息
 	out				JSPWriter			输出的数据流
 	session			HttpSession			会话
 	application		ServletContext		全局的上下文对象
 	pageContext		PageContext			JSP页面上下文
 	page			Object				JSP页面本身
 	config			ServletConfig		Servlet配置对象
 	exception		Throwable			捕获网页异常

JSP注释

  • 然后呢,这个jsp中啊,也可以写注释,然后呢,因为这里面呢,可以直接写html,所以呢,html注释,<!-- ... -->,它是支持的,你可以写html注释,但是呢,你要注意啊,这是html注释,它只能注释掉html的内容,只能注释掉标签,那咱们现在呢,这个jsp文件里,它还可能有java,这个html的注释是注释不掉java代码的,那如果说你想,既注释掉java,又注释掉标签,怎么办,用这个注释<%-- 注释内容 --%>,这样写,百分号,百分号,这样。那这种注释,叫jsp注释,它是jsp特有的注释,能注释掉一切内容,那后面呢,我们看啊,如果说我们写的代码要注释一部分,看这部分里有什么,如果说有java,又有标签,那就用百分号这个,否则用html的带感叹号的也可以,总之呢,看情况。

JSP表达式

  • 然后呢,jsp中呢,可以写java代码,那么写java代码,3种情况,我们先介绍一下,了解以后,我们就写一个例子来看一看,演示一下,那么第一种情况呢,叫jsp表达式,那么jsp表达式呢,它的这个语法,语法规则是这样的,我们是在一个,<%= ... ... %>,尖括号,里面写东西, 是小于好,百分号,等于号,然后呢,百分号,大于号结尾,就是,这是一个对称的,小于号,大于号,两边是百分号,等于号是多出来的,然后呢,在这个符号里,我们写这个表达式,那这个表达式可以写什么呢,你可以写变量,可以写变量之间的运算,可以呢,写有返回值的方法,就调用有返回值的方法,总之呢,那么我们写完这些内容以后,这个就是,它能够把我们所写的内容,输出到浏览器上,这个JSP表达式,它的作用是用来输出的,可以输出一个变量,可以输出变量的运算结果,可以输出呢,方法的返回值,所以,这里面呢,写的是这样的内容。

JSP脚本和JSP声明

  • 然后呢,第2种情况,第2种写法叫JSP脚本,那JSP脚本,它书写的时候,我们是把它写到,这个<% ... %>,小于号,百分号之内,这回就没有等于号了,那在这里,我们可以写完整的Java的逻辑,比如说,你写个for循环可以,你写个if可以,你可以写任意的完整的一句java代码,但是一定要完整。然后呢,还有一种方式是,前面是一个感叹号,这个叫JSP声明,那我们可以呢,在这里声明这个成员变量,声明方法,然后呢,被当前的JSP所调用,总之,大概就是这样。就是我所学的JSP这项技术,我们在编写时,写的是一个特殊格式的文件,文件里面,html,css,js,直接写,这个文件里java代码有3种规则,表达式用来输出,脚本用来写完整的代码段,声明用来声明方法或成员变量,就这样。

2.JSP开发案例演示hello.jsp

案例需求

  • 那下面,我们就写一个例子来看,这么说的话,这个不直观,写个例子来看,一看就懂了,那这个例子就这样,就是说我想呢,用浏览器访问服务器,我希望呢,服务器给我返回一个动态网页,那我希望呢,这个网页上有什么呢,有个列表,然后呢,那列表中,有若干个li,每个li里呢,给我输出一个随机数就可以了,但这个随机数呢,我希望呢,是服务器生成的,不是客户端生成的,所以呢,得用java代码生成,那咱们来写这段逻辑,非常容易。那大家呢,打开发工具eclipse,那打开以后,因为咱们当前,JSP是一个新的内容,我们最好新建一个项目,那这个项目名叫jsp1,在这新建一个项目,也是web项目,项目名就叫jsp1,就可以了。然后呢,Packaging打包方式选择war,然后啊,完成。

代码设计与分析

项目创建

  • 完成以后,处理方式一样,它也报错,也需要点Generate...,另外呢,这个jsp,它也是JavaEE中的一部分,所以,我们也需要引入JavaEE的包,所以 ,我们还要依赖于tomcat下的那个包。点Generate...生成配置文件,然后呢,依赖tomcat自带的包,把项目建好。那下面,我们就写一个jsp,那写的话,这个jsp,因为它不是java,所以呢,不要把它写到Java Resources之下,然后呢,它看起来呢,更像一个html,因为它上来能写标签啊,所以呢,一般我们是把它写到webapp的下面,所以呢,展开这个项目,打开webapp的这个目录,那在这呢,我要创建一个jsp,那么它的名字随便取,我们写第一个jsp,就叫hello.jsp吧,简单,就这样。选择webapp这个目录,然后右键,new,new什么呢,有一个叫JSP File,new它的话呢,会自动创建一个后缀为jsp的文件,还带有一定的格式,但是呢,一般我们不用这个,不是,你建好了,你们每次都超出我的这个预期呢,怎么那么快呢,手速果然快,是吧,以后你收收你的神通,慢点啊,我还没说呢,你就。

JSP文件创建

  • 为啥呢,因为它创建的这个结构,它的代码其实对我们来说,没什么用,为啥呢,因为我说了,我们将来工作的时候啊,那个网页的结构,是美工给我提供的,我会把美工给我的那个静态网页的代码,直接粘贴过来,就完了,所以你创建一个JSP,带有这个结构也会被覆盖,明白这意思吧,就我们开发时,它自带的,自带的这些个标签,没有任何用,我肯定是从美工,给我的静态网页里,把它贴过来,后面的这个电信计费项目也会这么干。那所以说,没有必要创建一个JSP File,看的还闹腾,一大堆乱七八糟的东西,还得解释,不用看了,我们干脆呢,创建一个空的,干净的文件File就行了,就这样,就是在webapp之下,new一个File,然后呢,文件的名字任意,但后缀必须是叫jsp,那刚才说了,叫什么呢,hello.jsp

JSP指令代码

  • 然后呢,打开hello.jsp,那么这个jsp中,一般,通常我们第一句话,写的是一个特殊的内容,那这个内容,<%@page pageEncoding="utf-8"%>,那注意,写这句话一定要严谨,别写错字,这个不要多一个字符,少一个字符,主要大小写,注意空格什么的,一定要写对了,那这个东西啊,叫做标签,这个是jsp特有的标签,这个标签呢,比较特殊,以前没有见过,它是成对出现的,小于号,对应的是大于号,它用的是尖括号,然后呢,里面是百分号,这个jsp呢,喜欢百分号,然后呢,前面有个at(@),at之后呢,是单词page,page之后呢,是一个单词叫pageEncoding,等于utf-8,那这句话显然是声明编码,声明page的编码,那声明的是什么呢,声明的是当前的这个jsp,这个文件的编码,那么这个编码有什么用,在什么地方会用到,后面会详细说,现在先这么写着,一般,第一句话,就写这个。

代码分析hello.jsp

  • 写完这个以后,下面,后面就得写这个html了。那么正式工作的时候,做项目时,我们会从把美工给我们的代码,直接贴过来就完了,但现在呢,没有美工,我们先自己写一个,那html的基本结构,那先写什么呢,<!doctype html>,这叫什么呢,这叫文档类型,就是版本;然后呢,根元素<html>...</html>,然后呢才是,head和body。总而言之啊,这个jsp里,我们可以直接写html的标签,那如果有css,有js,也可以直接书写。那我们写的就是一个网页的基本的结构。那下面呢,我们再说一下,我们最终这个网页上,要显示什么内容呢,那如果我们这样写的话,它是一个固定的,静态的东西。我希望呢,它有动态的逻辑,那之前我也说了,我希望啊,这个网页上,有一个列表,列表之内有若干个li,每一个li里呢,要显示一个随机数,那这个随机数呢,需要由服务器,由java生成,不是js,那有人说,为什么是这个java生成呢,因为有些内容,还必须由java生成,这样安全,你比如说,将来我们做那个登录功能,一般登录功能上,都得有那个验证码,都有验证码,验证码就是由服务器生成,因为如果由客户端生成的话,这个容易被破解,就不安全。所以说,我们这个让服务器生成一个随机的东西,是比较合理的,验证码就是。

<%@page pageEncoding="utf-8" %>
<!doctype html>
<html>
	<head>
		<meta charset="utf-8"/>
		<title>第1个JSP</title>
	</head>
	<body>
	</body>
</html>

  • 那在这呢,我要写一个列表,那就写一个列表吧,那ul就行,不需要有序。然后呢,列表之下,要有若干个li,有多少个呢,这个多少个都行,比如说你10个也可以,20个也行,100个也行,无所谓,那你注意了,如果说li太多的话,手动一个一个敲有点麻烦,最好是怎么办呢,循环,循环之内输出一个li,循环100次,就100个么,这样方便,写循环。那这写循环呢,我们可以用java写循环,那在这,比如说,我想循环10次,然后呢,显示10个li,在这用java去循环,那怎么写呢,第一个内容,我们要演示的是,这个叫jsp脚本,那jsp脚本啊,它这个里面所包含的内容是完整的java代码段,是一句一句完整的java代码,if也好,for循环也好,这都是可以的。
  • 那因为我要循环,所以就得写jsp脚本了,那么jsp脚本的语法是这样的啊,<% %>,这样的,对称的,小于号,大于号,中间是俩百分号,在两个百分号之间写java代码,那为了书写方便呢,我把它这个换一行,咱们垂直写,别横着写,换个行。那我们在这个<% %>,符号之内写java,那我要写的是for循环,自己写啊,for循环,循环10次,这个for循环没什么好说的,就是java的语法,这里面就是java代码,然后呢,每次循环,我要显示一个li对吧,那我就写个li,你说我直接<li>这样写能行么,这显然不行,因为这里面是java,java没有这个语法,不让写,那怎么办呢,咱们变通一下,怎么办呢,这样做,在中间加了%> <%,相反的这样两个符号,什么意思呢,这本来是一段完整的java代码,我是强制把它拆成两段了,看出来了吧,拆成两部分了,那你看,那拆成的两部分,上面<% for(int i=0; i<10; i++){ %>是java,下面<% } %>,这部分是java,那中间,中间是不是还是html,我就可以直接写li了,写li。

<body>
	<ul>
		<!-- 1.jsp脚本 -->
		<% 
			for(int i=0; i<10; i++){
		%>
			<!-- 2.jsp表达式 -->
			<li><%=bai(Math.random())  %></li>
		<%
			}
		%>	 
	</ul>
	<%@include file="time.jsp" %>
</body>

  • 就是说这个,我们写jsp的时候啊,是标签里面套着写java,java里面再套着写标签,标签里面再套着写java,有人说,哎呦,这太麻烦了,这也挺麻烦的,其实不麻烦啊,这个我们套来套去,一般也就套个两层到头了,不会更多了,将来做这个电信计费项目时,也会深有体会,就通常的案例中,不会嵌套很多层次,这你放心,不会太复杂啊。那每次循环,我们输出一个li,那我希望li之内,输出一个随机数,<li></li>,这里输出一个随机数,那这个随机数呢,是由java生成,那我们在这个jsp里写java,要输出一个东西,显示一个东西,怎写呢,用第2项内容,这叫jsp表达式,那我想呢,就在li的内部,用这个表达式输出一个随机数,那表达式是这样写,表达式和脚本的语法差不多,只不过呢,它里面多了个等号,表示我要输出一个东西,然后呢,表达式内部,咱们不能写完整的java代码段,也不能写for循环,不能写if,它只能写变量,变量之间的运算,以及呢,带有返回值的方法,总之,我们意思呢,是要让它输出一个值,你得写一个值,能代表一个值的东西。那随机数可以,随机数是什么呢,是Math.random(),是这样的,这是一个有返回值的方法。
  • 我们先演示这两步,就可以测了。这个jsp啊,它还有一个好处,就是说,我们写完jsp以后,jsp不需要加以配置,直接就能访问,像静态网页一样,能直接访问,所以呢,它就比Servlet,要省事不少,那下面呢,我把这个项目部署一下,我们来访问这个jsp看看。部署项目,然后呢,启动tomcat。那部署完以后,目前我还没有去看tomcat,那想想看,猜猜看,这个hello.jsp,它在部署的代码的哪个地方,它应该在部署的代码的哪个位置,那部署的话,Eclipse会把webapp拷到tomcat下,改名叫什么呢,jsp1,那webapp拷过来,webapp之下是不是就有这个hello.jsp,所以,这个hello.jsp,它就在jsp1项目之下,那我们访问hello.jsp的话,就直接访问就可以了,不用去看,因为这个部署的原则,就是这样的。那我们打开浏览器,访问一下这个页面,localhost:8080/jsp1/hello.jsp,看一下:
    在这里插入图片描述
  • 我们一访问这个jsp,确实看到了,网页上出现了一个列表,列表中呢,有的是随机数,10个随机数,那注意,这个jsp,它和html还是不一样,那html是写死的,html内部是没有办法写java代码的,它没有动态的这个逻辑,那么jsp里呢,它可以写java代码,有动态的逻辑,有动态的数据,这些数据是动态的,所以有本质的区别,所以尽管说,两者有点相似,但是我们可以说呢,html是前端的,是客户端的程序,jsp是服务器端的程序,你别看jsp上有标签,那么jsp其实呢,是在服务器上,在tomcat里运行的,它不是在浏览器上运行的,当然,它运行的结果,是给浏览器,返回了一个网页,那返回的网页长啥样,咱们可以看到,在浏览器上,右键,查看网页源代码,那右键,查看网页源代码,我们能够看到html,就这样一个内容:
    在这里插入图片描述
  • 那你看,我们看这个网页源代码,这里面有那个JSP的相关的java代码么,没有,就是说,当我们看到内容时,我们看到的是一个静态网页的内容,那这个静态网页,从哪而来呢,它是由jsp生成的,听明白了么,就这个静态网页是jsp生成的,不是Servlet,我们先对jsp啊,有一个初步的了解,那么jsp原理呢,我们后面会详细讲,讲的时候,再去理解。那我们在写jsp的时候,除了有这个表达式和脚本,还有一种语法,叫jsp声明,那下面呢,再演示一下这个jsp声明,回到刚才的这个hello.jsp上,那么我想这样,我想呢,在这个hello.jsp上声明一个方法,java方法,这个方法呢,比如说是,求某一个小数的100倍,乘个100,100倍,就计算某个小数的,放大100倍,就这么一个方法。
  • 那这个方法呢,我在表达式里好调用,这样的,那声明的话呢,你在哪声明呢,我们在之前声明,在ul之前,找一个空白的地方。这是我们要演示的第3个内容,就是jsp声明,那么声明的语法是这样的,<%! %>,加一个感叹号,那么为了写代码方便,我把它换个行,然后呢,在这部分里面呢,我们声明一个函数,这个函数呢,返回一个小数,然后呢,它的作用是求出某一个小数的100倍,那这个方法名叫什么呢,叫bai吧,简单点省事。那你想求哪个数据的100倍,得把这个数传进来啊,所以有个参数,参数呢,也是一个小数,然后呢,这个方法,我们返回的是,这个传入的小数的100倍:

<!-- 3.jsp声明 -->
<%! 
	public double bai(double d){
		return d*100;
	}
%>

  • 然后呢,这个函数,我想在<li><%=Math.random() %></li>,这调用,就是我输出的小数,我想放大100倍,在这调用,所以我们在回到这个位置,在Math.random()之外,套上这个函数bai,<li><%=bai(Math.random()) %></li>,那么有的人呢,在写这个代码的时候,可能前面会报个红叉,有人会报叉,有人不会,我这就没有,这主要就看什么呢,看rp(人品)了,是吧,但这个叉,你可以无视它,没什么关系啊,没事,这个它不是什么错误啊,那为什么会报个叉呢,是这样,因为啊,咱们这个jsp的代码呢,它是标签套java,java套标签,标签又套java对吧 ,互相套,套来套去呢,把这个,eclipse啊,工具给套晕了,所以,开发工具在检查这个语法结构时,它有的时候被绕晕了,它认为是错的,但其实没错,明白吧,你确认没错就可以了,这没关系啊。那写完以后,咱们再试一下啊,你把这个项目呢,重新部署一下。那我们打开浏览器看一下,localhost:8080/jsp1hello.jsp刷新以后有变化么,确实放大了100倍。
    在这里插入图片描述

完整代码实现hello.jsp(项目jsp1)

<%@page pageEncoding="utf-8" %>
<!doctype html>
<html>
	<head>
		<meta charset="utf-8"/>
		<title>第1个JSP</title>
	</head>
	<body>
		<!-- 3.jsp声明 -->
		<%! 
			public double bai(double d){
				return d*100;
			}
		%>
		<ul>
			<!-- 1.jsp脚本 -->
			<% 
				for(int i=0; i<10; i++){
			%>
				<!-- 2.jsp表达式 -->
				<li><%=bai(Math.random())  %></li>
			<%
				}
			%>	 
		</ul>
	</body>
</html>

JSP原始语法的缺陷

  • 通过这个小例子吧,我们就初步了解一下,这个jsp的,它的语法,它的语法呢,大概就这样,写标签,里面写java,java里面再写标签,标签里面再写java,那标签的写法和html没有什么区别,那么java,不能直接写,我们必须呢,把java包含在不同的符号之内,那有的符号,叫脚本<% %>,有的符号,叫表达式<%= %>,有的符号叫声明<%! %>,那么这3种符号,大家就了解一下,那这3种符号呢,写起来也并不友好,感觉有点,有点不太舒服,有点恶心,感觉呢,别扭,但没关系,我们后面呢,会讲一个全新的内容,新的技术能够,能够把这个符号替换掉,我们将来工作时,一定不会直接写这样的符号,一定是用那个替代的方案,那个替代的方案呢,是el表达式,和jstl标签。那这个内容呢,有些书上呢,会把它放到最后讲,但是呢,我会把它往前提一提,就往前提一下,因为什么呢,我们工作时呢,如果用jsp,就一定会用这项技术,一定会用el,一定会用jstl,它这是很重要的东西,你放到最后一天讲不合适, 讲完之后就过去了,没有练习,不合适啊。

JSP声明的缺陷

  • 所以,我们提到前面来讲,讲完之后呢,我们后面做项目时会用,这样是比较合理的,那现在呢,先不说那个了,我们先了解这个脚本,表达式,还有这个声明,那么这3种特殊的,java代码书写的方式,其中呢,脚本和表达式,你要用的话,反正也可以用,但是呢,咱们工作时,一定不要用这个声明<%! %>,或者说尽量不要用声明,为什么呢,因为你要想声明一个方法,你干脆在一个类中声明,我们把这个类引到页面上,就是你要非得要声明一个方法,你写个类,在类中声明这个方法,我们引入这个类,调用这个类,一句话调用,不要在jsp上,写大量的java代码,因为毕竟啊,jsp是把标签和java,揉在一起了,产生了耦合度,是标签和这个java有耦合度了,所以这项技术呢,怎么说呢,逐渐的,也开始呢,出现了一个颓势,现在很多企业开始选择不用它了。但还有很多企业在用,所以说,原因是什么呢,就是耦合度高,是java和标签耦合度高。
  • 所以你看啊,这东西呢,这项技术吧,是Sun,它提出来的,是Sun发明的,这么牛的企业,这么牛的技术人员,它发明了一项技术,也是会由于这个耦合度的原因,会产生没落的时刻对吧,会产生一些影响,所以说这个很重要,这个耦合度,将来我们将框架你也知道,就是我们最后讲框架,讲Struts2,说Struts2框架呢,现在也开始就是,没落,大家更愿意用SpringMVC,为啥呢,因为那个框架里面的,组件的耦合度高,也是有这个耦合度的问题,所以呢,一个软件的耦合度呢,是至关重要的,它会影响这个软件的发展,一个框架,一项技术,可能是因为耦合度,会走向一个衰弱的程度,所以说,耦合度很重要。就总而言之吧,我们写jsp的时候,尽量别写声明,要声明,在类中声明,别再jsp页面里声明。

3.JSP指令详解

page指令简述

  • 那大概了解了jsp语法,我们再往下看 ,那么,刚才啊,我们在写这个jsp时,我们第一句话,写了一句特殊的一个东西,写了个这个东西,<%@page pageEncoding="utf-8" %>,这个东西叫什么呢,有一个学名,叫做指令,这个指令,还不止一种,有好几种,那每一种指令,它所代表的含义,它所表达的意思,不一样,那总而言之啊,指令是用来做声明,做配置的,它不是用来处理具体的逻辑的,那么指令呢,在什么时候会调用,咱们后面呢,讲原理时也会讲,那我们先了解一下,到底有哪些指令,就无论是哪种指令吧,它的语法规则是一样的,就是<%@指令名 属性=值 %>,比如,<%@page pageEncoding="utf-8" %>,其中属性等于某值,可以有多个属性,可以再写属性等于某值,然后,刚才我们写了pageEncoding是属性,utf-8是值,那这个指令有3种,一种叫page指令,刚才用到的,还有一个呢,叫include指令,还有一个呢,是taglib指令。3种指令,那先说前两个。第3个最后讲。

include详解1

include指令案例演示time.jsp之需求分析

  • 那page指令,刚才看到了,它用来声明这个文件的编码,那include是用来做什么呢,说一下,然后我们演示一下。我们在开发一些项目的时候,很多时候,项目中的很多网页,它的结构相似,你比如说,我们开发电信计费项目,电信计费项目呢,它的网页结构很相似,都是,这个比如说这顶部,有这个logo区,然后呢,下面有这个导航,然后再往下有内容,最后,有版权,之前做的那个管理员列表页面,不就这样么(web部分),那它就是电信计费项目中一个具有代表性的网页。总之啊,我们这个一个软件内,很多网页结构是相似的,简化一下,没有导航区,比如说,就logo区,内容区,版权区,就简化一下。那很多网页结构一样,然后呢,比如说,我这个项目有要求,我要求,在这个logo区的右侧,统一的给我显示,当前的日期,或者说时间吧,当前的时间,要显示时间,那你想啊,咱们每一个网页结构都一样,那每个网页都要显示这个时间,是吧,都要显示时间,那我们要显示时间的话,应该显示服务器时间,还是客户端时间呢,一定是服务器时间,因为之前说了,客户端时间不准对吧,不一定准。
  • 那这么多,就是我们做一个项目,所有页面结构都一样,都要显示时间,你最好别写那么多次对吧,你要是每个网页这么写一遍,太麻烦了,是这样吧,最好是能够让这段代码能够复用,所以我们可以这样,我们可以呢,单独写一个jsp,比如说,就叫date.jsp,或者time.jsp吧,时间吧,time.jsp,然后呢,最好是能够让这个time.jsp,被其他的网页所复用,最好是能够让time.jsp复用,那可以这样,我们可以呢,让我们所写的网页引用,或者说依赖这个time.jsp,复用它。那这样的话呢将来,如果说需求发生变化,我们不用说每个页面都改一遍,我们只要改time.jsp就可以了,你比如说将来,需求发生变化,说时间的前面再写上,这个用户,用户名,我们改time.jsp一处就可以,而不用说,每个网页都改,因为你要知道,我们在开发时,就是像这样的网页有很多的,那它都共用一个jsp就好了,那我们怎么去把这个time.jsp,引入到其他的这网页jsp上呢。用一个指令啊,就叫include,所以include,它的作用就是,能够把一个jsp,引入到另外的一个jsp上,是这样一个作用。

page指令详述

page指令pageEncoding

  • 那下面呢,我们就写一下,演示一下,大家体会一下,我们就写一个time.jsp,然后呢,把这个time.jsp,引入到哪个页面上呢,咱们目前只有一个jsp,目前只有谁呢,就是hello.jsp,那我们就以hello.jsp来代表我们项目中的,众多的页面当中的一个吧,把time.jsp,引入到hello.jsp里。那再打开eclipse,那么我呢,在项目jsp1,在webapp下,创建一个新的 jsp,叫time.jsp,那么这个jsp,它的书写的方式,和hello.jsp是相似的,首先呢,我们也是要写什么呢,写那个page指令,我们也要声明这个文件的编码,那写一下,<%@page pageEncoding="utf-8"%>,然后啊注意,这个指令,它不止这一个属性pageEncoding,还有别的属性,还有属性呢,书上有介绍,看一下:

page指令
- 作用:用于导包、设置页面属性
- 例如: 
<%-- 导包 -->
<%@page import="java.util.*" %>
<%@page import="java.util.*, java.sql.*" %>

<%-- 设置response.setContentType() 方法的参数值 -->
<%@page contentType="image/gif " %>

<%-- 设置容器读取该文件时的解码 -->
<%@page pageEncoding="UTF-8" %>

page指令属性contentType

  • 它还有一个属性叫做,contentTypecontentType这个单词熟悉么,这是什么呢,这是服务器向浏览器输出的内容的格式对吧,就是这个意思,那这句话的意思是什么呢,是我这个jsp,向浏览器输出的到底是什么,默认值就是html,所以这句话,其实可以不写,因为不写默认就是网页,那咱们写一下,了解下,写一下吧。那再写个属性啊,叫contentType,<%@page pageEncoding="utf-8" contentType="text/html"%>,我把这两个属性呢,解释一下,先大概的解释一下,后面讲原理时呢,还会详细解释。在前面写个注释啊,这两个属性当中的pageEncoding,是什么呢,是声明此jsp文件的编码,然后呢,contentType是声明此jsp项目,浏览器输出的格式,输出的内容格式。那这两个属性,我们讲原理时,还会再说,再强调,先简单了解一下。

page指令import

  • 那除了这两个属性之外,还有一个属性,那还有个属性叫importimport是导包,那么如果你在这个jsp里想用哪个类,就要导哪个包,想用什么要导什么,那咱们这个jsp上,我们需不需要导包呢,需要导什么包呢,需要导啥包啊,那个data,为啥导date呢,因为我们要输出时间对吧,是服务器时间,你得导一个date,java.util.Date,那我就导了,我也可以写成java.util.*,我就为了省事,为了简短,写个点星,你写点Date,也没关系。

<%@page pageEncoding="utf-8"  
contentType="text/html"
import="java.util.*,java.text.*"%>

include详解2

include指令案例演示time.jsp之代码分析

  • 那除了导入date以外,还用导别的东西么,还需不需要导别的内容呢,还要导入SimpleDateFormat,因为你的日期得格式化对吧,那么SimpleDateFormat,这个包名还知道么,我们再导第2个包,逗号隔开啊,那个报名是java.text.*,这个包名,你不用死记硬背,没记住也没关系,如果你想知道包名也很容易,我们可以呢,点这个图标(Ctrl+Shift+T),弹出框搜,SimpleDateFormat这个类对吧, 搜到以后一看就知道了,所以这东西啊,简单了解,不用记啊。
  • 那包也导完了,那下面我们就可以呢,输出时间了。然后呢,我们要写出这个网页结构,那其实呢,这个网页结构不用写完整,我只需要写出body中的内容就可以,那比如说,我想把那个时间输出到段落里,我就写个段落就行,那大家肯定会想,那为什么没有写完整这个结构呢,你看,就是这个time.jsp,它并不是单独访问的,它是作为其他的网页的一部分而存在的,是这意思吧,time.jsp,就是其他页面的一部分,是这意思吧,对吧,我们在用时会把它引入到别的页面上用,把它作为别的页面的一部分,那其他的页面是不是结构都有了呢,都有了,所以它不用写完整,写一个段落啊,一个div就可以了。那它这样啊,我在段落之内,最终的话,是要输出时间的,那我要输出什么内容,得写,这叫什么呢,<%= %>这叫表达式对吧,那表达式要输出变量,或者是运算,或者是返回值的方法,我们得有这个东西对吧,因此呢,在此之前,我们还得呢,得到当前的时间,那么在此之前得到当前时间,可以写什么呢,<% %>,这叫什么呢,这叫jsp脚本,是这样吧,脚本,因为你要new Date,你要格式化,这是完整的java代码段,需要在脚本里写,那我就写一下,就是new Date,没什么好说的。

<%
	Date d = new Date();
	SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
	String time = sdf.format(d);
%>
<p><%=time %></p>

  • 好了,创建了Date,对它进行了格式化,我们最终得到了一个字符串,叫time,我们输出的就是time这个变量,就行了。然后呢,下面,我们要把这个time.jsp,引入到hello.jsp里,那我们再回hello.jsp,那么我想把这个时间呢,放到哪呢,放到这个列表之后吧,在这个列表以后,<ul></ul>之后。那么你想引入jsp,作为当前的jsp的一部分,用一个指令,这指令叫include,<%@include file="time.jsp" %>,那么file里面呢,你要写上那个,你要引入的那个文件 的路径,那因为time.jsphello.jsp在同一级目录下,平级,所以直接写文件名就可以了,那完成以后呢,咱们可以测试了,把这项目呢,重新部署一下,然后呢,打开浏览器,再访问一下hello.jsplocalhost;8080/jsp1/hello.jsp,访问以后呢,发现,这个hello.jsp下面,确实有了个时间,没问题。
    在这里插入图片描述

include指令案例演示之存在的问题

  • 有人会想这个时间,怎么没变化呢,为啥没变化呢,因为你看啊,我浏览器,这一回车,访问服务器一次,得到一个时间对吧,那如果你想时间变,是不是得再访问一次,再得到一个时间啊,你刷新就变了,有人说得,我这个手动刷新,累死我了是吧,而且每次刷新,那个数据都变了,对吧,那怎么能做到说,这个网页它的内容不变,只有时间变呢,或者说,在网页不刷新的前提下,让时间发生改变,怎么做呢,当前还没学过,要做这件事,必须得学会一个新的技术叫做ajax,这个ajax会在,我们讲完Spring框架之后讲解,那个内容也很简单,就两天时间,内容就搞定了,所以到那时候自然就会了,先别着急。

include指令案例time.jsp完整代码实现(项目jsp1)

webapp目录下的time.jsp
<!-- 
pageEncoding:  声明此jsp文件的编码
contentType:  声明此jsp向浏览器输出的内容格式
 -->
<%@page pageEncoding="utf-8"  
	contentType="text/html"
	import="java.util.*,java.text.*"%>
<%
	Date d = new Date();
	SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
	String time = sdf.format(d);
%>
<p><%=time %></p>
webapp目录下的hello.jsp
<%@page pageEncoding="utf-8" %>
<!doctype html>
<html>
	<head>
		<meta charset="utf-8"/>
		<title>第1个JSP</title>
	</head>
	<body>
		<!-- 3.jsp声明 -->
		<%! 
			public double bai(double d){
				return d*100;
			}
		%>
		<ul>
			<!-- 1.jsp脚本 -->
			<% 
				for(int i=0; i<10; i++){
			%>
				<!-- 2.jsp表达式 -->
				<li><%=bai(Math.random())  %></li>
			<%
				}
			%>	 
		</ul>
		<%@include file="time.jsp" %>
	</body>
</html>

4.JSP运行原理

JSP运行的完整流程(宏观)

  • 那咱们了解了这个jsp,它的基本语法,那jsp比较简单呢,语法就这么点东西,就差不多了,当然也没有都讲完,剩下的内容呢,主要是讲它背后的原理啊,以及一些改善的这些方面,那么它的基本语法就这么点东西,那么,按照书上的这个进度,下一个话题,它说的是,jsp隐含对象,然后呢,在这个jsp隐含对象之后,它讲的话题呢,是jsp运行原理,那我认为啊,这两个话题应颠倒一下,先讲原理,再讲对象,因为我们通过原理,能够阐述那个隐含对象是什么,先讲隐含对象这个比较,比较怎么说呢,比较生硬,比较唐突,所以我把这个顺序颠倒一下,我先说jsp的这个原理,运行原理。然后呢,书上呢,也是给我们画了个图啊。不过它画的是流程图,这图啊,反正就是几个框,几个代码,感觉不舒服,我还是重画吧,我们画图呢,阐述一下jsp运行的原理,就是讲一下,那浏览器访问服务器,服务器如何通过jsp,给浏览器拼一个动态网页的,那总体而言呢,jsp的作用和Servlet一样,那其实呢,jsp的运行的过程,和Servlet的运行过程,差不多,几乎是一样的,只有一点微小的区别,那么我们把重点放到那点微小的区别上去,但是呢,整个流程我还是画一遍,咱们还是呢,这个直观的体会一下。
  • 那么,要想讲这个原理啊。它是在web项目当中去讲啊,所以呢,我先画个框, 代表浏览器啊,再画一个框,代表服务器,那么浏览器,要访问服务器,实现了一个交互,然后呢,服务器给浏览器返回呢,它想要的动态的网页,然后呢,浏览器和服务器之间呢,它们的交互,靠谁去交互呢,靠谁去通信呢,这两者之间的通信,依赖的那个东西叫什么呢,通信组件,和这个Servlet差不多啊,就叫通信组件。那这个名字,我不写也可以吧,我一画,你一看就明白了,因为Servlet也是一样的,就不写字了啊,麻烦。浏览器有通信组件,服务器也有,然后呢,浏览器啊,我们比如说输入了地址,或者说我们点了按钮,就可以对服务器呢,进行访问,那么访问的时候,还是那样的步骤,首先呢,浏览器和服务器怎么样,建立连接,其次呢,浏览器要对数据进行打包。
  • 什么叫打包呢,什么叫数据包呢,所谓的打包,就是浏览器把想要发送给服务器的数据,进行一个组织,组织成一个标准的,具有一定结构的字符串,那这个结构,是由这个w3c规定的,那至于到底是什么结构,咱们在那个network里能看到,就有什么内容,network里都有,那如果你想看最原生的,最原始的,那个结构,你得看文档,还是那句话,看文档,那一般你也不用看了,知道就可以了,大概了解就行了。这第一步啊,建立连接,第二步打包,打包数据以后,数据要发送给服务器,对吧,发送时因为,它打包时拼的,组织的是一个字符串,对吧,发送的时候,要转成byte对吧,要转换,转换以后呢,服务器,接收到数据以后,怎么办呢,拆包,拆包呢,是要把,这个byte,再还原为字符串对吧,因为它给我们的是字符串,不应该是给我们byte,byte不知道该怎么处理了,所以,第4步呢,是拆包,因为呢,这几步和我们Servlet原理都一样,所以这个话,我就不写了,就这样了。
  • 那请求到达服务器端以后,服务器怎么处理请求呢,也是跟Servlet的一样,首先创建俩对象,创建什么呢,request和response对吧,就是说我们后面的程序所需要的东西,服务器它很自觉的,很主动的,把它创建完毕,我们后面就可以用,然后呢,这个服务器啊,帮我们创建了request和response,然后它会把数据,把它拆包得到的数据,放哪去呀,存入request,存到request里,那标一下啊,这是第五步new,然后,这是第6步,第6步,我得写个字,要不然这个很别扭啊,就存,有人说,怎么叫存呢,往对象里存东西就那么几个办法,一个是new的时候,构造器,往里传数据对吧,一个就是set,对吧,不过就那么几个办法,还能怎么样呢,对吧,那么,它必然是采用某一种办法,具体我们也没细看,如果你想知道的话呢,可以查那个手册啊,或者看一看什么相关的这个资料,看一下。
  • 总之呢,往request里存入了数据,存入了数据以后呢,对象也有了,然后最终,它要调谁呢,注意,它不是调Servlet了,它是要调谁啊,这回是要调jsp,是这样吧,调jsp,比如说,我们刚才的案例中的hello.jsp,要调用它啊,那么tomcat呢,它会很自觉的,在它的目录下去找到这个文件,所以这一步啊,是找,不是直接调用,是找到它,是寻找,也很好找,咱们不就是把jsp放到了这项目下,对吧,那一找就找到了,那非常容易,找到以后你注意啊,这个jsp啊,它很特殊,它不是一个java文件,它也不是一个html,它能直接运行么,这个文件没法运行,没法运行怎办,这个Sun是这样处理的,或者说,Sun要求啊,这服务器是这样处理的,怎么处理的呢,这个tomcat,它会把这个jsp,进行一个翻译,或者说,进行一个转换,它会把这个hello.jsp,转换为一个HelloServlet,它把这个jsp转换,或者叫翻译成了Servlet,这个我们就叫翻译吧,或者说,你在jsp中写了什么东西,它相应的会生成一个Servlet,Servlet当中呢,就有什么东西,所以这件事我们可以形象的说,叫翻译,通俗讲叫翻译。
  • 当然了,这个翻译的这件事,咱们一会,一会我带着你看一下,一看你就明白了,先这么说着,这是第8步,翻译。那jsp呢,翻译成了Servlet,然后呢tomcat,它最终调谁呢,调Servlet,而且调用Servlet时,它会把request和response,统统的传给Servlet,那这是第9步,传入相关的参数,调用这个Servelet,这一步呢,是调用,标一下,调用。所以呢其实,我们现在写的是jsp不假,但是呢,本质上,tomcat调的还是谁呢,还是Servlet,换汤不换药,这是一个幌子,这是个幌子,这是Sun啊,它给我们一个善意的欺骗,表面看起来,这是一个新的技术,其实本质还是Servelt,这个jsp的本质,就是Servlet,所以这也是为什么,我们之前,一定要讲Servlet,因为jsp是依赖于它,这东西不讲,不讲明白的话,你jsp即便会用了,它底层的原理,你是理解不上去,这个理解不到位。所以我们先讲的是Servelt,然后呢,由Servelt向浏览器做出响应,将数据呢,发送给浏览器,当然发送的时候,这个服务器会对数据呢,打包,然后呢,这个浏览器呢,会对数据拆包,还是这样一个过程,顺序标一下,这是第10步,输出,11步,打包,12步,发送,13步,拆包,14步,关闭。
    在这里插入图片描述

JSP的设计理念

  • 那和以前,咱们上次讲的那个Servlet相比,多了一两步对吧,主要差在哪呢,就这,第七步,找到jsp,还有第8步,翻译。那以上就是jsp,它的工作过程,它的运行原理,那么说完这个过程以后,我们最终得到一个结论,什么结论呢,概括一下,就是jsp的本质,就是Servlet,那这个结论,我在这个前面写一下吧,jsp的本质就是Servlet。那有人想,它为什么这样呢,为什么这么玩啊,这个玩法有点高级哈,其实这个玩法还真的很高级,这体现了Sun的这个,程序设计者的这个,设计师的这个,怎么说呢,不是说严谨,是这个思维的灵活性或者它的高明之处,就在于此。是这样啊,就是说,当时呢,就是多年前吧,那个时候(2000年左右),互联网刚兴起,大家都上网了,一开始网页都是静态的啊,看个新闻,看篇文章,就这个,后来呢发现,这个满足不了需求了,对吧,开始出现动态的。那那个时候呢,Sun算是首先一批呢,开发动态,支持呢,做动态网页的厂商之一,技术之一,但是呢,它这个Servlet推出以后啊,是能够动态网页不假,太麻烦了对吧,咱们也体会了,你要做一个极小极小的网页,还凑合,你要做一个稍微复杂一点的网页,就要命啊,所以说,这个Sun一看,也确实不行,这个各个企业用的时候,反映也不行,说你这么玩,这个太麻烦了,这早晚得不行。
  • 所以Sun呢,也要寻求解决的办法,与此同时啊,在市场上,已经有一项技术,是比较成熟,比较ok了,体验也不错,那项技术,asp,属于微软,当年呢,那个年代,大家做网站,动态网页,一般都用asp去写,为啥呢,因为ASP的写法,和这个JSP有点相似,比较方便,比Servlet方便多了,所以,如果在那个时候Sun,不赶紧做出改变的话,它早晚都会被淘汰,明白吧,很容易就,一两年之内,大家都不用,它就会被淘汰了,所以Sun呢,得急于做出改变,那它就推出了jsp这项技术,说白了jsp啊,参考了asp的想法,明白么,参考了一下。那么,参考的时候,这个周期还得短,如果说你说我这个,技术推出来以后,搞个一年,有点晚了,明白吧,你这技术开发一年,人家已经用asp,已经用的很习惯了,不会再用你了,怎么办呢,还得快,Sun呢于是,既然时间短,还要解决问题,然后呢,最好还要不打脸,以前我开发的这个Servlet,大家都不用了,这不打脸么对吧, 怎么办呢,一结合,哎,我让你写jsp不假,但我运行时,底层是用Servlet处理的,那我以前呢,这项技术也用上了,没打脸,然后呢,用户体验又提高了,也能够跟你 ,微软相抗衡,明白这意思吧,一举多得,最终得到这么一个结论。

软件设计的基本原则

  • 所以呢,其实,我们去企业开发啊,无论是开发一个原创的技术,还是说,我们开发一个,一个什么东西,一个什么什么软件,那么很多时候呢,都要考虑成本的问题,其实这里面,最关键的就是成本的问题,那如果说我们开发一个理想的软件,应该是不限时长,不限成本,就是随便开发,以这个完美为主,但是呢,很多时候,我们没有这样的这个条件,很多时候呢,我们的客户要求我们,你这个软件成本还得低,人家报价报50万,我只给你30万,你又得给我开发完,然后呢,你说开发半年,我必须4个月搞定,这不断的压缩我们的这个成本和时间,就是我们必须得节约成本,所以说,很多时候呢,我们没有办法啊,这个,按照我们这个真实的想法,按照我们这个,最想要的那种方式去开发。
  • 我在一开始,早先做程序员的时候,也是有这样的一个困扰,就是我在开发的时候,我就看项目经理很不爽,你怎么老整这些恶心招,然后的话,糊弄客户呢,我有这种感觉,我就感觉很鄙视它,我说作为一个技术人员,你不合格,我心里就这么想的,我整天跟他吵架,我说你不能这么写,你这么写效率多低呀,你这对人家不负责任啊,你怎么能这么干呢,你应该像我这样干,你像我这样干效率高啊,多合适啊,对吧,项目经理还嘴笨,说不过我,你就这么干,你别那么整,说不过我,不行,然后,反正你说服不了我,我就得跟你吵,我就想按我这个方式干,然后,后来有一次呢,部门经理这个,这个给我们聚餐,部门经理说,说洪鹤不错,洪鹤挺负责任的,一天老是跟项目经理吵架,那也是,你吵架不是个事,主要是,说明你这个,确实很在意这个事,很上心呢,很有责任心呢,挺好。
  • 但是,项目经理嘴笨,没有跟你说清楚,跟你说一遍啊,说,如果就客户啊,给我们的,就是怎么说呢,我们接这个,这个项目啊,比如说,我们给客户的报价是50万,那么按照项目经理的那种办法,做完之后,客户也能接受,那我们只需要呢,花出这个,大概二三十万的成本,我们还能赚个二三十万,如果按你的办法的话,我们这个开发周期得长,因为你的办法麻烦,我们周期得长,周期一长的话,我估计我们的成本就得达到四十万,甚至50万,搞不好得赔钱,洪鹤,你说我们怎么办,我说,啊,那还得按照项目经理的来,这成本呢,你不能追求完美啊,我们是为了赚钱,又不是为了干什么,是吧,一想也是,其实,你回到这个,这个原理中来也是这样。那么Sun当时做的时候,也是要考虑成本的,它的成本是什么,时间的成本,明白吧,它必须很快的推出一项技术解决问题,不然的话,你开发一年两年,那么慢,不行。所以呢,它给我们做了一个典范,就是在那个场景下,它做出这样一个选择,是很明智的,而且也很好啊,这样的话,以前的技术Servlet也用上了对吧,这也不是说这个东西做完了之后,没用,打脸了,没打脸,然后呢,这样的话,时间又短了,挺好。

page指令的3种属性原理解析

  • 总而言之啊,就是说,jsp它的本质就是Servlet,整个的过程,和Servlet运行原理几乎是一样的,那整个过程,也是基于什么呢,基于这个http协议啊,也是这样。好了,然后呢,在这个原理之下啊,我们再,多解释一点内容,解释什么呢,咱们之前,讲这个指令啊,讲这个page指令,你看,在这个指令上啊,有这么几个属性:import,这很好理解对吧,没什么好说的,那pageEncoding和contentType,这两个,我们需要说一下:

<%@page pageEncoding="utf-8"  
	contentType="text/html"
	import="java.util.*,java.text.*"%>

  • 首先说呢,pageEncoding,它这个属性,声明编码,是在什么时候用,那这个编码其实呢,是在翻译的时候用,那所谓的翻译,就是什么呢,tomcat一看,找到了hello.jsp,这里有个jsp,那我要调的是Servlet,我需要自己写一个Servlet,明白吧,说白了,我们程序员懒,不爱写这个东西,tomcat帮我们写了,明白吧,但它写这个类的依据是jsp,所以,我们有什么jsp,它就会写一个什么Servlet,那这个过程我们,通俗来讲叫翻译,那翻译的时候啊,它得一边读jsp,一遍写Servlet,是这意思吧,读一句,写一句,你有什么逻辑,我就写什么,那它读jsp的时候,jsp是一个文本,需不需要知道,它的编码呢,需要,你要不知道编码的话,jsp这里面的编码不清楚,它就读错了,对吧,一定是要知道编码的,所以,我们第一句话,写的pageEncoding,是在翻译时,读这个jsp时用的,明白吧,是这样的,标一下。
  • 那这个pageEnconding,这个属性,是在翻译时,tomcat读取jsp文件时,需要用到的,这个必须得写,必须得有,那除此以外呢,还有一个属性叫contentType,那你想啊,jsp最终被翻译成了Servlet,Servlet要向浏览器输出内容 ,我们是不是得写一句话,setContentType呀,比如res.setContentType("text/html;charset=utf-8");,是这意思吧,那句话从何而来呢,我们也没写啊,那你注意,我们在程序中所写的这句话,contentType="text/html"就是告诉tomcat,我的输出的格式是什么,明白吧,那tomcat呢,它在生成这个组件的时候,就会写response.setContentType("text/html");,所以说,我们写的那个contentType那句话,就等价于response.setContentType("text/html");,而这句话呢,最终目的是要告诉浏览器输出的格式,所以contentType,是要告诉浏览器,最终输出的格式,是给浏览器用的,那在这标一下吧,contentType是给浏览器使用的,浏览器要知道格式。
    在这里插入图片描述
  • 然后呢,注意,contentType="text/html",这句话呢,一般可以省略,为啥省略呢,就是省略时,就默认值啊,默认值是什么呢,是text/html,另外呢,它也有默认的编码,比如charset="utf-8",它默认的编码charset和pageEncoding一致,就是说,它的后半句啊,默认编码和JSP中page指令中声明的pageEncoding一致,那你想,这句话contentType,我们一般还用写么,不用了,因为我们一般,肯定是用它来输出网页,对吧,一般都是输出网页,就这个;然后编码的话,page指令中的pageEncoding这句话写了,contentType的编码就默认跟它一致就可以了,对吧,你也就不用写了,所以,在JSP中的contentType这句话,通常是要省略的。

JSP翻译成Servlet原理详解(微观)

  • 那么,这样我们就把这个jsp的运行原理说完了,反正归根结底吧,jsp本质上就是Servlet,那我先把这个图呢,先存一下,那么,我们得出这样一个结论,这个有依据么,我们得看一看这个依据加深印象,让你放心,让你踏实,有的同学不看到证据,就老是感觉不踏实啊,我们去找找证据啊。那么tomcat,它依据jsp,生成的Servlet,是事实存在的,就在硬盘上,我们可以找到的,在哪呢,它就在tomcat里,咱们去tomcat去找,那大家呢,打开你的tomcat,找一下,那么生成的这个组件,生成的这个类,它呢,不在wtpwebapp下,它生成到哪去了呢,在work里,你打开tomcat,再打开work,work下有一个目录叫Catalina。
  • 打开以后,哎,老外命名可有意思了,老外命名的话,一般都是,他老婆叫什么名,他女儿叫什么名,他家狗叫什么名,他家猫叫什么名,都是这样,都是啊,你像tomcat,他家的猫叫Tom, 啊,tomcat,这个Catalina,就指不定是谁了啊,不知道,然后呢,里面有localhost对吧,打开localhost,localhost里面有啥呢,有项目名对吧,我们打开jsp1,jsp1下有什么呢,org/apache/jsp/,看,这里面有啥,看一下啊,你看这个目录啊,是work,很深啊,这个目录很深啊,work/Catalina/localhost/jsp1/org/apache/jsp,它为啥把路径搞这么深呢,因为它不想让你知道,因为啥呢,它不想让你知道这个里面,居然还是Servlet,它就想让你以为,这个jsp是一个新的技术明白吧,但是呢 ,我们还是把它抠出来了啊。
    在这里插入图片描述
  • 那你看,它里面生成的东西啊,一个是hello_jsp.java对吧,很明显,是将hello.jsp,生成这个类是吧,它的命名有一个规则,然后呢,hello_class是翻译成的,字节码文件,我们看这个java文件,看里面有啥啊,看看都生成的东西是什么,我用编辑器啊,把它打开,把hello_jsp.java打开,你看,你看这注释啊,什么Generated by the Jasper Componet of Apache Tomcat...,这个文件是由tomcat中的一个组件生成的,是吧,大概是这意思,版本是tomcat/7.0.67,时间是什么什么啊,大概这意思啊,注释,然后呢,后面有很多代码,这个我们阅读这样代码的时候啊,就不要过细,不要过细,你不要说它引入哪个类,我就必须把那个类看明白,那糟了,你看它引入的是javax.servlet.*,点星是吧,这里面有好多个类,你要把它都看明白的话,那太累了,看它大概的思路,然后呢,你看呢,这个类它继承于谁呢,继承于谁呢,注意,继承的不是HttpServlet,是继承于HttpJspBase,org.apache.jasper.runtime.HttpJspBase,这个东西啊,其实它和HttpServlet是,几乎是一样的,但它为什么没有直接继承于HttpServlet呢,它也有考虑。
  • 它有考虑,怎么考虑的呢,再说一下,就是说,你看啊,Sun它有两套技术,一套技术就是Servlet,那里面有个父类叫HttpServlet,另一套技术是jsp对吧,jsp呢,它搞了一个类,它不叫Servlet,它叫HttpJspBase,这么个玩意,其实这玩意的代码,和HttpServlet它几乎是一样的,那它是这样想的,它说那万一将来jsp没发展好,万一还要回归到Servlet这条线上呢,明白这意思吧,它怕互相有影响,所以它直接copy出一份,然后的话呢,这个,我要改什么的话,互不影响,它是这么想的,但其实这个东西啊,跟他几乎是一样的,然后呢,这个,咱们的Servlet就继承于,继承于HttpServlet,然后呢,我们的这个jsp所生成的Servlet,我叫JspServlet,继承于HttpJspBase,如果说呢,你要专门对jsp做某些处理的话,在HttpJspBase这个父类里写,不会影响到原来的东西,明白吧,就两面互不影响,它怕互相影响,或者说万一将来HttpServlet里面要加什么东西的话,对jsp也有影响,所以,虽然说呢,是这个,JspServlet它本质上是Servlet,但是呢,它做成了两套,不是一套,是为了避免互相,两条线有影响,因为毕竟,这两项技术都能解决问题。
  • 再看啊,总之呢,JspServlet它继承的这个父类HttpJspBase,相当于就是HttpServlet,然后呢里面,你看它有这个有方法,咱们之前写的那个bai,那个方法是吧,在这啊,但这个我们不管了,因为我说了,一般我们工作时,你别去写这个声明对吧(不去写JSP声明,jsp是把标签和java,揉在一起了,产生了耦合度,是标签和这个java有耦合度了),jsp声明,这东西就当它不存在。然后再看,这里有很多方法我们,我们不细看了,我们看关键的,后面呢,大概是在,哎,你再看,它里面60行,63行左右,也有init和destroy对吧,即_jspInit()_jspDestroy(),也有这样的方法,结构都一样,就是改了个名啊,换汤不换药。然后呢,66行一个方法叫_jspService,那这个其实和咱们以前写的service一样,然后呢,_jspService方法的参数,你看一下,一个是request,另外一个response,是这样吧,参数一样,结构都一样啊。
  • 然后往下看,看这个方法里面的逻辑,那这个方法呢,首先呢,它声明了一大堆变量,这个变量的名字,我们先看一下,有个初步的印象,后面会讲啊,你看,有一个变量叫pageContext,有个变量叫sesion,还有叫application,config,out,page,是吧,等等,它有这么几个变量啊,后面我们会讲,先有一个初步的印象,一点点印象就可以,所以呢,这个方法的一开始,是声明一堆变量,然后紧接着,它是给变量赋值,紧接着这块,是给变量赋值,不用细看了啊,大概就这样。再往后,那是输出了,因为呢,Servlet它要向浏览器输出东西,你得输出对吧,得用流,所以下面呢,它用这个流,往浏览器呢,输出东西,你看输出的这个形式啊,对于普通的字符串,它是write,这个write就是输出普通字符串,然后你再看,你看对于这个,咱们写的,在jsp脚本里写的for循环,这个for循环,它保持不变对吧,保持不动,没有write,然后呢,对于我们写的什么Math.random(),这个jsp脚本和jsp表达式,它做了什么处理呢,print和write,那这个write和print,有什么区别呢,write输出呢,普通的文字,字符串,print,可以print这个变量,是这样的,它是做了一个区分,其实你都用print也可以,它是特意这样处理了一下,有个区分,write这是静态的,out.print(bai(Math.random())),这是print变量,以此类推吧,大概就是这个意思啊。
  • 那么通过呢,看这个文件,我们就验证了一个结论,刚才说的一个结论,就是说jsp,它本质上就是Servlet,那么tomcat,在运行时,它没法直接运行jsp,它是运行,翻译所得到的那个Servlet,还是由Servlet来处理请求,做出响应的,是这样的,那么tomcat只是翻译Servlet的一个依据,那么刚才呢,我们所画的这个图啊,是一个大概的,大概的流程,那整个流程当中呢,最关键的一点,是在于第7,和第8两步,那尤其是第8步翻译,那这一步呢,刚才看了,但我们需要再归纳总结一下,我们归纳一下,那tomcat翻译这个jsp文件,它的基本的规则,和基本的步骤是什么,通过这个基本的规则和步骤,我们能得出一些结论,主要是出于这么一个目的。下面呢,我再说一下这个局部的细节,再画一个图,只说一下这个翻译的过程,现在我说的话题啊,是那个局部,就是jsp找到了,它是怎么翻译成Servlet的,这个翻译的过程。
    在这里插入图片描述
  • 那首先呢,我画一个方块,这个方块呢,代表什么呢,代表jsp,hello.jsp,那么我们说呢,hello.jsp它是,就jsp吧,别hello.jsp了,就jsp啊,它是怎么被翻译成Servlet组件的呢,那么jsp中有什么呢,它有的是,你看它有的是这个,有标签吧,是吧,它有标签,这是静态的内容;还有什么呢,它还有这个,脚本,是这样吧,还有什么呢,表达式,当然还有声明,声明我就不想探讨了,我们工作时,一般不让写声明,那玩意不好,如果你想声明,写个类声明,然后呢,import明白吧,这样的话,代码少啊,就是jsp中的java代码少,不要直接写声明。总之啊,我们平时所开发出的jsp中啊,常规的内容就是,标签,脚本,表达式,那这些内容是怎么被翻译过去的呢,它最终被翻译成了Servlet,我们讲一下这个翻译的过程啊。
  • 那翻译时,大概是如下几步,第一步,是tomcat首先呢,根据jsp创建一个类,第一步是创建一个java文件,这是第一步,因为本来硬盘上没有这个文件,它要创建,创建完以后啊,要写出文件的内容,那第二步呢,是写出内容来,这是第二步啊,那第二步写内容,这是我们通俗讲叫翻译,就是一句一句的翻译,你jsp中有什么话,我就翻译成这个类中的某些代码,那么翻译的基本的步骤,基本的流程,大概是这样的,说一下,其实通过,刚才通过看那个,就是翻译,生成的代码,咱们大概也有了一些了解,首先呢,在那个Servlet方法,一开头,它是干什么的呢,它先声明了啥呢,声明了一些变量,是这样吧,它一开始声明了一些变量,这是第一步,它先声明变量,声明了变量以后,然后,它干什么了呢,赋值,这赋值就算是声明变量的一部分吧,然后后面的话,就是做翻译了,那如果你jsp中有标签,对于标签怎么处理呢,write标签,是这样吧,直接write,如果你这个jsp中有脚本,怎么办,或者有表达式怎么办,print,表达式它就print了,print表达式,还有,如果说这个,jsp中有这个脚本,脚本怎么办,脚本它就直接保留,没动对吧,没有做什么处理,一个for循环,还是for循环,没做处理啊,所以最后呢,是保留了脚本,大概是这样的一个逻辑。
  • 这是这个tomcat啊,翻译jsp的基本的方式,咱们通过观察,得出的一个结论,然后呢,你要注意了,这个除了声明变量以外,标签,脚本和表达式,这几步,它都是对jsp中代码的处理,是这样吧,对吧,都是对jsp中代码的处理,或者说呢,这个,后面这3部分,write标签,print表达式,保留脚本,这些代码,其实,圈上的这些代码,它就是jsp中的代码,是这样吧,这些代码,这些逻辑就是jsp中的逻辑,就是jsp代码。只有变量不是(jsp翻译成Servlet时,只有声明变量不是jsp中写的代码)。那最终呢,它经过这个翻译啊,它声明了这个变量,写了这些内容,那这个翻译生成的Servlet中,就有这些内容了,对吧,就有这些代码了,那这个类最终也写完了,最后一步干什么呢,最后一步,它需要编译,你这个java文件建好了,内容也写好了,得编译对吧,编译成class,好调,所以最终的话,是编译,编译class,好了,那么这个tomcat啊,它把jsp翻译成Servlet,这个详细的过程,大概是这样的,这是一个细节。

隐含变量的由来

  • 那么在这个细节之内,我们还探讨一下,这个变量的意义,你看啊,它是在方法service一开头,就声明了一堆变量(声明变量),这些变量能不能,在这后面,write标签,print表达式,保留脚本,这个地方调用呢,可不可以在这些里面调用呢,这个脚本能不能调这个变量啊, 表达式里能不能调这个变量呢,可以 ,因为有先有后对吧,可以的。所以我们会得出一些结论,第一个结论,就是说,变量是在jsp代码前声明的,我可以这么讲么,这个蓝色方块中的代码,write标签,print表达式,保留脚本的这些代码,它是不是等价于jsp中的代码啊,或者我就说,它就是jsp中的代码对吧,应该是这样,可以这样讲啊,我对齐点,它们有着对应关系。就是说,这个蓝色方块中的代码,它就是jsp中的代码,两者是等价的,因为蓝色方块中的代码就是根据jsp而来的,这就是jsp代码,所以呢,变量是在jsp代码之前声明的,那正是因为变量是在jsp代码之前声明的,所以我们得出一个结论,那么jsp代码中可以直接调用这些变量,可以吧,可以调用。
  • 那么从这个jsp的角度来说啊,这些变量能直接调用,我们给它取个名字,就是,称这些变量为隐含对象,或者叫内置对象,都可以,那为什么叫隐含对象,或内置对象呢,因为jsp程序中,jsp代码里,可以直接调用它,所以,我们取名叫隐含对象,或者叫内置对象,这是我们对这个变量的一个解释,那我们能够通过这个jsp的原理,引出这些变量,从而呢,引出什么呢,我们下面要讲的隐含对象,或者说内置对象,就了解它们到底是什么。好了,那么关于啊,这个jsp啊,它翻译的详细过程,以及呢,在这个过程中,这个变量,它为什么叫隐含对象,叫内置对象,它为什么可以被jsp直接调用,这个原因,做一个理解。这是第4个话题,就是jsp的原理。

案例hello.jsp(Servlet翻译前代码)

<%@page pageEncoding="utf-8" %>
<!doctype html>
<html>
	<head>
		<meta charset="utf-8"/>
		<title>第1个JSP</title>
	</head>
	<body>
		<!-- 3.jsp声明 -->
		<%! 
			public double bai(double d){
				return d*100;
			}
		%>
		<ul>
			<!-- 1.jsp脚本 -->
			<% 
				for(int i=0; i<10; i++){
			%>
				<!-- 2.jsp表达式 -->
				<li><%=bai(Math.random())  %></li>
			<%
				}
			%>	 
		</ul>
		<%@include file="time.jsp" %>
	</body>
</html>

案例hello.jsp(Servlet翻译后代码)

/*
 * Generated by the Jasper component of Apache Tomcat
 * Version: Apache Tomcat/7.0.67
 * Generated at: 2020-05-22 00:02:47 UTC
 * Note: The last modified time of this file was set to
 *       the last modified time of the source file after
 *       generation to assist with modification tracking.
 */
package org.apache.jsp;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import java.util.*;
import java.text.*;

public final class hello_jsp extends org.apache.jasper.runtime.HttpJspBase
    implements org.apache.jasper.runtime.JspSourceDependent {

 
			public double bai(double d){
				return d*100;
			}
		
  private static final javax.servlet.jsp.JspFactory _jspxFactory =
          javax.servlet.jsp.JspFactory.getDefaultFactory();

  private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants;

  static {
    _jspx_dependants = new java.util.HashMap<java.lang.String,java.lang.Long>(1);
    _jspx_dependants.put("/time.jsp", Long.valueOf(1543333692630L));
  }

  private volatile javax.el.ExpressionFactory _el_expressionfactory;
  private volatile org.apache.tomcat.InstanceManager _jsp_instancemanager;

  public java.util.Map<java.lang.String,java.lang.Long> getDependants() {
    return _jspx_dependants;
  }

  public javax.el.ExpressionFactory _jsp_getExpressionFactory() {
    if (_el_expressionfactory == null) {
      synchronized (this) {
        if (_el_expressionfactory == null) {
          _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
        }
      }
    }
    return _el_expressionfactory;
  }

  public org.apache.tomcat.InstanceManager _jsp_getInstanceManager() {
    if (_jsp_instancemanager == null) {
      synchronized (this) {
        if (_jsp_instancemanager == null) {
          _jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
        }
      }
    }
    return _jsp_instancemanager;
  }

  public void _jspInit() {
  }

  public void _jspDestroy() {
  }

  public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
        throws java.io.IOException, javax.servlet.ServletException {

    final javax.servlet.jsp.PageContext pageContext;
    javax.servlet.http.HttpSession session = null;
    final javax.servlet.ServletContext application;
    final javax.servlet.ServletConfig config;
    javax.servlet.jsp.JspWriter out = null;
    final java.lang.Object page = this;
    javax.servlet.jsp.JspWriter _jspx_out = null;
    javax.servlet.jsp.PageContext _jspx_page_context = null;


    try {
      response.setContentType("text/html;charset=utf-8");
      pageContext = _jspxFactory.getPageContext(this, request, response,
      			null, true, 8192, true);
      _jspx_page_context = pageContext;
      application = pageContext.getServletContext();
      config = pageContext.getServletConfig();
      session = pageContext.getSession();
      out = pageContext.getOut();
      _jspx_out = out;

      out.write("\r\n");
      out.write("<!doctype html>\r\n");
      out.write("<html>\r\n");
      out.write("\t<head>\r\n");
      out.write("\t\t<meta charset=\"utf-8\"/>\r\n");
      out.write("\t\t<title>第1个JSP</title>\r\n");
      out.write("\t</head>\r\n");
      out.write("\t<body>\r\n");
      out.write("\t\t<!-- 3.jsp声明 -->\r\n");
      out.write("\t\t");
      out.write("\r\n");
      out.write("\t\t<ul>\r\n");
      out.write("\t\t\t<!-- 1.jsp脚本 -->\r\n");
      out.write("\t\t\t");
 
				for(int i=0; i<10; i++){
			
      out.write("\r\n");
      out.write("\t\t\t\t<!-- 2.jsp表达式 -->\r\n");
      out.write("\t\t\t\t<li>");
      out.print(bai(Math.random())  );
      out.write("</li>\r\n");
      out.write("\t\t\t");

				}
			
      out.write("\t \r\n");
      out.write("\t\t</ul>\r\n");
      out.write("\t\t");
      out.write("<!-- \r\n");
      out.write("pageEncoding:  声明此jsp文件的编码\r\n");
      out.write("contentType:  声明此jsp向浏览器输出的内容格式\r\n");
      out.write(" -->\r\n");
      out.write("\r\n");

	Date d = new Date();
	SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
	String time = sdf.format(d);

      out.write("\r\n");
      out.write("<p>");
      out.print(time );
      out.write("</p>\r\n");
      out.write("\t\r\n");
      out.write("\t\r\n");
      out.write("\t\r\n");
      out.write("\t\r\n");
      out.write("\t\r\n");
      out.write("\t\r\n");
      out.write("\t\r\n");
      out.write("\t\r\n");
      out.write("\t\r\n");
      out.write("\t\r\n");
      out.write("\t\r\n");
      out.write("\t");
      out.write("\r\n");
      out.write("\t</body>\r\n");
      out.write("</html>\r\n");
      out.write("\r\n");
      out.write("\r\n");
      out.write("\r\n");
      out.write("\r\n");
      out.write("\r\n");
      out.write("\r\n");
      out.write("\r\n");
      out.write("\r\n");
      out.write("\r\n");
      out.write("\r\n");
      out.write("\r\n");
    } catch (java.lang.Throwable t) {
      if (!(t instanceof javax.servlet.jsp.SkipPageException)){
        out = _jspx_out;
        if (out != null && out.getBufferSize() != 0)
          try {
            if (response.isCommitted()) {
              out.flush();
            } else {
              out.clearBuffer();
            }
          } catch (java.io.IOException e) {}
        if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
        else throw new ServletException(t);
      }
    } finally {
      _jspxFactory.releasePageContext(_jspx_page_context);
    }
  }
}

JSP运行原理小总结

  • 那JSP运行原理这个话题,又有2个小的话题,第一个小的话题,jsp它的运行过程,jsp处理请求的过程,然后呢,第二个小的话题, 这个jsp翻译的详细过程,它们分别对应画了两个图,我们把这两个内容呢,串一下,再看看,那前者是jsp运行的完整过程,是宏观的过程,而后面这个,是一个微观的过程,是一个细节,或者说,它是对前面这一个图的补充,一个扩展,指的是这个环节,宏观过程的第8步翻译,到底是详细是怎么样的,那么,在前面的这个图中,我们得出了一个结论,jsp本身就是Servlet,那它是怎么形成的,它怎么变成的Servlet呢,是翻译的,那怎么翻译的呢,是tomcat翻译的,tomcat根据jsp,找到jsp以后,先创建一个java文件,然后呢,一边读jsp中的内容,一边呢,翻译出呢,这个java文件中的内容,然后呢,编译成class,就可以了。
  • 那翻译的时候啊,它有如下几步,主要是那个service方法,先声明变量,然后呢,对于jsp中呢,不同的内容,做不同的处理,对标签write,表达式print,脚本保留,做不同的处理,然后呢,因为在此之前,就声明了变量,那它声明变量的目的是什么呢,就是为了让下面这些代码(标签,表达式,脚本)能够调用它,明白吧,这是它目的,有的时候需要,有用,有什么用呢,后面,在后面再讲,别着急。那因为呢,变量是在jsp的代码之前声明的,jsp代码可以调用这些变量,所以呢,对于jsp而言,这些变量能立刻调用,直接调用,所以我们称其为隐含对象,也叫内置对象,是这样一个原因。那了解了这些内容以后,下面我们将话题引到这个,隐含对象上去,书上也是这样讲的。

5.JSP的9个隐含对象

  • 那么jsp,什么是jsp隐含对象,内置对象呢,刚才我画的那个翻译的流程图,其实就解释这件事了,隐含对象就是我们在翻译时,一开头的那些变量,对吧,就是一堆变量而已,只不过呢,这些变量都是对象,然后呢,这些对象可以直接在jsp上调用,这就是隐含对象,这就是内置对象,就这么规定的。那这个对象啊,咱们之前看那个代码有好多对吧,我还特意让你,说你看一眼,有个印象,那估计也记不住,但是,我们在看一下话的话,应该有个,有一点点印象啊,书上有个归纳啊,这个隐含对象,一共有几个呢,9个,它还给我们归了类,一共9个对象,归为4类,这太难记了,对吧,所以一般也别记了,别这么记,9个对象归4类,这怎么记啊,归两类还凑合,算了,别看那个归类了,不用记,不好记,我也记不住,哎,注意啊,这个jsp中的9个隐含对象是什么,大概有什么作用,尤其它是什么,列举这件事,这是一道非常常见的笔试题,很多企业面试的笔试题里,都有这个东西,所以,你得把它记下来。
  • 然后呢,书上呢,有罗列啊,说这9个对象分别是什么,然后呢,类型是什么呢,这都有。然后呢,它这个顺序呢,不太好,不好记啊,这里头,有很多对象是我们认识的,有很多对象是我们所不认识的,那我归纳一下,我先把我们认识的,归纳到一起,不认识的,再追加一下明白吧,然后熟悉的,认识的,我们就一笔带过,不认识的,再强调一下,至于怎么用,我再给出一个例子。

JSP页面中的隐含对象
- 什么是隐含对象:容器自动创建,在JSP文件中可以直接使用的对象
- 作用:JSP预先创建的这些对象可以简化对HTTP请求、响应信息的访问
- 隐含对象
 	1.输入输出对象:request,response,out
 	2.作用域通信对象:session,application,pageContext
 	3.Servlet对象:page,config
 	4.异常对象:exception
 - JSP页面中可以使用的隐含对象如下:
 	隐含对象			类型				说明
 	request			HttpServletRequest	请求信息
 	response		HttpServletResponse	响应信息
 	out				JSPWriter			输出的数据流
 	session			HttpSession			会话
 	application		ServletContext		全局的上下文对象
 	pageContext		PageContext			JSP页面上下文
 	page			Object				JSP页面本身
 	config			ServletConfig		Servlet配置对象
 	exception		Throwable			捕获网页异常

  • 好,归纳一下啊,注意,前面,第一列这个是对象的名字,就是变量名,后面是它的类型,那我们在使用这个对象时,必须写这个名字明白么,比如说,你要用request,你就必须写request,你写req行么,不行,为啥不行呢,不是规定,也是规定吧,这不能说不是规定,为啥不行呢,你看啊,就是说,它翻译所,翻译生成的这个类当中,这个service方法里,方法的参数也是隐含对象,因为方法参数,我们在jsp里是不是可以直接调呢,也可以直接调吧,因为方法的参数不也是在jsp代码之前么,是吧,这个参数也算是隐含对象,那你看,它这个参数名,它就叫,它就叫request对吧,你要想引用它必须叫request,是这意思吧,对吧,这不能改。

final javax.servlet.jsp.PageContext pageContext;
javax.servlet.http.HttpSession session = null;
final javax.servlet.ServletContext application;
final javax.servlet.ServletConfig config;
javax.servlet.jsp.JspWriter out = null;
final java.lang.Object page = this;
javax.servlet.jsp.JspWriter _jspx_out = null;
javax.servlet.jsp.PageContext _jspx_page_context = null;

  • 再比如说啊,你看它后面这些变量,比如这个变量叫pageContext,它名字就叫这个对吧,你改名字,就引用不了了,所以,你的对象名,其实就是变量名,不能乱写,一定是这样的啊。好,归纳一下,这样好记啊,那么,啊,这是第5个话题吧,就是,jsp隐含对象,也叫jsp内置对象,这都可以啊,隐含,或者说内置对象,怎么叫都行,然后呢,这是一道非常常见的笔试题,反正,你最好自己背一背,然后呢,这9个对象分别是啊,第一个是request,第二个是,response,第3个是,out,然后是,我想一想啊,不是session啊,那个session咱没学过,我说,我先把学过的,归纳到一起,然后,第4个,config,然后呢是,第5个,application,还有一个是 ,第6个,exception,类型是Throwable,其实呢,前6个对象,我们是学过的。
  • 我说一下类型,你就知道了,request,这是变量名,变量名叫啥都行,对吧,但是它的类型是固定的,request类型是HttpServletRequest,就处理请求的对象,明白吧,以前天天用啊。response类型是HttpServletResponse,用来做响应的,明白吧,天天用,这个out,它的类型是JSPWriter,好,你注意啊,这个JSPWriter,以前没学过,但是呢,它的作用啊,它和printWriter一样,几乎是一样的,没什么好说的,只不过,它可能是重载了一些方法,就重构了一些方法,改了一些方法,但都是输出啊,没什么好说的,基本上是一样的,就相当于是PrintWriter,然后呢,再看config,config的类型呢是ServletConfig对吧,咱们刚刚学过的,那application叫做,ServletContext,刚刚学过的东西,那这两个对象,我们在jsp里,也可以直接用,如果对象里有数据,可以直接获取。application这个名字有点别扭啊,它的名字叫context就好了啊,它叫application,那没办法,它就这么叫的,咱们也只能是接受了。
  • 然后呢,exception,它的类型呢是Throwable,就是异常,就是这个,是谁抛出异常呢,注意,网页,你说的网页,指的是jsp,还是什么,你指的是什么,jsp,jsp是一个网页么,你注意啊,jsp的本质是一个组件,这个组件用来生产Servlet,由Servlet最终生成html,对吧,所以jsp啊,其实本质上,你不能说它是,不能说它是网页,应该说它是组件,更专业,然后,它的作用是用来指导,tomcat生产html的,是这样的。好,那你注意啊,那exception是哪个异常呢,是tomcat在调这个Servlet时,所抛的异常,明白吧,如果生产的Servlet这里有错,抛的异常就是exception,但是你Servlet这里有错,一定是jsp写错了,对吧,也相当于是jsp中的错误,也可以这样理解。就总之啊,是这个组件所抛的异常,一定是抛出异常时才有用,抛不出异常时没什么用,就取不到,不抛异常的时候,不过一般我们也不怎么用它,因为什么呢,对异常的处理,将来我们会讲,tomcat可以统一处理,我们不用说,还得去抓这个东西,异常,没必要。
  • 然后呢,第7个,还有一个内容叫session,session的类型呢,是HttpSession,这个内容啊,咱们现在没有讲,后面会有专门的章节来讲,就后面讲,然后呢,第8个,是page,然后第9个,是pageContext啊,那么第8个,page这个类型,是Object,那page是什么呢,它就是this,那到底是什么呢,我们看一下生成的那个代码,就知道了,如果说这些东西啊,哪个地方不清楚啊,你看一下翻译所生成的代码,看一下就知道了,你看,java.lang.Object page = this;,看到了么,你看page变量,就等于this,是这样吧,它的类型是Object,所以说page是什么呢,page就指代那个Servlet,明白吧,指代那个Servlet,或者说它指代那个jsp,因为Servlet是通过jsp生成的,就是this指代jsp生成的那个Servlet,但这玩意吧,一般我们也不用,没啥用,就是说怎么说呢,用不用,你也得知道,那有人说为啥不用,还得这么啰嗦呢,因为这是一项,一门技术,这门技术得考虑到方方面面对吧,就哪怕是只有几个人用,它也得搞这个东西,所以说,尽管是不常用,但是呢,也有,你要知道啊。
  • 最后一个呢,是pageContext啊,它的类型就是PageContext,这个也没讲过,那么它是什么呢,它是一个管理者,通过它,可以获得其他8个隐含对象,就这个对象里,装了其他8个隐含对象,我们得到它以后,就得到一切了,那这东西怎么用,后面会有演示啊,那一共是9个隐含对象,那平时,我们比较常用的是哪几个呢,标一下,request是比较常用的,response几乎就不用了,为啥不用了呢,你看,生成的代码里,我们有必要,自己去write么,它自动write,自动调response,调write就write了,是吧,我们只要写出表达式就自动write,我们不用自己write,明白吧,所以我一般不用response,以前是要,现在不用了。然后再看out,out呢,也不怎么用了,也少了相当于,因为它自动,自动就write了对吧,就是你只要写出这个,<%= %>,写一个表达式不就out了吗,不就输出了么,所以你不用自己输出了,这个现在也少一点,然后呢,这个ServletConfig和ServletContext,看情况,一般也不多,exception异常也很少用,session,这个非常常用,和request差不多,这个page,也很少用,一般也没啥用,那pageContext也还算是比较好用的东西,所以啊,一共9个隐含对象,3个相对常用一些啊。
  • 总之呢,我们学这个隐含对象啊,我们背它的意义更大,因为这是一道笔试题,你要背下来,那每个对象怎么用呢,稍微举个例子啊,这是如何使用隐含对象,我举个例子,我是以这个request来举例,比如说呢,我在写jsp时,我写一个jsp脚本,我在脚本里啊,可以直接写什么呢,request,直接调request,比如说,request.getParameter("user"),即<%String user = request.getParameter("user");%>,我可以直接呢,写这样一句话,就是,你脚本里可以直接调request,明白吧,它有什么方法就可以调什么,就是这样,或者也可以这样,比如说,我想写表达式,想输出东西,你也可以这样写啊,<%=request.getMethod()%>,就总之啊,隐含对象,就是我们可以直接在jsp里,引用和写,直接写就可以了,它有什么方法就调什么方法,当然每个对象,有什么方法,那有的我们学过,有的没学过,没学过以后学的时候再看,学过的就不说了。
  • 那么关于这个jsp,它的原理和隐含对象,就先说到这里,重点一定要背。原理呢,介绍这个jsp翻译的过程,然后呢,我们也去看了,那个生成的那个类,然后,通过观察,通过这个推导,我们最终呢,也引出来了,这个什么是jsp隐含对象,那么在它翻译时,那个类当中,方法一开头,声明的那堆变量,加上那个参数request和response,那么就是隐含对象,因为这些内容呢,它是在jsp代码之前,这个出现的。jsp代码里呢,可以调用,所以呢,能直接调用的东西,我们叫隐含对象,也叫内置对象,9个,有这么多。那么,总而言之啊,你想在jsp里呢,直接调用它们,直接用就可以了,拿过来就用,刚才也给出了例子,总之呢,无论你写这个脚本,还是表达式的时候,都可以直接写。那么,这些对象啊,我哪些常用,给你画出来了,这些常用的对象呢,我们后面都会演示,那不常用的话呢,可能就用不上,那用不上就算了,用上哪一个,我们演示哪一个,因为太多了,没挨个演示,但是也没必要。

6.利用JSP重写员工查询功能(Model1)

需求设计与代码分析

  • 那么,这9个对象吧,反正你课后吧,你得把它记下来,然后呢,这是一道经常面试的一道题,现在呢,我们初步的了解了这个jsp啊,那下面呢,我们就利用一下这个jsp,我们开发一个小功能,然后呢,体会一下,这个jsp它的这个开发动态网页,是不是比以前方便了,那为了有一个对比啊,我们还是开发以前我们做过的那个功能,员工查询功能(Servlet版),那我们看一下,我们这回写员工查询的功能,会不会比之前,我们直接使用Servlet,更方便一点呢。好了,那咱们来开发这个案例啊,这个案例的开发的思路,不用说了,因为之前用Servlet写过,咱们可以回顾一下,看一下:
    在这里插入图片描述
  • 这是以前呢,我们直接使用Servlet开发这个员工查询的功能,现在要用jsp的话,无非就是把它FindEmpServlet换了,就这个实体类,EmpDao,还是原来的不用动,就把Servlet它换了就可以,那咱们来写代码,那写的话呢,咱们新建一个项目吧。那新建一个项目,web项目,叫jsp2,创建好jsp2这个项目,然后呢,别忘了 导包,那创建完这个项目以后,咱们要开发这个查询功能,这个开发的这个步骤和上次一样。首先呢,我们也得写这个实体类,其次呢,也得写这个Dao,不过呢,因为这两项内容,和我们之前写过的,没什么变化,对吧,一样,咱们干脆呢,就把它copy过来就算了,就直接用,我们主要是呢,写这个jsp,看一下,jsp和之前的这个Servlet之间,这个有多大的差别。
  • 那大家打开这个eclipse啊,然后呢,你打开那个EmpManager,我们从EmpManager之下啊,copy一些东西过来,直接用,EmpManager,然后你看,这个项目下,以前呢,我们写过了,dao和entity对吧,这俩包下就有那些内容,那我们直接呢,把这俩包,一复制就可以了,整个包一复制就好了,那我们选择这个dao和entity这俩包啊,ctrl+c,复制啊,复制完以后,我打开jsp2这个项目,把我们copy的东西,粘贴到src/main/java的下面,就是把刚才复制的EmpManager项目下dao和entity这俩包,复制到jsp2项目下的src/main/java这里来,就是copy啊,没什么技术含量。
    在这里插入图片描述
  • 现在实体类和dao啊,都已经写完了,copy的也算是写的,对吧,你不用笑,咱们上班的时候,那是能copy肯定会copy的,谁愿意再写一遍呢,对吧,一天工作挺忙的,这个模块3天搞定,3天搞不定,我还不copy,是吧,当然,一般工作时,我们copy的东西,肯定是要加以改变的,有些内容是,能留的,有些要改,就copy个结构,那我们这个就是恰好,不用改,正好啊。好,然后下面呢,我们要想向浏览器输出一个网页,动态网页,我们这回不写Servlet了,我写的是jsp,那写吧,打开eclipse,然后呢,jsp要写在webapp里对吧,所以呢,展开src/main/webapp,就直接在这个webapp下创建这个jsp就可以了啊,直接写就行了。那我就选择webapp,右键,New/File,那这个文件名,后缀必须叫jsp啊,那名字呢,怎么都行,我叫啥呢,我叫find_emp.jsp吧,叫这样一个名字,find_emp.jsp,那要注意,我是吧jsp放到了webapp下,不是WEB-INF下,有人呢,不小心误把它放到了WEB-INF下,放进去之后,你会发现访问不了了,有问题啊,那这个问题是怎么产生的呢,将来会讲,反正现在呢,一定不要放在WEB-INF里。
  • 好了,创建完以后,我们打开这个jsp,打开它,那个jsp书写的方式,它的语法的结构,咱们刚刚讲过,正好呢,再巩固一下,那我们想写jsp,首先要写的是,那个东西叫什么啊,那叫指令,指令对吧,叫指令,我们先写一个指令叫page,即<%@page pageEncoding="utf-8"%>,这个指令上,除了pageEncoding之外,还有别内容,还有什呢,它还有contentType,contentType这个属性用不用写呢,不用,因为默认值就是html;而且默认编码和pageEncoding一样,不写了。还有有个属性叫import,需不需要写import呢,需要,那import是导包,我要导什么包呢,我得导什么包,我们得用dao对吧,还得导什么,实体类entity对吧,还有别的么,实体类不就Emp,是不还有list集合啊,list<Emp>,要导入实体类,dao,和List集合,这样,我写的简短一点,entity.*, dao.*, java.util.*啊,这是我为了简短,都写成星了,即<%@page pageEncoding="utf-8" import="entity.*,dao.*,java.util.*"%>
  • 包也导完了,那下面呢,我们要开始写它里面的内容,那我们再写一个jsp的时候啊,首先呢,是写出标签,那么工作时呢,这个标签啊,是我从美工那cpoy过来的,但现在没有美工,我们得手敲对吧,咱们手写一下吧,先写出一个,这个标准的网页的结构啊:

<%@page pageEncoding="utf-8"  import="entity.*,dao.*,java.util.*"%>
<!doctype html>
<html>
	<head>
		<meta charset="utf-8"/>
		<title>查询员工</title>
	</head>
	<body>
	</body>
<html>

  • 写好了一个标准的网页结构,写到这啊,我就感觉呢,确实比Servlet好,好在哪呢,之前写Servlet的时候,我们敢写这些网页结构的标签,这些东西么,甚至来讲,如果说,这个网页上有样式文件 ,有js,你敢写么,咱们以前都没敢写全对吧,那太麻烦了啊,现在 的话没关系了,你可以写 ,写标签还是很容易的。然后呢这个网页 中间,这个内容有什么呢,主要是一个table对吧,有标题,有数据,所以我们在body里啊,再写个table,我写上了table,table里,默认显示一行,这一行显示是标题,那么table呢,默认它是没有边框对吧,默认也没有这个宽度,为了好看一点呢,我想给这个table啊,加边框,那么给table加边框,咱们可以写样式,也可以呢,在table上写属性,对吧,都行,那我们写哪一个呢,样式,为啥写样式呢,因为你想写样式,行吧 ,样式咱们很久没写了是吧,你都想了是吧,挺想念,写一遍,那写样式的话,我写样式有几种方式,3种,分别是,内联,内部,外部,这个案例我就写内部样式,好吧。
  • 内部样式,你得写一个标签叫style,那我们通过css,给表格加边框的话,那么我们需要选择谁,给谁加border,table{ border: 1px solid red;},这就行了么,就你选择table加上边框就可以了吗,试一下,border: 1px solid red;,那么,只写这一句话似乎不行吧,因为以前咱们讲这个管理员列表的时候,光写上table的话,它外面一圈对吧 ,有边框,还差点啥呢,里面没有,里面没有怎么办呢,td上也得写是吧,td上也得有,所以写css很麻烦,再写上td吧 ,然后呢,对td呢,也加上border,td{ border: 1px solid red; }。这样呢 ,还有点问题,外面有一层border, td有一层border,那td和td之间,td和table之间有缝隙对吧,那么,为了去掉这个缝隙,我希望单元格合拼,怎么实现,那句话怎么写,cellspacing,那是昨天写的属性,不是cellspacing,不是你们说要写css的么,怎么回事啊,我是应你们要求写的啊,你写着,写不下去了啊,那句话叫什么来着,先不说那句话叫什么吧,那句话要写到哪个地方,table上对吧,不能在td上对吧,哎,当时我这样讲的,那句话叫什么呢,border-collapse: collapse;,它的值就是collapse,这是边框合拼,css就这么干。

<%@page pageEncoding="utf-8"  import="entity.*,dao.*,java.util.*"%>
<!doctype html>
<html>
	<head>
		<meta charset="utf-8"/>
		<title>查询员工</title>
		<style>
			table{
				border: 1px  solid  red;
				border-collapse: collapse;
			}
			td{
				border: 1px solid red;
			}
		</style>
	</head>
	<body>
		<table>
			<tr>
				<td>编号</td>
				<td>姓名</td>
				<td>职位</td>
				<td>薪资</td>
			</tr>
		</table>
	</body>
</html>

  • 好了,这个table的样式呢,我们就写完了,写完以后啊,我们再往后看,那下面呢,这个table除了有这个标题行以外,还得有数据对吧,还要有数据行,那下面呢,我写这个数据行,那写这个数据行的话,那你想啊,有多少行数据,你知道么,我还得查对吧,得调dao,所以我们得写代码查询,那得写java代码了,我们得写是表达式啊,还是脚本啊,还是什么呢,脚本,因为你查询是吧,是完整的逻辑,脚本是这样写啊,<%%>,这样。那<% %>这里面呢,我要调dao,那就实例化Dao,去调是吧,那个dao呢,叫EmpDao。那我实例化dao,调了查询方法findAll(),得到的数据,那得到数据以后,有一条数据,我们得输出一行来,对吧,得显示一行,所以,你得遍历这个集合是吧,遍历它;那每次遍历,咱们要输出一行,要写一个tr,为了写tr方便,这怎么办呢,把它拆开对吧,拆开以后,在for循环的内部,在这里就又可以写标签了,我又可以写tr了,写个tr,tr里呢,有4列啊,那么一共呢,是4列,那每一列的值,来源于谁呢,来源于e,对吧,我要输出e中一个值,那得用什么输出呢,<td><%= %></td>,这叫什么啊,这叫表达式啊。

<body>
	<table>
		<tr>
			<td>编号</td>
			<td>姓名</td>
			<td>职位</td>
			<td>薪资</td>
		</tr>
		<%
			EmpDao dao = new EmpDao();
			List<Emp> list = dao.findAll(); 
			for(Emp e : list){
		%>
		  	<tr>
		  		<td><%=e.getEmpno() %></td>
		  		<td><%=e.getEname() %></td>
		  		<td><%=e.getJob() %></td>
		  		<td><%=e.getSal() %></td>
		  	</tr>
		<%
			}
		%>
	</table>
</body>

  • 好了,这就完成了啊,那完成以后啊,这个jsp呢,是不需要配置的,可以直接访问,那下面呢,我们把这个项目部署一下,咱们访问下这个jsp,看一看输出结果,部署一下这个项目,然后呢,启动tomcat,启动完以后,打开浏览器,访问一下这个jsp,localhost:8080/jsp2/find_emp.jsp
    在这里插入图片描述

完整代码实现(项目jsp2)

webapp目录下find_emp.jsp

<%@page pageEncoding="utf-8"  import="entity.*,dao.*,java.util.*"%>
<!doctype html>
<html>
	<head>
		<meta charset="utf-8"/>
		<title>查询员工</title>
		<style>
			table{
				border: 1px  solid  red;
				border-collapse: collapse;
			}
			td{
				border: 1px solid red;
			}
		</style>
	</head>
	<body>
		<table>
			<tr>
				<td>编号</td>
				<td>姓名</td>
				<td>职位</td>
				<td>薪资</td>
			</tr>
			<%
				EmpDao dao = new EmpDao();
				List<Emp> list = dao.findAll(); 
				for(Emp e : list){
			%>
			  	<tr>
			  		<td><%=e.getEmpno() %></td>
			  		<td><%=e.getEname() %></td>
			  		<td><%=e.getJob() %></td>
			  		<td><%=e.getSal() %></td>
			  	</tr>
			<%
				}
			%>
		</table>
	</body>
</html>

src/main/java目录下的dao包下的EmpDao.java

package dao;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import entity.Emp;

public class EmpDao implements Serializable {
	
	public List<Emp> findAll(){
		//模拟查询所有的员工
		List<Emp> list = new ArrayList<Emp>();
		
		Emp e1 = new Emp();
		e1.setEmpno(1);
		e1.setEname("唐僧");
		e1.setJob("领导");
		e1.setSal(9000.0);
		list.add(e1);
		
		Emp e2 = new Emp();
		e2.setEmpno(2);
		e2.setEname("悟空");
		e2.setJob("职员");
		e2.setSal(9000.0);
		list.add(e2);
		
		Emp e3 = new Emp();
		e3.setEmpno(3);
		e3.setEname("八戒");
		e3.setJob("职员");
		e3.setSal(6000.0);
		list.add(e3);
		
		return list;
	}
	
	public void save(Emp e) {
		System.out.println("增加员工:"+e.getEname());
	}
}

src/main/java目录下的entity包下的Emp.java

package entity;

import java.io.Serializable;

public class Emp implements Serializable {
	
	private Integer empno;
	private String ename;
	private String job;
	private Double sal;
	
	public Integer getEmpno() {
		return empno;
	}
	public void setEmpno(Integer empno) {
		this.empno = empno;
	}
	public String getEname() {
		return ename;
	}
	public void setEname(String ename) {
		this.ename = ename;
	}
	public String getJob() {
		return job;
	}
	public void setJob(String job) {
		this.job = job;
	}
	public Double getSal() {
		return sal;
	}
	public void setSal(Double sal) {
		this.sal = sal;
	}
}

7.开发模式

开发模式概论

  • 那么通过这个小例子,我们就了解了,怎么去用jsp,解决这个问题啊,那么发现呢,我们用jsp做动态网页确实是比Servlet要简单一点,首先不用配就简单一点对吧,然后再一个呢,jsp写标签方便,写多少样式,写多少js,这回不头疼了,总之呢,这种方式要优于呢,前者,优于Servlet,然后呢,按照书上的这个进度,那么,下一个话题,我们要探讨的是转发,不过呢,咱们直接讲这个转发,这个跨度太大了,这中间呢,还缺点东西,我们想转发的前提呢,是要告诉你,我什么时候需要转发,而什么时候需要转发的话,它不是一两句话,能说清楚的,所以,我们把这个前提条件,这个背景,要说清楚,那这个背景是下一个话题。这是呢,第6个内容,这个内容叫什么呢,叫这个开发模式,web项目的开发模式,透过这个开发模式啊,我们能知道,那为什么要用转发,转发是什么意思,那开发模式呢,我们讲的是什么呢,就是讲的,我们在java这门语言当中,我们想开发web项目,我们有哪几种套路,讲的是一种套路,方式方法。
  • 那其实呢,完整来说啊,咱们这个java的web项目的演变,一共有3个阶段,第一个阶段呢,我们称之为Model 1,第一阶段,模式1;那么,第2个阶段,我们称之为,Model 2,模式2;第3个阶段,是我们采用框架解决问题,那是后话,咱们当前还没讲框架,所以,第3阶段,我就没法说了,后面会讲。那在我们这啊,我们只能探讨前两种阶段,Model1Model2,那么这两种阶段,我们在开发的时候,是怎么去运用,我们所学的技术去解决问题,然后呢,他们的方式方法有什么差别,有什么利弊,你要搞清楚,然后呢,你工作时,你要清醒的认识到,哎,我当前这个项目,它是用哪个模式开发的,我把它搞清楚。如果呢,你连当前的这个模式都没搞清楚的话,你可能开发时呢,就很混乱,是人家也有jsp,人家可能也有Servlet,但是呢,它是怎么运用的,你可能搞乱了,所以要把这个套路搞清楚。

Model1:Servlet/JSP+DAO(主标题)

标签和java混合开发(副标题1)

前后端不分离(副标题2)

HTML和Java高度耦合(副标题3)

  • 那么,在不同的套路下,不同的模式下,这项技术的使用方式,是有很大差别的,那我们先说第一个Model1,先说Model1,画个图来阐述啊,说什么是Model1Model1呢,是一种开发模式,当然开发的也是web项目,但凡呢,想讲web项目的,什么什么东西,我都是这样画的啊,左边是浏览器,右边呢,是服务器,然后呢,浏览器要发请求访问服务器,是吧,服务器呢,最终可能是要访问数据库,这里还有数据库的事,咱们画的稍微完善详尽一点,浏览器,服务器,数据库,这3者,那么,Model1模式下,浏览器发出请求,要求返回动态网页的话,那服务器由谁来处理这个请求呢,由Servlet,或者jsp,那么,无论你是用Servlet,还是jsp处理请求,其实呢,它都是一个档次,它的层次是一样的,还都比较初级。那么这种场景下,我们是使用,或者是Servlet,或者是jsp,处理请求,使用一个组件处理请求,那么处理请求的时候呢,Servlet或jsp,它需要调谁呢,它是不是得调dao啊,对吧,是吧,它得调dao啊,然后呢,Dao,需要呢,访问数据库,数据库呢,会把数据返回给dao,dao呢,再把数据给Servlet或jsp,给这个组件。
  • 然后呢,这个Servlet或jsp,这个组件经过加工和整理,把数据发送给浏览器,这种模式,我称之为Model1,是一个比较原始的开发方式,那么在这种方式下,它的特点是使用一个组件处理请求,不单单是处理请求,它还得处理业务,得调dao对吧,得到数据以后,它还得处理数据,有的时候啊,咱们业务比较复杂,得到数据以后,可能还要运算明白吧,还要加工,还要整理,然后呢,它还要把这个数据呢,显示给浏览器,还要输出,那么这种模式下呢,这个怎么说呢,这个组件,干的活太多了,那它里面的代码耦合度太高了,耦合度高,就不利于这个团队的配合,也不利于代码的维护,是这样的,这是这种模式啊,它的典型的特点啊。
  • 概括一下,就是Model1模式的典型特点是什么呢,是使用一个组件,既处理请求,又这个输出响应,那么该组件内部,代码耦合度高,那它里面高度的耦合了什么呢,标签和java,是这样么,你看,别管是jsp,还是这个Servlet,它里面是不是既有标签,又有java啊,对吧,该组件内部代码耦合度高,它将这个HTML和java高度耦合在一起,所以呢,你这样处理的话,是很不利于团队的配合,很不利于代码的维护的。什么叫不利于团队配合呢,因为你是写这么一个东西,就做出了很多事,那比如说啊,咱们这个程序写完,有bug,报错了,或者说,比如说业务需求发送变化,要改样式,要改页面结构,那我们应该这个jsp,或改这个Servlet是吧,要改它。
  • 按理说,改网页结构,改样式,应该由美工来改,那你觉得美工敢改这个东西么,不敢,它不会明白吧,它一看这啥啊,不敢改,不敢动,那得怎么办呢,美工呢就得,这个,改它原来的,原始的那个html,改好以后,给我们程序员,我们再去把这个内容,挪到jsp里,明白吧,说白了,再折腾你,再做一遍,本来是一个人能干的活,非得两个人干,就不利于团队配合,然后呢,这个,一改网页结构,网页中的java代码也受影响,也要加以修改,总之一改的话,改动很大,不利于这个团队配合,不利于维护。可以这么讲吧,就是说你将来去工作的时候啊,你的公司啊,基本上不会用这样的方式去开发项目了,应该不会,如果说,这个很不凑巧,你遇到这样的公司的话,那怎么办呢,你自己想想办法啊,因为这样的公司啊,不太,不太,容易学到东西是吧,太古老了,别说你们啊,就是我呢,这个有了十几年的开发经验,我见到这样的项目,也只有一次,就见过一次,第一家公司我见到过。

经典事例

  • 第一家公司好像跟你们说过吧,就是说,那个,我们老板是个海归么,是吧,老板是个美国硅谷回来的海归,然后,这个有一天呢,这个老板就跟我说,说,哎,小李啊,说现在你这个能力,我感觉也变强了,我还挺高兴的,想老板终于夸我了,以前老骂我,天天骂我,骂的我都,怀疑人生了,然后,终于夸我一次,我挺高兴,我挺激动的,我说,谢谢老板,我一直在努力啊。然后老板说了,说,你看啊,你的能力强了,你这个应该承担点更重要的事情,我这手里啊,有一个项目,以前我自己亲自维护的,我很重视啊,现在的话,你行了,把它给你,你能搞定么,我看你都这么说了,我还能搞不定么,没问题,可以的啊,拿来吧,给我了以后啊,我打开一看,这个项目呢,应该是挺小的,为啥呢,我一看,这里面那个jsp,不是很多啊,就几十个,几十个我忘了,估计有个三四十,还是多少,反正不是很多啊,我一看,不是太多,我一看,这个项目还行吧,不是很大啊,我就随手呢,就点一下,打开了一个jsp。
  • 打开以后呢,我的eclipse就卡死了,卡死以后呢,我心思,那怎么回事呢,我再打开呗,再打开eclipse,再点,又卡死了,哎,好奇怪啊,难道这个页面很大么,我就用记事本打开,用编辑器打开,打开以后,我一看,好家伙,得将近上万行代码,啊,一会是标签,一会是java,一会是标签,一会是java,套来套去,套去套来,套的我都晕了,我都看不明白,我根本就不敢改,你想这代码谁敢改,你改这一行,它对整个这些代码,哪有影响,对吧,这太不直观了。这么啰嗦,也没有条理性,不敢改,我就很郁闷,好家伙,这么个破项目给我来了,我心里充满了抱怨,充满的抱怨,不自觉就流露出来了,就嘟囔了一句,那它tm那个孙子写的,刚好老板呢,就坐我旁边,老板嘿嘿一笑,那我写的,你想一下啊,这就是Model1的杀伤力啊,我差点因为这都挂了,所以啊,所以啊,遇到Mode1项目,咱们能离它远点,离他远点,你见到之后,你是会骂人的,你是想死的,所以,最好别见到这种东西啊。
  • 应该不会见到了,因为,我这个从毕业,工作都十几年了,这么长时间,还有公司用这个,应该不会了,那也太古老了啊。好了,总之啊,这是一个,这是一个以往的一个阶段,你了解一下,应该是不会遇到了,我们之前所写的小案例,是这么做的,为啥那个小案例,没有写的很完善呢,没有访问数据库呢,是模拟的呢,因为这个,这个方式,不可取明白吧,不是很好,不是主流的方式,没必要花太多精力去搞啊,只要我们在这个案例中啊,把这个Servlet和jsp,它的语法明白了,就可以了,明白吧,所以说主要是这个。好,这图我存一下啊,这是Model1
    在这里插入图片描述

Model1模式案例演示:员工查询功能

Model2:MVC架构:Model+View+Controller(主标题)

DAO+JSP+Servlet(副标题1)

DAO处理业务逻辑数据(副标题2)

JSP处理前端页面展示(副标题3)

Servlet负责页面与业务数据的传输交互(副标题4)

  • 好,那再说一下Model2,这种模式推出以后,市场上的反响也不是很好,那有的企业就说了,那你看,我们这个开发一个小项目,这个页面比较简单还好,如果页面很复杂,逻辑很复杂的话,这个软件,这个组件的耦合度,实在是太高了,不利于维护,是不敢用这种方式,去开发大项目的,那么Sun呢,也意识到这一点,所以呢,它对这个技术呢,加以了这个,怎么说呢,规定啊,这个,推出了一种新的套路,那这个新的套路呢,已经成为了主流,那这个新的套路是什么呢,说一下。那还是web项目,解决web项目中的问题,那还是有浏览器,有服务器,还有呢这个,数据库啊,但之间的交互是HTTP协议啊。好,那Sun呢一看啊,就是说一个组件,处理请求不方便,那就俩呗,耦合度高怎么办,就拆呗对吧。
  • 说白了,咱们这个,这个技术啊,它来源于生活,生活当中呢,比如说,某个人干的活太多,干不过来,怎么办呢,俩人干对吧,俩人干不过来,3个人干,那么企业在用人时也是这么一个原则,企业用人追求的原则是什么呢,一个萝卜一个坑,你只专注干你擅长的活,把你的活干到极致,最好,如果将来你走了,你干的那活,一点点,很快找个人顶替你明白吧,就希望是这样。或者企业追求的目标是这样的,就是说,任何人,不管是谁走了,这个企业不会黄,明白吧,如果说这人走了,企业就黄了,这耦合度太高了,明白吧,是这样的,真的是这样啊,以前我在用友的时候,就号称什么呢,说,用友有好几十个,这个副总裁,谁走了,公司也不会黄,就这么讲的,这有点扯远了啊,再扯回来。
  • 就是说,那这个,一个组件处理的事太多,耦合度高,就拆,怎么拆呢,你看啊,原来啊,是一个组件,现在可以拆成俩,或者3个啊,那Sun呢,一看啊,我们现在的这个技术的版图里,有的是Servlet,还有的是jsp,那这两个配合一下,会更好,然后呢,会发现,这个Servlet更适合, 更擅长写java,是这样吧,Servlet里面不是更适合写java么,那jsp里呢,jsp更适合写什么啊,更适合写标签对吧,它写标签方便,那两者相配合就好了。那可以这样,他是这样想的啊,那浏览器啊,发送请求给服务器,我服务器呢,处理请求,处理请求是要接收参数的对吧,这是java代码,由谁处理呢,Servlet,它更适合,更擅长;然后呢,处理完数据以后,得到一些结论,这个结论给jsp,jsp更擅长显示一个结果,是这样吧,更擅长写标签,那它将这两个组件一配合,这个就更方便了,那当然了,Servlet处理请求,接收参数,那么它要处理具体的业务的话,它可能还会调谁呢,可能还会调这个DAO,它可能还需要调DAO,一般都是要调DAO,这样的。
  • 所以你看,那么Sun的想法就是什么呢,原先是一个组件,现在拆成两个组件,互相配合就好了,各自干各自更擅长的事情,就好了。然后呢,在这种模式下,Model2模式下,那浏览器端的所有请求,我们都交给Servlet统一处理,那么,服务器要给浏览器返回的所有内容统一交给jsp处理,明白吧,两者相互配合,这样就更好了,解耦了,那这样的话呢,我们最大程度的保证jsp里,有很少的java,不能说没有,就一点点,明白吧, 最少的,你把结果给我,一个集合,我就一遍历,一输出就完了,最少,不会很多,这样耦合度就低了啊。那这里有一个问题啊,就是说,那么Servlet要把数据给jsp,怎么给呢,这件事我们称之为转发,Servlet把数据给jsp,这件事,我们称之为转发,那转发呢,后面我们会演示,然后呢,也会详细讲解,这里先提一下。
  • 好了,总而言之啊,就是,这是Model2模式,是采用多个组件,对原来的那个组件呢,解耦了。那其实呢,这种解决问题的方案,它是符合一种设计模式的规定,其实呢,Sun是引入了一个设计模式,来改善了对象的关系,改善了组件的关系,那这个模式叫什么呢,叫做MVC模式,MVC模式,那MVC模式,指的是什么,它是什么意思,我们需要介绍一下,就是这种架构,这种设计,就是MVC模式,明白吧,就它指的是什么,说一下。这里你要注意啊,MVC模式,是极为重要的,这是一个非常经典的设计模式,不止是java语言中用,那别语言,像.netphp,很多语言都会用,包括手机端,也会用,那这种模式,早在上个世纪就出现了,八九十年代就有了,很早以前就有了啊,那么,经过证实,经过这个验证吧,它能够在这个软件开发中吧,起到很关键的作用。
  • 那这个模式啊,是我们整个web项目开发的一个指导的思想,是一个基石,那么非常之重要,如果面试的时候问你啊,什么是MVC模式,不知道,基本就完了,所以一定要知道,而且呢,MVC模式,是一个出现频率极高的面试题,所以我把语言概括一下,你课后你要背下来。一定要背下来。好,解释一下啊,在后面写了啊,MVC模式啊,什么是MVC模式,MVC模式呢,它是一个经典的设计模式,是软件的分层思想,那么按照这个思想,软件至少要分为3层,最基本的要分为3层,这3层分别是什么呢,M,V,C,对,MVC指的就是这3层,3层,那M,V和C,分别是什么呢,M,它指的是Model,V指的是View,C指的是Controller。Model,View,Controller。
  • Model是什么呢,它指的是业务层,即业务层,用来处理业务,当然了,什么叫业务,可能你当前呢,理解的还不透,因为我们课堂上呢,不会给你讲很复杂的业务,我们主要专注于技术,你工作时呢,是利用技术解决业务,那业务就复杂了,那你工作时会真正理解什么叫业务,它有多复杂,总之呢,有专门一层,处理业务。然后呢,View是视图层,或者说表现层啊,用来展现数据,那Controller,它是控制层,那是这个业务层和视图层的桥梁,负责呢,衔接两者,负责管理,负责控制,大概是这样。然后啊,就最后再归纳一下,那这个模式有什么用呢,它可以降低软件中代码的耦合度,便于团队开发及维护。这是它呢,最终的一个,最终的目的和作用,就这样。

MVC面试技巧

  • 是这样啊,就这个MVC模式啊,这么几句话,最好是背,但是我个人建议你,最好是理解,然后呢,面试的时候呢,按照自己的想法,按照自己的理解说出来,用自己语言组织说出来,这样感觉更好,有的人真听话,真的是背啊,以前出过那个笔试题,让大家答,一看,那真的,跟我这个一字不差的,背的可那啥了,但是你要知道,这种死记硬背也不好,不好在哪呢,一个企业的这个人来了,面试,他可能是只招3个人,但他面试100个人,咱们挨个轮流去面试对吧,说的答案都一样,你觉得他的这个,他的感受会好么,很不好。那我以前,在企业里也是做,负责那个技术面试的,你来个人面试,答案刚好和刚才那人一样,什么感觉,你俩谁照谁超的,谁照谁背的,你还是上网上找了一个文章背的,我感觉到你在背,我感觉对你很没有兴趣,为啥呢,我就怕你死记硬背,我就怕你思路不灵活,我们就想要一个思维灵活的程序员,能够应付各种业务,明白吧,为企业解决各种业务,你要死记硬背的话,完了,那肯定不会灵活,那灵活的人,那不可能死记硬背,一定是这样的。
  • 所以呢,你这个,你可以背,但是呢,最终要转化为自己的语言,哪怕是你说的比较,磕磕绊绊,有的语言组织的也不是很顺,有点这个颠三倒四的,这都没关系,只要让面试官体会到啊,你理解了,你是用自己语言描述的,就行了,所以说总之呢,你一定要理解,用理解以后的语言去描述啊。其实MVC模式啊,就是说,就是一种这个顶层的架构模式,顶层的设计模式,咱们工作时,每个项目基本上都这么干,但是我想说一下什么呢,这个我们实际开发时啊,有可能会深化这个模式,什么叫深化呢,人家说了,说应该是具备3层,我们做就必须得3层么,我可不可以比他搞的还细,4层5层6层呢,它只是一个指导的思想对吧,它指导我们去怎么开发,但我做的时候,可能会更细化,这这样,是这意思吧。
  • 所以我们开发时啊,往往会搞出4层,5层,甚至6层,都有可能,那得看企业了,看这个业务的复杂度,将来呢,后面阶段,我们所学的云笔记项目,就是4层结构。那万变不离其宗,4层是由3层演化而来的,明白么,你只有理解了这个MVC,3层,456,才能理解,所以,万变不离其宗,这个你要理解,那我们课上呢,我们没法说,我们4层,5层,6层都演示一遍,没办法啊,因为这玩意没法演示,我们只演示3层,将来,你遇到4层,5层,6层,才能理解,再一个呢,这个MVC模式啊,其实就是一个团队配合,这个怎么说呢,互相的配合这么一种思路,是来源于生活的。

Model2 源于生活

  • 你比如说我们去一个,这个饭店吃饭,那饭店都有领班,把我们领进去对吧,先生您坐这,找个座位,把我们都领进去,然后的话呢,给我点餐,点完餐以后,这饭谁做呢,后厨做,是这意思吧,是这样吧,就那个前台,那个领班就好像是一个,这个Controller一样,是这样吧,控制,你来了,我接待你一下,帮你点餐,下完单以后,具体的干这个活,谁干,后厨对吧,后厨就好像什么呢,Model,最后呢,后厨做完菜以后,谁给你端上来呢,传菜的那个对吧,给你,拿个盘子,端个碗,然后的话,可能还得修饰美化一下,拿点餐具什么的,给你。所以,他们是分工合作的,其实MVC的思想就是从这来的,或者说,你像我们去那个,企业面试也是差不多,去企业面试,前台接待你,你来面试了,答个题吧,答完题以后,领着你去那个技术部,找个技术经理给你面试,那这个前台就好像是这个Controller对吧,负责流程,那个开发步骤那个人,负责处理业务,面试你,最后呢,那个你走的时候,可能将来还会有人,给你发邮件,给你一个反馈说你行,还是不行,是这意思吧,是配合的,相互配合的。
  • 所以MVC模式没那么复杂,它就是一种,来源于生活的这个经验,是各个人,或者各个对象之间相互配合,干这个事会更好,是吧,每个人干的活,一点点,容易控制,不容易失控啊,这么个想法。好,那你看,这个模式,我们套用一下,你感觉啊,Servlet,jsp,DAO,这3者,谁是M,谁是V, 谁是C,咱们这么看,首先看这个jsp,它是什么啊,显然是V,它不是负责显示的么,是吧,负责展现的,就V;再看这个Servlet,它属于什么呢,它属于C,因为它负责流程控制,流程来了,接待一下对吧,调谁,给谁是吧,负责控制,它不干具体的活,它是负责这个流程的控制,负责,是一个桥梁么;DAO呢,那就剩个什么啊,M,增删改查,处理业务;但是我们将来实际开发时啊,可能是,不只就是一个业务,不止是一个增删改查,可能是有很多,很复杂的情况,所以将来呢,DAO,这一层,可能会再去细分,不止是有DAO,还有别的,那是后话,目前,我们先了解到这种程度,就不错了啊。我把这个图存一下:
    在这里插入图片描述

Model2模式案例演示:员工查询功能(主标题)

利用Model1模式重构Model1模式案例(副标题1)
利用MVC模式重构案例:员工查询功能(副标题2)
需求设计与代码分析
  • 那以上呢,是我们说的这个Model2模式,说它怎么怎么好,怎么怎么解耦,那咱们也试一下啊,所以呢,我们就利用这种模式,我们再开发一次这个,员工查询的案例,然后呢,和之前的那个开发的方式呢,做一个对比,看一看,那这种开发模式,是不是有优点。那我们开发的话,你看之前,我们已经写过DAO了对吧,员工查询这个DAO不是已经有了么,实体类也有了是吧,所以我们在开发时啊,只要写一个Servlet和一个jsp就行了,所以下面呢,我们先写这个Servlet,那么它呢,处理请求,调dao得到数据,它要把数据给谁呢,给jsp,它把数据给jsp,怎么给呢,转发,这里我们先用一下转发,然后用过以后,我们后面再详细讲解。
  • 那打开eclipse,那个项目jsp2,打开jsp2,因为要写Servlet,所以我们打开这个Java Resources下的/src/main/java,先建个包,包名叫web,然后呢,在这个包下创建一个Servlet,那这个类啊,主要是查询员工,取名还是直观一点,取名还是叫FindEmpServlet,那继承于HttpServlet。那么创建好这个类以后啊,要想处理查询请求,我们需要呢,重写父类的方法,我们重写父类的service方法。那么,要想处理这个请求,之前说过,我们处理任何请求啊,基本上就是3大步,第一步呢,看一看有没有参数需要接收,第二步呢,处理业务,增删改查,第3步呢是做出响应,那我们看第一步,第一步有参数么,没有吧,因为查询全部的员工,没有任何条件,对吧,没有参数;然后呢,第二步啊,第二步是要查询,那我就直接查了,查询所有的员工:

    //查询所有的员工
    EmpDao dao = new EmpDao();
    List list = dao.findAll();

转发RequestDispatcher的应用
  • 那调DAO,查到了所有的员工,那么查到数据以后,第3步,我们不是呢,在这里直接向浏览器输出响应,而是怎么样呢,把数据转发给jsp,对吧,或者说把整个请求,把这件事转发给jsp,交由jsp继续处理,所以,这是两个人协作,所以下一步啊,是将请求转发给jsp。这个转发的jsp叫什么呢,咱们给它取个名字啊,咱们已经写过了一个jsp啊,咱们写的jsp呢,叫find_emp.jsp对吧,我们今天再写一个,就别和他同名了,咱们换个名字吧,那我们给这个jsp取个名字叫emp_list.jsp啊,转发给这个jsp,那怎么去实现这个转发呢,也是一句话 ,这句话是这样写,那大家想一想,咱们要实现转发的话,我是用request来实现,还是response实现,从Servlet转发到JSP,是这一步,从Servlet到jsp,是转发,咱们应该用request,还是jsp来解决问题呢,在这个阶段,咱们开始响应了没有,没有;请求结束了没有,没有;还在处理当中对吧,就Servlet没处理完,接着处理,是这意思吧,就像接力一样,这事还没完成呢,所以呢,是在请求的处理过程当中,进行当中,这件事由request处理,明白吧,谁由response处理呢,一定是这个,jsp到浏览器这个地方,响应的时候,所以转发时,使用request。

  • 那这句话这样写啊,request.getResquestDispatcher("").forward(req, res);,request调了一个方法,getResquestDispatcher(""),然后呢,get到以后,紧接着还得再点一下forward,又调一个方法,forward(req, res);,forward时呢,传入request和response。那稍微解释一下,request.getRequestDispatcher(""),这句话,是获得一个对象,这个对象就叫做RequestDispatcher,我们get到的,就是RequestDispatcher,那这个对象呢,是请求的转发器,专门负责转发的,是专门用来转发的对象,得到这个对象以后,进行转发,调它的forward方法进行转发,那forward方法呢,我们需要传入request和response,因为什么呢,你把这件事,交给别人继续处理,你得把处理这件事的工具给它,是这意思吧,对吧,比如你4x100接力,你要接力的话,你得把这个接力棒给人家对吧,你不给的话就作废了。同样的,像这个处理请求,Servlet处理请求,它的依据是request和response,它想让jsp帮它接着处理请求,它得把,request和response,这俩工具给它,明白吧,传给它,所以,forward方法的意思是什么呢,把request和response给它,给jsp。

  • 然后呢,这里还不对,还没完,那我们要转发给哪一个jsp,在哪里写呢,在RequestDispatcher(""),这里写的是,那个jsp的访问路径,表示要转发给它,这个代码因为太长了,我换一行,断个句,断一下。然后RequestDispatcher("")这里面呢,写这个jsp的访问路径,那么,我们在写web项目时,但凡写路径,我们都,一般都是写相对路径,所以,这里还是写相对路径,那么要写相对路径啊,你还要想,我们当前访问的是谁,我们将要访问的目标,它又是谁。那你看,我们是从哪访问到哪啊,看一下,咱们是从Servlet访问到jsp,对吧,那Servlet,它就是FindEmpServlet,它的访问路径呢,还没配,我们先预计一下啊,我预计呢,它的访问路径,就叫findEmp,那么它的绝对路径,我们写完整,是这样的啊,/jsp2/findEmp;然后呢,目标是jsp,那么jsp呢,我们一会写的话,会把它放到webapp下,那么访问起来的话,它就是这样的啊,就是,/jsp2/emp_list.jsp

    //将请求转发给emp_list.jsp
    //当前:/jsp2/findEmp
    //目标:/jsp2/emp_list.jsp
    req.getRequestDispatcher(“emp_list.jsp”).forward(req, res);

  • 那么,把这两个路径呐,写出来以后啊,经过对比发现是平级,所以呢,相对路径就是jsp的名字啊,emp_list.jsp。那这就是转发,req.getRequestDispatcher("emp_list.jsp").forward(req, res);,那这句话的意思是什么呢,是我要把请求交给这个jsp,继续处理,那么交付的时候,我会把request和response也给它,所以呢,程序会从Servlet直接跳转到这个jsp,跳转的时候呢,那么tomcat会把request和response,传给这个jsp,有人说,那jsp怎么去接收这个request和response,其实很好理解,这个jsp,它最终是不是会被翻译成一个Servlet,是吧,它最终会被翻译成Servlet,它的名字应该是叫emp_list_jsp.java,是这样的一个类,这是它的规则,然后呢,这个类中有service方法,方法中有两个参数,request和response,service(req,res),那你想,最终tomcat并不是调用了这个emp_list.jsp,这个文件,它调是emp_list_jsp.java这个类(由服务器tomcat把jsp文件自动翻译成的Servlet这个类),是这意思吧,它调用的是这个类。那这个类不是有参数request和response么,对吧,所以forward,我们声明的request和response,传给谁了呢,传给这个service(req, res){}方法,明白吧,所以jsp文件,本身不能接收参数,但是呢,它会生成Servlet,Servlet可以接收。

  • 那其实呢,根本上还是从FindEmpServlet,这个Servlet跳转到了emp_list_jsp.java,这个Servlet,其实还是两个组件的对象的跳转,调用啊。好了,那还差点事啊,我们在转发时啊,需要把我们刚才查到的数据给它,对吧,把数据给它传过去(把查询所有员工dao.findAll()所得到的list集合的数据给emp_list.jsp传过去,实际传给的是这个jsp所生成的emp_list_jsp.java这个Servlet组件进行处理),那怎么把数据给jsp传过去呢,很容易,所以这里呢,还得写一下啊,就是我们将请求转发给jsp之前,还要做一件事,就是将数据,绑定到request上,那怎么把数据绑定到request上呢,就是set,

    //将数据绑定到request上
    req.setAttribute(“emps”, list);

  • request.setAttribute("emps", list);,你看,我在转发之前呢,我把数据绑定到了request之上,我把数据存到了request对象里,那emps是数据的名字,list是值,那绑定以后,转发时呢,我把request给了jsp,那你想,我在jsp上是不是可以通过这个request得到这个值啊,那怎么得到呢,怎么得到呢,存是set,取肯定是get对吧,get就可以了啊。好了,那这个Servlet,我们就写完了,就简单的这么几句话:


package web;

import java.io.IOException;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import dao.EmpDao;
import entity.Emp;

public class FindEmpServlet extends HttpServlet {

	@Override
	protected void service(
		HttpServletRequest req, 
		HttpServletResponse res) throws ServletException, IOException {
		//查询所有的员工
		EmpDao dao = new EmpDao();
		List<Emp> list = dao.findAll();
		//将数据绑定到request上
		req.setAttribute("emps", list);
		//将请求转发给emp_list.jsp
		//当前:/jsp2/findEmp
		//目标:/jsp2/emp_list.jsp
		req.getRequestDispatcher(
				"emp_list.jsp").forward(req, res);
	}
}

  • 那这里你要注意啊,就是我们现在呢,采用的是MVC模式,或者说Model2模式解决问题,那么,在这种模式下,以后呢,Servlet,基本上都不会直接向浏览器呢,print,基本上呢,都是请求转发给jsp,由jsp输出,明白吧,所以以后啊,我们这个开发时,很少会自己再print了,在Servlet里头,基本上就不用了。那这个类写完以后呢,我 们要进行配置啊,大家呢,再打开这个配置文件web.xml,把它配置好:

<servlet>
	<servlet-name>findEmp</servlet-name>
	<servlet-class>web.FindEmpServlet</servlet-class>
</servlet>
<servlet-mapping>
	<servlet-name>findEmp</servlet-name>
	<url-pattern>/findEmp</url-pattern>
</servlet-mapping>

转发的目标文件emp_list.jsp
  • 那么配置好以后呢,还差最后一个环节,最后啊,我们需要写一个jsp,重新写一个,那么jsp的名字呢,我们刚才要求了啊,就是规定了,叫什么呢,叫emp_list.jsp,那下面呢,我们再写这个jsp啊,那么这个jsp啊,我把它写到webapp下,所以,在webapp下创建一个新的文件,emp_list.jsp。创建好以后,这个jsp怎么写,咱们刚好再巩固一下,那我们要写一个jsp,首先呢,得写那个叫什么,指令,得写指令,我们必须写的指令是什么啊,page,<%@page pageEncoding="utf-8"%>,那么,page属性上呢,除了pageEncoding属性以外,还有其他的属性,还有contentType,可以省略对吧,还有import,那这里我们需不需要写import呢, 用不用啊,用,需要写,那我们都需要import什么呢,都需要导入什么,list,还有别的么,dao,还有别的么,emp,实体类对吧,确定么,哎,总说是实体类,就是entity,确定么,有人说了,要导list,要导dao,要导实体类,3个,和昨天一样,确定么,看一下这个图:
    在这里插入图片描述
  • 我们给jsp,给的是什么,我们给它什么呢,给了它一个集合,集合里装的是什么,实体类,所以呢,jsp上要使用实体类,要使用集合,实体类和集合是必然要导入的,对吧,那DAO,jsp和DAO发生任何关系么,没有,如果你还要直接调DAO的话,那又是Model1了,明白吧,那你还要Servlet干什么呢,就直接调DAO,算了对吧,那就Model1。所以说,以后啊,我们再写代码的时候呢,你要想清楚,时刻想清楚,脑子里有这个结构,那么jsp,一定不会和业务层,直接发生任何关系,那么它只会和控制层要一个结果,明白吧,它只要结果,它不管过程,一定要了解这一点,所以啊,jsp上,我们只需要导入,集合,实体类,就可以了,<%@page pageEncoding="utf-8" import="java.util.*,entity.*"%>,导入集合啊,还有实体类。
  • 那导入完以后,我们要写jsp,首先写的是标签对吧,我们还得写什么呢,这个网页的基本结构,还得写出table,还得写出呢,一些样式,太罗嗦了,这个网页的结构,和table,和样式,之前写过对吧,我们就,直接就copy过来算了,要不太麻烦了,找到那个jsp,写过的那个find_emp.jsp,打开它,然后呢,我们把这个jsp,把它里头的那些标签,从头到尾复制一下,当然可能会多一些东西,到时我们再删掉就可以了。就从这啊,<!doctype html>,往后复制,一直复制到最后,大概40多行,然后呢,粘贴到新写的,这个jsp里(emp_list.jsp),粘贴过来,那我们就,速度,进度就快了,因为这个东西啊,咱们也不是什么新内容,没有必要再,啰里吧嗦再写一遍,那现在呢,咱们这个页面上,就有了什么,文档类型啊,doctype,根元素啊,htmlhead啊,还得有metatitlestyle啊,也有了bodybody中也有了table,标题行,那么,除了标题行之外的行,这些动态的逻辑,我们把它删掉,明白吧,重新写,重新写一遍啊,因为现在呢,代码结构发生变化了,动态的逻辑,要有所改变。

<!doctype html>
<html>
	<head>
		<meta charset="utf-8"/>
		<title>查询员工</title>
		<style>
			table{
				border: 1px  solid  red;
				border-collapse: collapse;
			}
			td{
				border: 1px solid red;
			}
		</style>
	</head>
	<body>
		<table>
			<tr>
				<td>编号</td>
				<td>姓名</td>
				<td>职位</td>
				<td>薪资</td>
			</tr>
			<%
				EmpDao dao = new EmpDao();
				List<Emp> list = dao.findAll(); 
				for(Emp e : list){
			%>
			  	<tr>
			  		<td><%=e.getEmpno() %></td>
			  		<td><%=e.getEname() %></td>
			  		<td><%=e.getJob() %></td>
			  		<td><%=e.getSal() %></td>
			  	</tr>
			<%
				}
			%>
		</table>
	</body>
</html>

  • 所以呢,我们把,从这啊,<%EmpDao dao = new EmpDao(); . . . %>,从这个动态逻辑的,这个地方开始,到动态逻辑的最后</table>之前,这个范围,标题行之后,就是我们在for循环里输出的这一行代码,删掉,删掉以后呢,我们再看这个table,table中只有默认标题行对吧,就这样了。然后呢,这回,我们在这,要重新写这个动态的逻辑,重新写这个逻辑,在标题行之后,写一个jsp脚本,那我现在,我怎么去写这个代码,有人说接收,我要接收传过来的那个集合,对吧,我怎么接收,通过什么接收呢,通过什么接收啊,通过request,是这样吧,通过request。那我在这要用request,我怎么去写呢,就直接写request,这个单词对吧,必须这样写,那为什么能直接写呢,因为它是隐含对象,对吧,在jsp这个代码执行之前,request这个对象已经被创建了对吧,被传入了,你要心里有这样一个想法,我们jsp中所写的代码,最终,这个代码是运行在,Servlet的service方法内部,对吧,我们写的这些代码,它都是那个service方法中的一句话对吧,那么在这句话之前(jsp中的代码执行之前),request和response,已经传入了,可以直接调用。
  • 那么request,要获取那个数据,绑定那数据,怎么办呢,request.getAttribute("emps");,当时,我们存的数据的名字叫emps,是吧,按照同名去接收,那么这个,得到的数据啊,类型是Object,因为它是传任何数据,所以我们需要转型了,对吧,把它转为一个集合啊,List<Emp> list = (List<Emp>)request.getAttribute("emps");,那我们得到数据,类型是Object,但是我们存的确实是集合,我们可以强转,我们转为集合以后啊,对集合处理,要循环对吧,那写个for循环,循环好输出啊,这数据。

	<%
		List<Emp> list = (List<Emp>)request.getAttribute("emps");
		for(Emp e : list){
	%>
		<tr>
			<td><%=e.getEmpno() %></td>
			<td><%=e.getEname() %></td>
			<td><%=e.getJob() %></td>
			<td><%=e.getSal() %></td>
		</tr>
	<%
		}
	%>

  • 写完以后,访问http://localhost:8080/jsp2/findEmp(Model2模式),发现和之前,访问http://localhost:8080/jsp2/find_emp.jsp(Model1模式)的结果是一样的,实现了相同的效果。
完整代码实现(项目jsp2)
1.webapp下目录下的emp_list.jsp
<%@page pageEncoding="utf-8"  import="java.util.*,entity.*"%>
<!doctype html>
<html>
	<head>
		<meta charset="utf-8"/>
		<title>查询员工</title>
		<style>
			table{
				border: 1px  solid  red;
				border-collapse: collapse;
			}
			td{
				border: 1px solid red;
			}
		</style>
	</head>
	<body>
		<table>
			<tr>
				<td>编号</td>
				<td>姓名</td>
				<td>职位</td>
				<td>薪资</td>
			</tr>
			<%
				List<Emp> list = (List<Emp>)request.getAttribute("emps");
				for(Emp e : list){
			%>
				<tr>
					<td><%=e.getEmpno() %></td>
					<td><%=e.getEname() %></td>
					<td><%=e.getJob() %></td>
					<td><%=e.getSal() %></td>
				</tr>
			<%
				}
			%>
		</table>
	</body>
</html>
2.WEB-INF目录下的web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
  <display-name>jsp2</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  
  <servlet>
  	<servlet-name>findEmp</servlet-name>
  	<servlet-class>web.FindEmpServlet</servlet-class>
  </servlet>
  <servlet-mapping>
  	<servlet-name>findEmp</servlet-name>
  	<url-pattern>/findEmp</url-pattern>
  </servlet-mapping>
</web-app> 
3.src/main/java目录下的dao包下的EmpDao.java
4.src/main/java目录下的entity包下的Emp.java

员工查询案例之Model1与Model2:

  • 那Model2模式下的员工查询案例就完成了,即用这个MVC模式有把这个员工查询写了一下,这个Model2模式员工查询功能和那个Model1模式,jsp做的那个员工查询功能相对比,可以发现呢,这个Model2更麻烦了,麻烦在哪呢,Model2中新写的这个jsp,和Model1写的那个jsp,这代码量差不多,几乎是一样的。Model1模式不就是写了:

<%
	EmpDao dao = new EmpDao();
	List<Emp> list = dao.findAll(); 
	for(Emp e : list){
%>

  • Model2呢是

		<%
			List<Emp> list = (List<Emp>)request.getAttribute("emps");
			for(Emp e : list){
		%>

  • 基本上代码量一样,但是呢,你要知道,我们Model2这是多了一个Servlet是吧,还多了一段配置(对Servlet在web.xml中的路径配置),有人说,你这MVC模式,这没简化啊。但是你要,你要注意,我说过我们用MVC模式,一定会让代码简化么,一定会让代码少么,没有,说的是什么呢,我们用MVC模式的目的,是要解耦是吧,降低耦合度,那看我们现在的目的达到了么,对比一下啊,你比如说,Model1模式写的jsp,find_emp.jsp,如果说啊,我们这个业务发生了变化,List<Emp> list = dao.findAll(); 这不单单是要查询,查询完以后,还要对数据进行一系列的运算和整理,那这个运算和整理代码,我们一定要写在jsp里对吧,你在find_emp.jspList<Emp> list = dao.findAll();这查的,就得在这写,所以,这段代码可能会很多,那比如说,你要对它进行加工和整理的代码,长达100行,那这100行,增加到jsp里对吧,进一步增加了jsp的耦合度。

  • 那反过来,我们Model2做的这种方式,如果说呢,我们查询的逻辑发生了变化,要进行运算和整理,我需要在jsp里处理么,在这个emp_list.jspList<Emp> list = (List<Emp>)request.getAttribute("emps");这里处理么,不需要,我只需要在Servlet当中,加以处理就可以了,那么多的呢是,FindEmpServlet中,这块的代码:

    //查询所有的员工
    EmpDao dao = new EmpDao();
    List list = dao.findAll();

  • FindEmpServlet中查询所有员工之后,这块的代码,那jsp呢,emp_list.jsp,还是只要一个结果对吧,中间的过程,它一概不管,所以说啊,我们确实呢,把耦合度呢,降低了,目的是达到了,当然了,你像这个一些小案例啊,像现在这样的,这么简单的逻辑,我们用Model1模式,去实现也没什么大问题,那么,我们用Model2模式呢,可能是反而,更啰嗦了,但是呢,主要是因为什么呢,我们案例比较简单,我们杀鸡呢,用了牛刀,稍微显的费劲,但我们将来工作时啊,这个业务一定是比现在复杂多了,所以,用这个Model2模式,用MVC模式,那是十分有必要的,所以工作时,一定会用,MVC模式的,这是主流。那这个MVC模式啊,我们先初步有这样一个演示,有这样一个了解,后面我们在做电信计费的项目的时候,以及呢,以后我们做云笔记的项目的时候,我们的项目呢,都会采用这种方式去开发,工作时也会这样,所以以后,就这是常态了。

转发与重定向

转发的代码解释

  • 那么在我们探讨员工查询这个案例的时候,这里面呢,我们提到了一个新的知识点 ,叫转发,那转发呢,用法并不麻烦,就一句话对吧,就一句话,getRequestDispatcher(),然后呢,forward,(这里通过getRequestDispatcher是获得一个对象RequestDispatcher转发器,并将需要转发的目标访问路径传给这个对象,然后调用这个对象的forward方法传入参数request和response,对请求和响应数据进行相应的处理,并返回给需要转发的目标文件),那么,可以把呢,请求,交给jsp继续处理,说白了,是tomcat在底层,它调了一下jsp,当然它,我们说调jsp,它其实调的是jsp,所生成的,那个Servlet的,那个service方法,能理解吧,它悄悄的调了,调了那个东西,就是,我们写这句话,req.getRequestDispatcher(“emp_list.jsp”).forward(req, res);,tomcat调的是,翻译之后的那个Servlet,调的时候呢,传入了request和response,然后呢,我们在转发之前呢,绑定了数据 ,req.setAttribute(“emps”, list);,这个没什么好说的,这很容易了。

基础面试考点

  • 那关于转发呢,我们需要再,详细再解释一下,转发,我们需要呢,好好理解理解,好好记一记,因为转发呢,这件事也是面试时很容易问的,就是我们这段课啊,有很多这个,这个面试题或笔试题,像之前讲的什么jsp隐含对象对吧,像这个MVC模式,像这个转发 ,像这个重定向,那这些内容,都很常问,而且呢,哪一个问题,你说我不会回答,或者回答的不好,给人印象是极其糟糕的,因为这些东西呢,它就是web项目的基础,这些内容,你没有理解,或者不会运用的话,说明你web项目不会做,说明你做,即便你做出来了,也是抄的,不是你自己的能力,体现不出你自己的能力,所以一定你是要理解的啊。

转发的书面定义

  • 转发,有些书上呢,是这样说的:

    什么是转发
    一个Web组件 (Servlet/JSP) 将未完成的处理通过容器转交给另外一个Web组件继续完成。
    常见情况:一个Servlet获得数据之后 (比如通过调用dao),将这些数据转发给一个JSP,由这个JSP来展现这些数据 (比如,以表格的方式来展现)。

转发与重定向区别概述

  • 它说的也是非常的术语化,说的呢,不够通俗,不足以让人去理解,但贵在理解。所以我们换个角度,用一个通俗的方式去理解,再一个呢,其实,转发这件事,和重定向,它有相似之处,往往面试时呢,会把这个转发啊,重定向啊,做一个对比来问,一道很常见的面试题是,转发和重定向的区别,所以呢,这里,我需要将转发和重定向,对比的来说。那关于转发和重定向,第一个小的话题,是转发和重定向,它们的相似之处是什么,就是它们的相同点,那么,它们有相同点,当然,更多的是有不同点,它们的区别,那么,它们的区别,这个是重点,这个是要记下来,是要理解的,面试时就问这个,然后呢,最终呢,给出一个,使用建议,我们什么时候,使用这个转发,什么时候用重定向,给出一个建议。我从这3方面,解释这个问题。
  • 首先呢,我们说它们的相同点,那重定向(见 Servlet3中的员工增加案例 ),能解决什么问题,网页跳转,因为在Servlet3的员工增加案例中,是从这个增加跳转到了查询,跳转了,甚至呢,互联网上,不相关的网站也可以跳转对吧,它能解决网页的跳转,那网页是什么,就是个组件,动态网页就是个组件,就是我们说的Servlet或者是jsp,那在 Servlet3中的员工增加案例 使用重定向的时候,是从一个Servlet,从add(AddEmpServlet)跳转到了find(FindEmpServlet),跳转,或者说,我们从根上说呢,它解决的是,两个组件之间的跳转,两个组件就是两个动态网页,那其实转发也是一样的。在 员工查询案例(Model2) 中,转发是不是从Servlet到jsp,也是两个组件之间的跳转,所以,其实呢,无论是重定向,还是转发,它解决的问题都是,都是解决两个组件之间的跳转问题。就是都是解决两个,就是web组件之间的跳转问题。

web组件的解释

  • 什么叫web组件呢,那组件是什么呢,满足规范的对象对吧,那web组件是什么呢,是web项目当中,解决web项目当中,一些特定的对象对吧,说白了,就是Servlet或者是jsp,所以啊,你想从一个Servlet,跳到另外一个,你想从一个JSP,跳到另外一个,或者甚至呢,你想从Servlet跳到jsp,任意的两个web组件之间的跳转,你要么转发,要么重定向,明白吧,都能解决问题,所以说,它们的相同点,都是解决两个web组件之间的跳转问题,或者说解决请求的跳转问题也可以,这是相同点。然后呢,我们再说一下,它们的区别啊,重点是区别。这个区别啊,不是说一两句话,能说清楚的,我们需要呢,有点长篇大论了,画个图吧。

转发通俗的理解

  • 那么,想说清楚这两者区别啊,我从两个角度去说明,一个呢是,我们说一个现实中的,一个生活的场景,说一个比较通俗的场景,谁都能理解,透过这个场景,我们得出一些结论,然后呢,在这个结论基础上,我们归纳出这个,一些术语,然后呢,面试时去回答,所以我们从通俗去理解,最终得出一个,更学术化的结论,这样好记。那第一个层面,我们先讲一个什么呢,就是通俗的理解,通俗的理解转发和重定向的区别,那举一个生活化的小例子,左边一个方块,右边一个方块,假设呢,左侧,假设左侧啊,这个方块,它是范传奇,哈哈,假设啊,假设右侧呢是,王克晶他们家,就王克晶吧, 就是怎么说呢,很多年以前呢,范传奇还小,那个时候呢,他是一个小传奇啊,然后呢,王克晶呢,跟他一样,那也很小对吧,发小啊,小克晶是吧。
  • 然后,传奇小的时候呢,就不太正经,当然现在更不正经了,是吧,那干什么呢,他小的时候,很小的时候啊,就早熟啊,就向克晶呢,就是说,写过情书,那你看啊,一个人给另一个人写情书,我们可以理解为接收者,是一个服务器,发起者是一个客户端么,发出爱的邀请,是这意思吧,这种感觉啊。传奇给克晶写了个情书,写啥,我就不太明确了,这事我知道啊,然后呢,你注意,克晶呢,那时候,还比较小,还算是比较单纯的,然后呢,她一看,她有字还不认识呢,她看不懂,这啥意思呢,那小孩子啊,一些事不懂,她会怎么解决呢,哎,她问大人,她会问谁呢,她不会说上街找一个陌生人去问对吧,她一般会回家,问谁啊,问父母啊。
  • 所以,克晶呢这事,告诉了她妈,克晶妈,她把这个信呢,直接给了她妈妈,那她妈一看,传奇啊,这小孩子这么早熟,但是,都是邻居住的,还不能打他,怎么办呢,还不好打击他的积极性,这毕竟是一个美好的愿望,是吧,于是呢,她妈呢,以这个克晶的这个语气,或者以克晶的这个身份,悄悄的回了封信,丢在了传奇的书桌上,传奇得到这封信以后,他误以为是克晶给的,是这意思吧,他以为克晶给的,然后呢,这封信里大概写的什么呢,说你还小啊,长大再说吧,大概这意思啊,总之得出一个结论,传奇一看啊,还小,那等着吧,就这样啊,那么,在这个场景当中啊,就是这,小克晶这,她处理不了问题,她把这件事,交给她妈妈处理,这个行为,就符合我们所说的,转发还是重定向呢,转发。
    在这里插入图片描述

重定向通俗的理解

  • 所以,我们得出,很容易能得出一个结论,我们什么时候考虑用转发呢,就是服务端的这个组件,自己解决不了问题,必须别人帮忙的时候,转发,或者说两个组件,它们之间有依赖关系的时候,得用什么呢,转发啊,总之我们得出一个结论啊,俩组件依赖时,用什么呢,转发,这是一个通俗的结论,依赖时用转发,就像这个故事,所讲的一样,就这样。那这是一种场景啊,我们得出这样一个 结论, 再说另外一个场景,那人呢,终归是会长大的,那此时此刻呢,这俩人都长大了,那长大以后呢,那传奇就不是小传奇了 ,大传奇啊,克晶也不是小克晶,大克晶啊,那都长大了,传奇呢,这事念念不忘,又给克晶呢,发了一封信,说你看,当年,我向你表达过,你说,我小,现在我大了,看,我有权利了,怎么样,克晶呢,此时她也大了,她已经具有独立的人格,已经能够自己解决,这种问题了,对吧,所以呢 ,她不用再找她妈了,她直接就可以给传奇,立竿见影的回一封信是这样吧,给个回复,行就行,不行就不行,那比如说,克晶回的是什么呢,不行,都有孩子了,行啥啊。
  • 那不行怎么办呢,那克晶一看,哎呀,等了这么多年,20多年,这也不容易,给个建议吧,说也挺可怜的啊,说这个苍老师不错啊,你找苍老师去,说找苍老师啊,苍老师很好,然后传奇呢,这个小伙啊,怎么说呢,贵在,贵在这个,额,贵在这个听话啊,建议他找苍老师,他就去找了,那真找苍老师去了,但是苍老师比他们还大啊,然后呢,苍老师呢,更是具备了独立的人格,可以直接呢,给他回复啊,直接给他回复,苍老师回什么呢,苍老师回了一个他的,这个经常说的一句话,这句话可能有的人还不认识,Yamadi,是吧,这苍老师经常说的一句话。反正别管是什么,给了一个回复,传奇收到这个回复。
  • 那在这个场景里,克晶和苍老师,是一个服务者对吧,服务者,服务传奇,别管是成功与否,是服务了,进行服务了。那这两人,克晶和苍老师之间的关系,满足什么关系呢,重定向,它就不是转发,因为什么呢,大克晶她具备独立人格,那克晶收到信以后,那不能说,苍老师你处对象吧,这不太合适对吧,她可以给出建议,说传奇啊,你找苍老师,传奇去找苍老师,那这个没问题,所以,这个场景,它俩之间呢,大克晶和苍老师之间,没有依赖关系,两者是彼此独立的,那么不互相依赖的两个人,两个对象,两个组件,之间你想跳转,用什么呢,重定向。所以又得出一个结论啊,就是说不依赖时,我们用什么呢,重定向。
    在这里插入图片描述
  • 那最终呢,我们就通过这个小故事啊,想给大家说清楚这样的,就是得出这样两个结论吧,那么关于web组件,web对象,Servlet或者jsp,如果你想,在两者之间跳转,你就看两者的关系,如果两者的关系是依赖的,那就用转发,如果是不依赖的,就重定向,就是这么一个原则。那如果这个故事很好懂啊,看谁依赖不依赖,但这俩对象,依不依赖,就不容易搞清楚,那没关系,项目中再体会,那个套路你记住就好了,就是查询时是,依赖还是不依赖,增加时是依赖还是不依赖,有一个固定的,就是场景,记住也可以,所以别着急,慢慢来吧,慢慢体会,但我们尽量去理解啊,这一点。

转发专业的理解

  • 那当然了 ,这只是说的一个比较通俗的例子,只是为了得出一些结论,那根据这个结论呢,我们还需要做出个总结,总结出呢一些,比较学术化的,比较术语方面,专业方面的理解,咱们不能把这个理解,只停留在这个故事上对吧,你面试时说这个故事啊,面试官就疯了,所以呢,我们还得再再,提升一下,升华一下,给出一个专业的理解,那这个专业的理解,怎么办呢,背下来,背啊,这是一道笔试题啊,所以你得背下来。那么,我们专业的理解,就别去说人了,咱们就说两个对象,两个组件,那到底是哪两个组件呢,不一定,谁都行,所以呢,我们需要,这个比较泛泛的来讲啊,比如说这是浏览器啊,右侧呢是服务器,然后呢,比如说,这个浏览器要访问服务器,访问服务器端的一个组件,这个组件叫A,行吧,大概写个名字啊,叫A,那浏览器要访问服务器的A组件,我们在地址栏,或者在按钮上,写的一定是A的路径对吧,这是显然的,我们通过A的路径,访问A组件,然后呢,A组件解决这个业务,那如果说A组件,自己解决不了这个业务,需要B帮忙,那显然就是转发了,对吧,就A转发到B,因为它互相依赖了。
  • 那最终的话,浏览器得到的结果是谁提供的呢,其实是B提供的,是B提供的结果,但这件事,浏览器知道么,不知道,想之前的那个故事,范传奇给王克晶写封信,王克晶把信给她妈了,她妈悄悄的回了封信,传奇看到信以后,还以为是克晶回的,是这意思吧,他不知道这件事,这是悄悄的,所以呢转发这件事,浏览器不知道,反正浏览器得到一个东西,它就认为啊,这就是我访问者给我的,是这样的。那么根据这个原则吧,我们做一个归纳啊,这个转发的基本特征是什么呢,有如下几点,就转发的特点啊。
  • 第一点,整个转发的过程中,咱们需要包含几个请求呢,有几个请求来实现呢,这是不是就一个请求啊,是吧,一次请求,只包含一次请求,然后呢,这个转发完以后,地址栏,是否会发生变化呢,不会变,不信你看,刚才我们写的那个案例:员工查询案例(Model2),我访问的是findEmp,我得到的这个表格,这表格是jsp给的对吧,地址栏没变吧,地址栏为啥不变呢,因为浏览器根本就不知道,有那个jsp存在对吧,它不知道那个东西存在,它就以为是findEmp给的, 没变。所以呢,转发以后,地址呢,是不变的。
    在这里插入图片描述
  • 那第3个,那么,转发的话,因为是一次请求,那么一次请求当中, 服务器会创建几个request,一个,一次请求,服务器new一个request,那由Servlet原理,和jsp原理可知,咱们是一次请求,服务器new一个request,所以,一次请求只有一个request,所以呢,那A和B是不是共用一个request,是,那是不是可以用request来共享一些数据呢,是的,咱们之前的案例:员工查询案例(Model2),不就这么干的么,在A这,往request存了个数据对吧,B取到了,是这样吧 ,就这么干的啊。所以呢这个,一次请求,服务器只创建一个request,那A,就两个组件,可以通过request共享数据,这是第3个特点。
    在这里插入图片描述
  • 还有最后一个,就是说转发的话,这个A有没有可能转发到外面去,就我们写的项目,就我的A,能不能直接转发到百度去,转发到淘宝去,可不可以,能不能行呢,不可以,这个转发,是一个私下的事对吧,是悄悄的事,我们这个软件,不可能访问别的软件,对吧,A没有能力,访问其他软件中的对象,明白吧,没有这个能力,没有这个权利,所以,这个转发只局限在一个项目的内部,就是只能转发到项目内部的资源,或者内部的组件,无法转发到外部。那么这是转发的4个特点,这4个特点你要记下来,当然,面试的时候啊,如果是笔试的话无所谓,你就写吧,面试的时候呢,最好经过一个整理,按照自己的这个语言,把它说出来。然后,面试的时候呢,你在说的时候,可以稍微先想一下再说,不用说面试官,刚说完这句话,你马上就得回答,不是,你说,我稍微想一下,可以的,这是转发。

重定向专业的理解

  • 再看重定向。还是浏览器和服务器的关系啊,比如说,浏览器要访问服务器的A组件,那假如说呢,A组件的这个处理能力比较强,它自己一个人呢,就解决了问题,那有人可能会想,那什么组件能一个人解决问题呢,什么样的业务,能一个组件就解决问题呢,比如说删除,你看,我要删除的话,我访问服务器,传个参进去,要根据什么来删除数据,那服务器,我们肯定是写一个Servlet对吧,接收参数,把数据删掉,那删掉以后,咱们有内容要给浏览器看吗,数据都没了,对吧,就没了,没了以后怎么办呢,你也不能让浏览器空着对吧,应该回到查询对吧,应该跳到查询,所以说,它能够直接呢,处理这个业务,但处理完以后呢,没有什么可给浏览器的对吧,那应该跳转到另外的一个地方去,另外一个组件上去,那另外个组件呢,又和A没有关系,怎么办呢,就重定向。所以重定向呢,往往是解决这样一类问题,比如说删除时重定向到查询,A是删除,B是查询,那么,这样的话,就适合用重定向,因为它俩呢,互相之间呢,没有依赖关系,彼此是独立的。
  • 那你看啊,这个重定向的话,重定向这件事,浏览器知道么,知道,浏览器的地址栏会发生变化么,会,因为这是浏览器自己访问的B,对吧,它自己去访问的,它知道,所以地址栏变成了谁啊,是不是变成了B啊,地址栏由A变成了B,发生变化,当然最终结果,也是由B提供的,那浏览器是知道的,那整个过程包含了两次请求,对吧,大概是这样。那么,我们再归纳一下,重定向的一些特点啊。第一点,那重定向有几个请求呢,两次请求,然后呢,这个重定向,与转发对照的说,它地址会发生变化么,会变,地址会改变,而转发的话,地址是不变,而第3点,转发呢是一次请求对应一个request,对吧 ,通过request,两个组件可以共享数据,那么重定向,是两次请求,两次请求是不是有两个request呢,是的,就是浏览器访问服务器,每次访问,服务器都给它创建一个新的request,用来处理这个请起,处理完以后,请求结束时连接断开,连接断开的话,request和response,一定会释放,明白吧,是吧,那连接都断了,一定会释放,所以请求结束时,request和response,就销毁了,没有了,再请求,再创建。
    在这里插入图片描述
  • 所以呢,第3点这个,两次请求,服务器会创建两个request,那么,两个组件无法通过它共享数据,那有人说,那我两个组件,想共享数据怎么办,这个将来我们会说,我们之前提到的那个session,就是jsp隐含对象中,有个对象叫session,我说很常用,可以由它来解决,后面会说。最后,那重定向能重定向到项目之外的这个组件么,可以,因为重定向这件事,浏览器自己干的对吧,它有权利访问任何人,所以可以重定向到项目的外部的组件,那么以上啊,是我所归纳的,转发和重定向的特点,它们的特点对比的来看,各有4个特点,这个特点呢,要去记啊,或者说呢,你把这事记住,然后的话,为你手机上设个提醒,将来快这个面试了,现背也行,现在背的话,估计也会忘,反正自己想办法提个醒,以后要背,这很重要。但要了解,要理解。这是它们的区别。

转发和重定向使用场景简述

  • 关于转发和重定向,了解了它们的相同点,也知道了它们的区别以后,我们给出使用建议,目前先建议,后面做项目时,也会遵循这个建议,去解决相关的问题,再去体会。那我们什么时候,用转发呢,就通常查询时用转发,查询时用转发,我们用Servlet处理查询的逻辑,把结果转发到jsp,加以显示,之前模拟做的这个 员工查询案例(Model2),不就这么干的么对吧,查询时用转发,然后呢,通常,增加,修改,删除后,重定向到查询,之前模拟做的那个 Servlet3中的员工增加案例 ),不就这么干的么。那先建议到这,后面做项目时呢,这些东西都会用到,都会练到,那么关于这个转发和这个重定向,咱们就说完了,一般的书上所描述的也是这个意思,自己总结的会更全面一些。

8.总结

  • 继Servlet以后呢,这里又讲了新的内容是jsp,那么这个jsp啊,是Servlet的一个升级的方案,它对Servlet呢,有所改善,那为什么说Servlet需要改善呢,因为它有的地方有问题,它最主要的问题呢,就是,书写起来麻烦,我们用它呢,开发一个动态网页,太费劲了,那我们需要一行一行的,输出网页中的标签啊,然后呢,如果网页比较大,样式比较多的话,这很不方便,再一个呢,也不利于团队配合,那么美工呢,给我们开发出了静态页面,我们需要呢一行一行的print,那美工呢,却没法写这段代码,所以这个,一件事,必须两个人完成,那比较啰嗦。那么,它这么做,它这么啰嗦的本质的原因在于呢,它是在java代码内部去写标签啊,那么jsp呢,对此有所改善,那jsp呢,是这个想法呢,是相反的,它是让我们先写标签,那我们再写代码时呢,可以把美工给我们的这个标签,直接粘贴过来,这样就省事了,然后的话呢,里面有哪些java代码,再去填啊,这个效率就提高了。
  • 那么总体而言呢,这个jsp,它的作用和Servlet一样,也是用来动态的生成了一些资源的,拼动态资源的,然后呢,这个,只不过呢,它的拼的这个方式,更科学一点,那往大了说呢,它是处理,也是处理这个http协议的,这是jsp。那么jsp具体怎么使用,咱们后面做了详细的讲解。这个jsp啊,它在书写的时候呢,是一种特殊的文件,后缀呢,就叫jsp,然后呢,它的内部可以包含呢,很多内容,首先呢,可以包含网页啊,html,css,js,这样的内容,可以写注释,可以写java,可以写指令,可以写隐含对象,那每一部分内容呢,咱们上次课,都有演示,但这里面呢,最主要的还是这个html和java,那我们在jsp里呢,写html,直接写。注释有两种,一种呢,是html注释,第二种呢是jsp注释,那html注释呢,它只能注释掉标签,如果里面有java代码,注释不掉;那么第2种呢,jsp注释,能注释掉一切,标签,java,都管用。那我们用的时候呢,看情况,或者你都用jsp注释也可以,<%-- --%>
  • 然后呢,jsp中呢,java代码,有3种情况,一种呢,叫表达式,一种呢,叫脚本,一种叫声明,昨天也说了,一般呢,不建议我们写声明,如果你想声明方法,想声明什么,你在一个类中写,我们可以把这个类,引入到jsp里去调,这样的话呢,我们尽可量的减少jsp中的这个java代码,让这个程序呢,更利于看啊,也更利于维护,如果说,你这个标签里面,java代码套的太多的话,这个也不好维护,耦合度还是高,那么jsp表达式呢,它的语法呢,是这样的,就是有个等号,等号里面呢,你要写的是变量,或者是变量的运算,或者是呢,这个有返回值的方法,总之呢,这个表达式,它的作用是用来输出呢,这个值,所以,你给它写一个值就可以了。然后呢,脚本是这样写,<% %>,没有等于号,那这里面呢,可以写完整的java代码段,你可以写for啊,写if啊,任何的完整的java语法,那声明的话,前面是个感叹号,这个简单了解。
  • 那么jsp啊,我们在书写的时候,咱们一开始呢,第一行,写了一个东西,那个叫指令,而且指令呢,不止一种,有3种,page,include和taglib,我们先讲了前两个,page和include,最后说的taglib。其中呢,这个page指令,它呢,有几个属性啊,一个是pageEncoding,那这个属性呢,用来声明,jsp这个文件的编码,第二个呢,是contentType,用来声明这个jsp,向浏览器啊,输出的内容的格式。第3个呢,是import,用来导包的,上面都用过啊,然后呢,include用来,将一个jsp引入到另外一个jsp之内,用来引入的,那很多时候呢,我们的网页,那很多网页结构很相似,比如说,所有的网页,顶部,都有这个用户名,所有的顶部都有时间,那么这些内容呢,公共的内容,重复的内容,我们可以呢,把它提取出来,然后呢,引入到,各个页面上去,这样的话呢,将来那个地方的业务发生变化了,我们只要改一改文件,而不是全部都改,这样比较合适。
  • 那说完这个话题以后,又说了jsp的原理,那么jsp运行原理,我们讲的也是啊,这个jsp组件,它处理请求的完整的过程,这个过程呢,通过图我们能看到,它和之前我们所讲的Servlet运行原理,几乎是一样的,那么,整个流程也是遵守这个http协议,那么主要的区别在于这,翻译,就是说jsp,服务器啊,接收到请求以后,已经创建好了,request和response,那服务器呢,最终要调用jsp,它找到jsp,找到以后呢,那这个jsp啊,它并不是java文件,它也不是html,它是不能直接运行的,所以呢,服务器呢,会对它进行翻译,将它翻译成一个Servlet,然后呢,由这个Servlet去,将它转换Servlet后做输出,所以呢,我们得出了一个结论,jsp本质上就是一个Servlet。
  • 然后呢,在翻译的时候,这个服务器啊,进行翻译,它翻译时啊,它需要读取jsp中的内容,那读取的时候呢,它需要知道,这个jsp文件的编码是什么,那么它会看呢,这个jsp的文件的第一行,看这个pageEncoding。那么翻译好的Servlet,在响应的时候,它需要写那句话,response.setContentType("text/html";charset="utf-8"),那这句话,从何而来呢,是根据hello.jsp,是根据jsp中的contentType属性,生成的,那么,我们ContentType属性里写什么,那么这里面(根据jsp所生成的Servlet里面),那句话response.setContentType(...)就写什么,啊,是翻译成了response.setContentType(...)。那总而言之啊,这个contentType属性,它是给浏览器用的,它是告诉浏览器,我输出的格式是什么,编码是什么,那么通常呢,这个属性不用写,因为它有默认值,默认的格式是这个text/html,默认的编码和jsp中page指令的pageEncoding属性一致,所以不用写。
  • 那么jsp翻译,生成的这个Servlet,我们也看了,确实有那么一个类,隐藏的比较深,然后呢,通过对这个类的观察啊,我们就,推出了这个翻译的过程,那翻译的过程呢,大概分为3步,第一步,我们有什么样的jsp文件,tomcat就把它翻译成,对应的那个Servlet,名字呢,是有对应关系的,所以第一步呢,就是创建java文件,第二步呢是翻译,那你jsp中有什么代码,就要把它翻译成Servlet当中的,相关的代码,那么在翻译的时候,它是这样的。首先呢,它先声明一大堆变量,然后呢,对于jsp中,我们所写的标签,它write,write就是向浏览器输出,然后呢,对于这个我们所写的表达式,它就print,对于我们所写的这个脚本呢,它保留,总之呢,对于jsp里面呢,不同的内容,它做了不同的处理。
  • 那么,在这些代码之前,因为有了变量,那这个变量呢,显然,可以在这些代码里调用,那这些代码就来源于jsp,所以呢,我们就理解,这些代码就是jsp代码,所以,我们能得出一个结论,就是jsp的代码里啊,可以直接调用这些变量,那正因为如此,所以呢,这个变量,我们给它取个名字,叫隐含对象,或者叫内置对象,就直接能用的。那翻译完之后啊,这是一个java文件,需要编译才能调用,所以呢,第3步是编译。那么,说完这个话题以后,咱们就引出了这个变量,隐含对象,那下面呢,我们归纳出了隐含对象,它的这个,有哪些,然后呢,这个一定要记下来,因为这是个笔试题,一共有9个,分别是request,response,out,config,application,exception,session,page和pageContext。那么,这9个对象呢,它的名字,就是变量名,是固定的,所以我们在用的时候呢,一定是写这样的名字,是不能任意发挥的。
  • 那具体怎么用呢,那我们也给出了一个例子,我们在jsp脚本里,或者是jsp表达式里,你想用的话,直接拿来用就可以了,直接写那个变量名,调用它的方法,就行了。那至于说哪个对象有哪个方法,那咱们用到时再说,用不上就算了,有很多对象啊,其实我们工作多少年都用不上,没什么用,但是呢,可能是极特殊情况会用到,所以呢,它也考虑进去了,那不常用的,我们就不演示了,常用的,我们后面都会演示啊。然后呢,每一个对象啊,它的类型,咱们也做了一个解释,那这里呢,就不一一回顾了,就是说你了解下就可以了,也很直观。那么常用的对象只有3个啊,一个是request,这个很常用,一个是session,目前还没讲呢,后面会讲,一个呢是pageContext,目前呢,也没用上,后面会用。那值得注意的是这个pageContext,那这个对象啊,它是一个管理者,它能管理其他的8个对象,如果我们得到它的话,其他的8个对象,就都能得到,所以,它是起到这样个作用,后面会演示。
  • 那么说完隐含对象这个话题以后呢,按照书上来讲,我们下一个话题应该讲的是转发,但是,我没有直接讲转发,我是讲了,就插入了一个话题啊,这个话题呢,很重要,但书上呢没有写,有点这个缺失了,我们补充一下,讲的是什么呢,开发模式,那这个java的web项目呢,是有一个发展阶段的,其实主要有3个阶段,早先那个开发模式叫Model1,第一阶段,然后,Model2第二阶段,现在最新的是,算是Model3,用框架解决问题,不过呢,用框架解决问题,咱当前呢,还没法讲,因为那是后面要讲的内容,所以我们当前呢,只探讨前两个话题,前两个阶段,那么,第一个阶段呢是我们程序员在处理浏览器的请求时,我们采用一个组件来处理,或者是Servlet,或者是jsp,尽管是,jsp比Servlet更好一点,但是呢,也是有限的,那么,jsp这个组件当中的,这个代码还是耦合度太高了,它里面有大量的标签,又有大量的java,对吧,耦合在一起,那一旦呢,这个代码量超过千行以后,是很难维护的,这个项目呢,是很容易失控的,所以呢,这种开发模式看起来结构简单,有利于学习,但工作时,它不利于代码维护,所以呢,这种方式,已经被淘汰了,我们工作时基本上不会遇到。
  • 然后呢,Model2,第2种开发模式,是Sun呢,引用了一种设计模式,利用这个设计模式,改善了对象的关系,第一种模式下,是这个对象耦合度太高 ,耦合度太高怎么解决呢,主要的解决方案就是,想办法拆开解偶,拆开就好了。那么Sun呢,是这样想的,它感觉呢,有两个组件都能处理请求,一个是Servlet,一个是jsp,那这个两个组件的话,Servlet更适合处理java代码;jsp呢,更适合写标签,所以呢,它让这个两个组件相互配合,那么浏览器呢,发出的请求,我们统一呢,让Servlet处理,因为它处理请求时呢,需要接收参数对吧,这些是java代码,那么在处理请求的过程中呢,如果说有必要的话,可以调DAO,调了DAO以后,得到了数据,可以呢,对数据进行加工整理,整理完以后,将最终的结果给jsp,jsp呢,得到的是最终想要输出的结果,直接输出就可以了。那这样的话呢,能最大程度的保证jsp当中啊,主要以标签为主,有少量的java代码,那结构呢,相对来说是比较清楚的,耦合度呢,是比较低的,这是比较合理的。那这种解决方案呢,是引用的一个经典的设计模式啊,叫MVC模式,或者说这是一种架构思想。
  • 那么MVC模式呢,是一个经典的模式,是一个分层的思想,它的想法是,将软件呢,分为3层,M层,V层,C层,分别指的是业务层,啊,视图层,和这个控制层,然后呢,每一层,解决不同的问题,说白了,我们干一个活,最好不要一个人全干,这样的话,对一个人的依赖程度太高了,最好是你们大家这个,互相配合干,每个人只干一点点,如果某环节,某个人,说我离职走了,或有问题了,换个人顶替他,也比较方便。所以呢,这个MVC模式啊,这种想法,其实就是来源于生活,来源于这个,咱们人和人之间相互配合的这么一种情况,那这个模式呢,它的主要目的,是为了降低软件中啊,这个代码的耦合度,有利于团队的开发及维护,那么,在上面的这个图当中呢,jsp它是视图层,用来展现数据,Servlet呢,是控制层,用来这个衔接,这两者的关系,用来调度;然后呢,DAO呢,是Model层,用来处理业务,增删改查。
  • 当然了,那么我们在工作的时候啊,这个分层,可能会分更多层,MVC呢,只是一个最基本的分层思想,我们在做的时候,可以再进行呢,深化细化,可以分为456,更多的层次,那么无论呢,工作时,这个项目分几层,它都是源于这个三层结构。
  • 那通过DAO+JSP+Servlet的结构,可以实现MVC模式,在MVC模式下,JSP与Servlet之间是需要进行数据交互的,Servlet是控制层,它需要将DAO处理后的数据,传给JSP进行显示,那这个由Servlet跳转到JSP,并传递数据的过程,就由转发来完成,转发可以解决两个web组件之间跳转的传递问题,并传递数据;在 Servlet3中的重定向 中可以知道,重定向也解决了两web组件之间的跳转问题,另外,重定向也是可以传递数据的,只是和转发传递的方式有所区别。那这两者的相同点呢,都是解决两个web组件之间的跳转问题,其中,web组件,就是web项目当中解决请求的组件,就是呢,我们的servlet,或者是jsp。那我们开发时,有的时候可能会从一个Servlet跳到另外一个Servlet,可能会从一个jsp跳到另外一个jsp,可能会从一个Servlet跳到另外一个jsp,互相随便跳,只要是两个web组件,可能就会产生这个跳转的问题,不过呢,一般常见的情况是什么呢,一般是从一个Servlet重定向到另外的一个Servlet,或者一般呢,是从一个Servlet转发到另外的一个jsp,一般是这样。
  • 因为在MVC模式下,我们是用Servlet处理请求,处理java的逻辑是吧,我们是用jsp呢,负责展现,两者相互配合,它就转发了,所以 Servlet到jsp呢,通常是转发的,然后呢,因为MVC模式呢,每一个Servlet处理,独立的一个请求,那如果是两个Servlet,是两个独立请求,它们之间跳转的话,因为是独立性,所以呢,重定向,一般是这样,当然,也这个不排除有特殊情况,那你把这个一般的情况掌握了,就能解决大部分的问题,然后呢,我们又讲了它们的区别,那首先呢,对转发和重定向,讲了两个小故事,想告诉你呢,这样的一个特征吧,或者这样的一个经验吧。那么如果两个对象,之间是依赖的,就用转发,如果两个对象之间呢,不依赖,就用重定向,是这样的。
  • 那具体来讲呢,它们的这个区别,各有4点,转发的话呢,它是只有一个请求,转发以后呢,地址不变,地址不变的原因呢,是因为浏览器,它并不知道转发了(到B),它不知道这个行为,所以它还以为,结果是A返回的,地址不变;那转发的话,因为是一个请求,那么在我们讲这个Servlet原理,或者是jsp原理时,画图也看到了,那么在每一次请求的时候,服务器都会去new一个request和一个response,那一个请求呢,只有一个request,服务器端的A和B两个web组件(转发的特点:1.一次请求;2.地址不变;3.一次请求服务器只创建一个request,2个组件可以通过request共享数据;4.只能转发到项目内部的组件。),共用一个request,可以通过request呢,共享数据。然后呢,转发的话,这个服务器内部的组件,它只能访问内部的东西,它访问不了其他的服务器,你不能随便呢,把这个请求呢,转发到这个淘宝啊,百度啊,不行,只能局限在我们项目之内。
  • 那第2个呢,是重定向,它的特点是,有两个请求,因为呢,第二个请求是浏览器自己发出的,浏览器完全呢,知道这件事的存在,所以呢,地址会发生变化(重定向的特点:1.二次请求;2.地址改变;3.二次请求服务器会创建二个request,二个组件无法通过它共享数据;4.可以重定向到项目的外部的组件。),那么两次请求呢,这个服务器啊,会创建两个request,服务器中的两个组件A和B,就没有办法通过request,来共享数据了,那通过什么共享数据呢,session,后面会说。然后,还有一个,那么重定向呢,可以这个定向到web的组件,因为第2次请求呢,是浏览器发出的,浏览器访问谁都是可以的,那么,这两项技术的区别,这个一定要记下来,因为面试的时候呢,这是比较常问的一道题。
  • 那么使用建议,就通常呢,Servlet要把数据给jsp时用转发,然后呢,增加,修改,删除时呢,之后用定向,这件事,我们会在项目中演示,就会看到。

参考文献(References)

[1.().

2..

3..

文中如有侵权行为,请联系me。。。。。。。。。。。。。

  • 0
    点赞
  • 0
    评论
  • 1
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

参与评论 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:数字20 设计师:CSDN官方博客 返回首页

打赏作者

Water Fan

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值