🎉参考视频教程https://www.bilibili.com/video/BV1kb411h7Aa
🎉个人网站http://qkmango.cn
文章目录
JSP初步
JSP是什么?
-
JavaServer Pages
-
基于Java语言实现的服务器端页面
-
JSP是javaEE规范之一
JSP文件通常放到什么位置?
-
JSP可以放到WEB-INF目录外,目前我们是这样做的
-
在实际开发中,有很多项目是将JSP放到WEB-INF目录中,保护JSP
-
WEB-INF目录中的数据时相对安全的
JSP文件后缀是什么?
- 默认是
.jsp
- 但是JSP文件的后缀也可以修改的,通过修改CATALINA_HOME/conf/web.xml文件
JS和JSP的区别
- JS:JavaScript,运行在浏览器中,和服务器没有任何关系,和Java也没有关系
- JSP:JavaServer Pages,运行在服务器中,JSP底层就是Java程序,运行在JVM中
JS和JSP就相当于雷锋和雷峰塔的关系,JS和JSP没有任何关系
JSP的执行原理
- 浏览器上访问的路径,虽然是以
.jsp
结尾,访问的是某个jsp文件,其实底层执行的是jsp对应的Java程序 - Tomcat服务器负责将.jsp文件翻译生成.java源文件,并且将Java源文件编译生成.class字节码文件
- 访问jsp,其实底层还是执行力.class文件
- Tomcat服务器内置了一个jsp翻译引擎,专门负责翻译jsp文件,编译Java源文件
- index.jsp会被翻译生成index_jsp.java,编译生成index_jsp.class
- index_jsp这个类继承了HttpJspBase,而HttpJspBase继承了HttpServlet
- 所以jsp就是Servlet,只不过jsp的强项是做动态页面展示
- 在JSP文件中编写的所有的HTML、CSS、JavaScript,对于JSP来说,只是不同的字符串,会被翻译到Servlet的service()方法的out.write(“翻译到这里”);方法中
JSP文件第一次访问时为什么非常慢?
- 启动JSP翻译引擎
- 需要一个翻译过程
- 需要一个编译过程
- 需要Servlet对象的创建过程
- init()方法调用
- service()方法调用…
为什么第二次以上方位JSP为什么快?
-
因为不需要重新翻译
-
不需要重新编译
-
不需要创建Servlet对象
-
直接调用Servlet对象的service()方法
JSP什么时候会被重新翻译为Java源文件
当JSP文件被修改后,会被重新翻译和编译
什么确定jsp文件被修改了呢?服务器会将记录的文件最后修改时间和文件的修改时间对比,最后修改时间不同说明被修改,然后重新翻译和编译JSP文件
JSP注释
<%----%>
这是jsp的专业注释,使用这种注释方式,不会被翻译到Java源文件中
<%-- JSP注释 --%>
JSP小脚本scriptlet<%%>
(webapp02)
JSP小脚本中的代码会被翻译到Servlet的service()方法中
<%
Java语句;
Java语句;
Java语句;
Java语句;
Java语句;
%>
- 小脚本中的Java语句会被翻译到Servlet的service()方法中,所以小脚本中必须编写Java语句
- 所谓的JSP规范,就是SUN制定好的一些翻译规则,按照翻译规则进行翻译,生成对应的Java源程序。不同的web服务器翻译的结果完全是相同的,因为都遵守了JSP翻译规范
- 小脚本的数量随意,可以多个
- 小脚本中编写Java程序出现在service()方法中,service()方法的代码是有执行顺序的,所以小脚本中的程序也是有顺序的
- 例如以下JSP会被翻译为:
JSP文件
<%
int i = 10;
%>
<html>
<%
System.out.println(i);
%>
<head>
<%
double d = 3.0;
%>
<title>JSP</title>
<%
System.out.println(d + i);
%>
</head>
<body>
hello jsp!
<%
System.out.println(d * i);
System.out.println(d / i);
%>
</body>
</html>
<%
String name = "king";
System.out.println("name = " + name);
%>
翻译为的Java源文件Servlet的service()方法中
public void _jspService(final HttpServletRequest request,
final HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException {
int i = 10;
out.write("<html>");
System.out.println(i);
out.write("<head>");
double d = 3.0;
out.write("<title>JSP</title>");
System.out.println(d + i);
out.write("</head>");
out.write("<body>");
out.write("hello jsp!");
System.out.println(d * i);
System.out.println(d / i);
out.write("</body>");
out.write("</html>");
String name = "king";
System.out.println("name = " + name);
}
控制台结果
10
13.0
30.0
0.3
name = king
网页显示
hello jsp!
- 以下代码编译无法通过,因为service()方法中不能编写实例变量、方法、静态代码块
<%
public String username;
%>
<%
public void method(){
}
%>
<%
static {
}
%>
JSP声明语法格式<%!%>
(webapp03)
声明语法块会被翻译到Servlet的类体中,即service()方法外面
<%!
实例变量;
静态变量;
方法;
静态代码块;
构造函数;
......
%>
- 因为声明语法块会被翻译到Servlet的类体中,即service()方法外面,所以声明块中不能直接编写Java语句,除非是变量的声明;
- JSP声明语法块中的代码会按照先后顺序被翻译到Servlet类体中;
如下代码翻译情况:
jsp源文件
<%!
int i = 10;
%>
<%
System.out.println("i = " + i);
method();
%>
<%!
public void method() {
System.out.println("method...");
}
%>
<%!
static {
System.out.println("Class Loader");
}
%>
翻译为的Java源文件
值得注意的是,虽然JSP源文件中调用method()方法看似在声明method()方法前,但是他们使用的一个是小脚本块,一个使用的是声明块,最终翻译成为的Java文件,声明块里面的method()方法的定义还是在前的,在类体里面
public class index_jsp extends HttpJspBase {
int i = 10;
public void method() {
System.out.println("method...");
}
static {
System.out.println("Class Loader");
}
public void _jspService(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
System.out.println("i = " + i);
method();
}
JSP内置对象(webapp04)
JSP中有9大内置对象,都在service()方法中定义
pageContext对象的描述https://www.cnblogs.com/nickup/p/9004594.html
-
什么是内置对象?
可以直接在JSP文件中拿来使用的引用
-
九大内置对象有哪些?
内置对象名称 完整类名 pageContext javax.servlet.jsp.PageContext 页面范围 request javax.servlet.htpp.HttpServletRequest 请求范围 session javax.servlet.http.HttpSessio 会话范围 application javax.servlet.ServletContext 应用范围 config javax.servlet.ServletConfig Servlet配置信息 out javax.servlet.jsp.JspWriter 标准输出流 page java.lang.Object [page=this] (很少用) exception javax.servlet.jsp.JspException 异常引用,需要学习JSP的page指令的isErrorPage属性
-
以上内置对象只能在service()方法中直接使用,在其他方法中无法直接使用,可以间接使用
-
💡主要研究JSP中的四个作用域对象/范围对象
pageContext < request < session < application
- pageContext:在同一个页面中共享数据,不能跨JSP页面
- request:在同一个请求中共享数据(使用请求转发)【使用较多】
- session:在同一个对话中共享数据【使用较多】
- application:在同一个应用中共享数据,只要服务器不关
🚩注意:JSP九大内置对象之page对象的误区
要注意的是,page对象虽然是this的引用,但是page的类型是java.lang.Object,所以是无法通过page调用实例变量、方法等等,只能调用Object类型的一些方法
众所周知JSP内置的page对象是this的引用,在经过访问翻译为Java源代码后可以看到
final java.lang.Object page = this;
,page正是这个Servlet对象的引用,既然是这样,那么就有了一下我写的JSP代码:
<%!
int i = 10;
%>
<%
System.out.println(page.i);
System.out.println(this.i);
%>
首先我使用声明块声明了一个实例变量int i = 10
,然后使用小脚本块通过page
对象调用变量i
,发现编辑器报错,我又试着直接使用this
来调用,发现并没有报错;
这是候我已经很疑惑了,page = this
,为什么this
可以调用到i变量,而page
调用不了呢?
于是将JSP文件进行了翻译为Java源文件,简化代码如下:
public final class index_jsp {
int i = 10;
public void service(HttpServletRequest request, HttpServletResponse response)
throws Exception{
final java.lang.Object page = this;
System.out.println(page.i);
System.out.println(this.i);
}
}
看到翻译后的Java源文件,绝对也并没有错误啊,先是声明了一个实例变量int i = 10;
然后再service()
方法中调用,page = this
,为什么还是this
可以调用实例变量i
,而page
无法调用实例变量i呢?
我又仔细看了一下代码,果然发现了问题所在,虽然page = this
,但是page
的类型是Object
,即page
只能调用Object
类型的一些方法!
JSP表达式<%=%>
(webapp05)
<%= %>
等同于out.print();
- 表达式语法具有输出功能,输出到浏览器
<%!
int i = 10;
%>
<!--使用out输出和使用表达式语法输出相同的内容-->
<%
out.print("i = " + i);
%>
<%="i = " + i%>
-
JSP的Java代码块是可以进行拼接的
🚩JSP中的Java代码不仅翻译为Java源文件后有先后的顺序,而且可以按照顺序进行拼接的;
如下的for循环为了动态输出h1~h6标题,将for循环拆分为了3部分,第二部分使用表达式进行动态输出
<% for (int i = 1; i <= 6; i++) { %> <h <%=i%> >标题字</h <%=i%> > <% } %>
翻译后的Java文件
for (int i = 1; i <= 6; i++) { out.write("<h"); out.print(i); out.write(">标题字</h"); out.print(i); out.write(">"); }
JSP指令<%@%>
(webapp06)
了解指令
-
指令的作用,是指导JSP翻译引擎如何翻译JSP代码
-
JSP中共用三个指令
- page 页面指令
- include 包含指令
- taglib 标签库指令
-
指令的语法格式
<%@指令名 属性名=属性值 属性名=属性值...... %>
page指令的常用属性
contentType
pageEncoding
import
session
errorPage
isErrorPage
isELIgnored
-
contentType
设置JSP的响应内容类型,同时在响应的内容类型后面也可以指定响应的字符编码方式 -
pageEncoding
设置JSP的响应字符编码方式<%@ page contentType="text/html" pageEncoding="UTF-8" %> //等同于 <%@ page contentType="text/html;charset=UTF-8" %> //翻译成为Java文件中的代码 response.setContentType("text/html;charset=UTF-8");
-
import
组织导入(导包)<%@ page import="java.util.Date" %> 翻译为Java源文件为导包语句 import java.util.Date;
-
session
设置当前JSP页面中是否可以直接使用session内置对象;不写则默认session=“true”//1. session="true"表示可以直接使用九大内置对象session对象;不存在则创建一个新的session对象 JSP: <%@ page session="true" %> Java: javax.servlet.http.HttpSession session = null; session = pageContext.getSession(); //2. session="false"表示不能直接使用session对象,并不会实实例化并获取session对象 JSP: <%@ page session="false" %> //3. 当我们想要使用session对象,并且如果没有session对象则不创建,那么可以指定session="false";然后通过Java代码的方式获取session对象 JSP: <%@ page session="false" %> <% HttpSession session = request.getSession(false); %> Java: HttpSession session = request.getSession(false);
-
errorPage
错误页面当前JSP页面出错之后要跳转的页面路径,需要使用该属性指定
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%--指定当此JSP页面出错后跳转到errorPage.jsp页面--%> <%@ page errorPage="errorPage.jsp" %> <html> <head> <title>JSP</title> </head> <body> <% <%-- 制造空指针异常,此JSP出错,会自动转发到errorPage.jsp页面--%> String s = null; s.toString(); %> </body> </html>
-
isErrorPage
是否是错误页面,不写默认为isErrorPage=“false”isErrorPage="false"表示内置对象exception无法使用
isErrorPage=“true” 表示内置对象exception可以使用
设置当前页面是否为错误页面,当后天出错时,为了前端用户的使用感受而设置了errorPage属性,出错时跳转到错误页面,但是后台的错误信息也不会被打印出来了,所以通过isErrorPage属性设置当前页面是否为错误页面,当isErrorPage="true"时,表示当前 页面是一个错误页面,进而可以直接使用exception内置对象打印错误信息
<%@ page contentType="text/html;charset=UTF-8" %> <%--设置当前页面为错误页面--%> <%@ page isErrorPage="true" %> <html> <head> <title>error page</title> </head> <body> error page </body> </html> <% <%--打印错误信息--%> exception.printStackTrace(); %>
-
isELIgnored
是否忽略EL表达式
includ指令
仅有一个属性
file
<%@include file="msg.jsp"%>
-
a.jsp可以将b.jsp包含进来,当然资源不一定是jsp,可能是其他的资源
-
include的作用:
在网页中有一些主题框架,例如网页头、网页脚,这些都是固定不变的,我们可以将网页头、网页脚等固定不变的单独写到某个JSP文件中,在需要的时候使用include指令包含进来;
优点:
代码量少了,便于维护(修改一个文件既可以作用于所有页面)
-
在一个JSP中可以使用多个include指令
-
include实现原理
- 编译期(翻译)即将文件包含进去,多个JSP文件一起翻译为一个Java源文件
- a.jsp包含b.jsp,底层共生成一个Java文件,一个class字节码文件,翻译期包含/编译期包含/静态联编
-
静态联编的时候,多个JSP可以共享一个局部变量和实例变量,因为最终翻译之后,局部变量都被翻译到service()方法中,实例变量都被翻译到类体中
JSP动作(webapp07)
JSP中有七大动作,下面着重学习forward转发动作和include包含动作,了解bean动作
forward转发动作
<jsp:forward page="index2.jsp"/>
等同于Java代码
request.getRequestDispatcher("index2.jsp").forward(request,response);
<%--index.jsp--%>
<%
// 向request范围中存数据
request.setAttribute("username","admin");
%>
<%--forward转发动作,将请求转发到index2.jsp--%>
<jsp:forward page="/index2.jsp"/>
<%--index2.jsp--%>
<%--从request范围中取数据--%>
<%=request.getAttribute("username")%>
include包含动作
include包含是动态包含,动态联编
使用include包含动作可以将其他jsp等资源包含展示在一个jsp
了解include动作
- a.jsp包含b.jsp,底层分别生成两个Java源文件,两个class字节码文件
- 编译阶段并没有包含,编译阶段是两个独立的class字节码文件,生成两个Servlet
- 使用include动作属于运行阶段包含,实际上是在运行阶段a中的service()方法调用了b中的service()方法,达到了包含效果
- a.jsp包含b.jsp,若两个jsp文件中有重名变量,只能使用动态包含,因为两个Java源文件是相互独立的,其余都可以使用静态包含
- include动作完成动态包含,被成为动态联编
包含原理
如下代码,index.jsp文件中使用include动作包含了index2.jsp文件
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<body>
<jsp:include page="index2.jsp"/>
</body>
</html>
index.jsp和index2.jsp两个文件翻译为Java源文件是各自独立的index.java和index2.java;
在index.java的service()方法中有这么一行代码,说明是index的service()方法在包含的位置调用了index2,执行了index2的service()方法;
所以两个jsp翻译后的Java文件是相互独立的,所以变量是不能通用的;
public void service(HttpServletRequest request, HttpServletResponse response) {
//.......
org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "index2.jsp", out, false);
//......
💡include指令与include动作区别
include指令
<%@include file="index2.jsp"%>
include动作<jsp:include page="index2.jsp"/>
- 无论是include指令还是include动作,都可以将其他页面包含进来
- include指令是静态联编,如果a.jsp包含b.jsp,那么在翻译期就会将b翻译进来,最后翻译形成一个Java源文件,所以a.jsp和b.jsp中的变量等是可以共用的
- include动作是动态联编,如果a.jsp包含b.jsp,那么在翻译期,会生成两个相互独立的Java源文件, 只是在执行a的service()方法的时候,在包含b的位置调用了b而已,所以两个jsp中的变量是不通用的
所以如果两个JSP中有同名的变量,需要使用include动作(动态联编);如果需要共享变量,可以使用include指令(静态联编);多数情况下都是使用include指令(静态联编)
bean
通过动作的方式,创建对象,以及往对象中设置值和取值
共有三个动作:useBean、setProperty、getProperty
<jsp:useBean id="对象名" class="包名.类名"/> <%--创建对象--%>
<jsp:setProperty name="对象名" property="属性名" value="值"/> <%--调用set方法--%>
<jsp:getProperty name="对象名" property="属性名" /> <%--调用get方法--%>