【JSP - Java Server Page】
【jsp运行原理】
jsp在第一次被访问时会被Web容器(Tomcat)翻译成servlet,再执行
过程:
第一次访问---->helloServlet.jsp---->helloServlet_jsp.java---->编译运行
* 被翻译后的servlet在Tomcat的work目录中可以找到
helloServlet_jsp.java这个类继承于org.apache.jasper.runtime.HttpBase这是个抽象类
通过查看tomcat服务器的源代码可以发现:
public abstract class HttpJspBase extends HttpServlet implements HttpJspPage
所以说jsp就是servlet
【jsp的最佳实践】
不管是Servlet还是jsp,虽然都可以用于开发动态web资源。
但由于这两门技术各自的特点,在长期的软件实践中,
逐渐把Servlet作为web应用中的控制器组件来使用,
而把jsp技术作为数据显示模板来使用
【jsp乱码问题】
有时候会出现乱码问题,可以通过把jsp页面的编码和解码格式设置成utf-8解决
首先分析一下jsp到servlet经历的编码和解码步骤,再理解jsp页面的前几行的编码和解码格式究竟影响哪一步:
图片来源:https://blog.csdn.net/wangxingliang_/article/details/60870989
- 客户端通过浏览器向服务器发出请求,在该请求中包含了请求的资源的路径,这样当服务器接收到该请求后就可以知道被请求的内容。
- 服务器根据接收到的客户端的请求来加载相应的JSP文件。
- Web服务器中的JSP引擎会将被加载的JSP文件转化为Servlet。
- JSP引擎将生成的Servlet代码编译成Class文件。
- 服务器执行这个Class文件。
- 最后服务器将执行结果发送给浏览器进行显示。
在jsp页面的开头设置了3个编码格式:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
pageEncoding的设置影响第3和4步
contentType的设置影响第5步
meta标签的编码格式设置:meta标签相当于一个报文头,用于指定浏览器html页面的编码和解码
实在不理解的话统一编码格式utf-8就完事了
jsp有动态反射机制,页面保存完毕之后不需要重启服务器即可访问修改过的页面(有时间深入理解一下)
【jsp语法】
jsp脚本:
1)JSP脚本片段:
<%java代码%> ----- 内部的java代码翻译到service方法的内部
<%@ page import="java.util.Date" %>
当前时间为:
<%
Date date = new Date();
String time = date.toLocaleString();
%>
2)JSP脚本表达式:
<%=java变量或表达式> ----- 会被翻译成service方法内部out.print()
查看翻译之后的.java文件(文件位置在tomcat服务器\work\Catalina\localhost\jsp_01\org\apache\jsp里面),上图的jsp片段如下图所示
import java.util.Date;
out.write("\t当前时间为:\r\n");
out.write("\t");
Date date = new Date();
String time = date.toLocaleString();
out.write("\r\n");
out.write("\r\n");
out.write("\t<font color=\"red\"> ");
out.print(time);
out.write(" </font>\r\n");
- * jsp脚本片段用于在jsp中编写多行java代码
- jsp脚本片段中只能出现java代码,不能出现其他模板元素(HTML,CSS,JS)。
- jsp引擎在翻译jsp页面时,会将jsp脚本片段中的java代码原封不动的放入Servlet的_jspService方法中
* 在一个jsp页面中可以有多个脚本片段,在两个片段间可以嵌入文本,HTML标记和其他jsp元素
** 单个脚本片段中的java语句可以是不完整的,但是,多个脚本片段组合后的结果必须是完整的java语句
<% for(int i = 0; i < 10; i ++){ %>
<h1>哈哈</h1>
<% } %>
* 多个脚本片段中的代码可以相互访问,就犹如放在一个jsp脚本片段中一样
<%! String name="123"; %>
<% name="abc"; %>
** 单个脚本片段中的java语句可以是不完整的,但是,多个脚本片段组合后的结果必须是完整的java语句
2段脚本代码构成了循环输出哈哈
<% for(int i = 0; i < 10; i ++){ %>
<h1>哈哈</h1>
<% } %>
3)JSP声明:
<%!java代码%> ---- 会被翻译成servlet的成员的内容
* JSP页面中编写的所有代码,默认会翻译到Servlet的service方法中,
而jsp声明中的java代码,会被翻译到service方法的外面
作用:可用于定义jsp页面转换成Servlet的静态代码块,成员变量和方法
<%!-- web引擎(比如说:tomcat)会自动把java脚本里的方法和成员属性拿到service方法外面 --%>
<%!
public void run(){
}
public String name;
%>
jsp注释: 不同的注释可见范围是不同
1)Html注释:<!--注释内容--> ---可见范围 jsp源码、翻译后的servlet、页面显示html源码
2)java注释://单行注释 /*多行注释*/ --可见范围 jsp源码 翻译后的servlet
3)jsp注释:<%--注释内容--%> ----- 可见范围 jsp源码可见(用这种,性能最高)
【jsp指令(3个)】
<%@ 指令 属性名="值" %>
jsp的指令是指导jsp翻译和运行的命令,jsp包括三大指令:
1)page指令 --- 属性最多的指令(实际开发中page指令默认)
属性最多的一个指令,根据不同的属性,指导整个页面特性
格式:<%@ page 属性名1= "属性值1" 属性名2= "属性值2" ...%>
常用属性如下:
language:jsp脚本中可以嵌入的语言种类
pageEncoding:当前jsp文件的本身编码---内部可以包含contentType
contentType:response.setContentType(text/html;charset=UTF-8)
session:是否jsp在翻译时自动创建session,默认不设置就自动创建,设置为false不创建,有不需要session的地方可以节省空间
import:导入java的包
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="java.util.Date" %>
errorPage:当前页面出错后转发到哪个页面
- * 属性必须使用相对路径,
- 如果以 / 开头,则表示相对于当前web应用程序的根目录
- 否则表示相对于当前页面
isErrorPage:当前页面是一个处理错误的页面
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" session="false"%>
<%-- 出现异常跳转到/errors/error_page.jsp, --%>
<%@ page errorPage="/errors/error_page.jsp""%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
//会出现java.lang.ArithmeticException异常
<%
int x = 1/0;
%>
</body>
</html>
也可以在web.xml文件中指定出现异常后转发到哪个页面
例子:web.xml页面中配置2个错误页面,项目下的某页面出现java.lang.ArithmeticException转发到这个页面,出现404(找不到资源)转发到另一个页面
web.xml文件配置
<error-page>
<exception-type>java.lang.ArithmeticException</exception-type>
<location>/errors/error_page.jsp</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/errors/404.jsp</location>
</error-page>
出现java.lang.ArithmeticException异常的页面,如果xml文件中配置了异常转发,那么就不需要在jsp添加errorPage指令了
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" session="false"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
//会出现java.lang.ArithmeticException异常
<%
int x = 1/0;
%>
</body>
</html>
error_page.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" session="false"%>
<%-- isErrorPage设置了true,表示别的页面出错跳转的话会把异常exception也带到这个页面,方便调试 -- %>
<%@ page isErrorPage="true" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>你出错了!!!</h1>
<%
exception.printStackTrace();
%>
</body>
</html>
2)include指令
页面包含(静态包含)指令,可以将一个jsp页面包含到另一个jsp页面中
格式:<%@ include file="被包含的文件地址"%>
例:把head..jsp和foot.jsp包含到一个新的jsp页面
head..jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
foot
<%
int x = 5;
%>
foot.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
foot
<%
int x = 5;
%>
执行静态包含的jsp(把head和foot两个jsp包含进来)
<!-- 静态包含 -> 编译时包含 -->
<!-- 包含的jsp文件除了编码格式和正文,其余的都不要写,否则包含以后容易出现覆盖情况 -->
<%@ include file="/public/head.jsp" %>
内容
<%@ include file="/public/foot.jsp" %>
动态包含(运行时,一条一条语句执行,执行到当前行才会去加载jsp页面):
<!-- 动态包含 -> 运行时包含 -->
<%
request.getRequestDispatcher("/public/head.jsp").include(request, response);
%>
内容
<%
request.getRequestDispatcher("/public/foot.jsp").include(request, response);
%>
3)taglib指令
在jsp页面中引入标签库(jstl标签库、struts2标签库)
格式:<%@ taglib uri="标签库地址" prefix="前缀"%>
以后再讲
【内置对象(9个)】
jsp被翻译成servlet之后,service方法中有9个对象定义并初始化完毕,
我们在jsp脚本中可以直接使用这9个对象
名称 | 类型 | 描述 |
out | javax.servlet.jsp.JspWriter | 用于页面输出 |
request | javax.servlet.http.HttpServletRequest | 得到用户请求信息 |
response | javax.servlet.http.HttpServletResponse | 服务器向客户端的回应信息 |
config | javax.servlet.ServletConfig | 服务器配置,可以取得初始化参数 |
session | javax.servlet.http.HttpSession | 用来保存用户的信息 |
application | javax.servlet.ServletContext | 所有用户的共享信息 |
page | java.lang.Object | 指当前页面转换后的Servlet类的实例 |
pageContext | javax.servlet.jsp.PageContext | JSP的页面容器 |
exception | java.lang.Throwable | 表示JSP页面所发生的异常,在错误页中才起作用 |
面试题:jsp有几个内置对象?
1.背出上表
2.8个在jsp引擎把jsp页面转换成的servlet.java文件中
还有一个在设置过page指令属性isErrorPage=true的servlet.java文件中
out对象
out的类型:JspWriter
out作用就是想客户端输出内容----out.write()
out缓冲区默认8kb 可以设置成0 代表关闭out缓冲区 内容直接写到respons缓冲器
// jsp文件中html页面会翻译成out.write(html代码)形式输出
cccccccccccccccccccc
<%
// 只用out
out.write("aaaaaaaaaaaaaaa");
//response.getWriter().write()速度比out.write()快
response.getWriter().write("bbbbbbbbbbbbbb");
%>
jsp页面统一用out.write
pageContext对象
jsp页面的上下文对象,jsp页面的运行环境,作用如下:
page对象与pageContext对象不是一回事
1)pageContext是一个域对象
setAttribute(String name,Object obj)
getAttribute(String name)
removeAttrbute(String name)
pageContext可以向指定的其他域中存取数据
setAttribute(String name,Object obj,int scope)
getAttribute(String name,int scope)
removeAttrbute(String name,int scope)
findAttribute(String name):用于最佳实践
---依次从pageContext域,request域,session域,application域中获取属性,在某个域中获取后将不在向后寻找
int scope参数的值是四个常量值,分别代表pageContext域,request域,session域,application域
四大作用域的总结:
pageContext域:当前jsp页面范围
request域:一次请求
session域:一次会话
application域:整个web应用
2)可以获得其他8大隐式对象
例如: pageContext.getRequest()
pageContext.getSession()
<%
// 使用pageContext对象存放数据
//pageContext.setAttribute("name", "page-zhangsan");
//pageContext.setAttribute("name", "request-zhangsan", pageContext.REQUEST_SCOPE);
//pageContext.setAttribute("name", "session-zhangsan", PageContext.SESSION_SCOPE);
pageContext.setAttribute("name", "app-zhangsan", PageContext.APPLICATION_SCOPE);
// page中取值
//out.write(pageContext.getAttribute("name").toString());
// session中取值
//out.write(session.getAttribute("name").toString());
//out.write(pageContext.getAttribute("name", PageContext.SESSION_SCOPE).toString());
// findAttr
// 分别从4个域中查找数据
// page -> request -> session -> application
out.write(pageContext.findAttribute("name").toString());
// 获取其他隐式对象
// jsp书写规范:jsp页面中不允许出现一行java代码
pageContext.getOut();
pageContext.getRequest();
%>
【jsp标签(动作)】
1)页面包含(动态包含):<jsp:include page="被包含的页面"/>
<!-- jsp动作标签:动态包含 -->
<jsp:include page="/public/head.jsp"></jsp:include>
内容
<jsp:include page="/public/foot.jsp"></jsp:include>
include指令与<jsp:include>动作的区别:
include指令通过file属性来指定被包含的页面。
<jsp:include>动作通过page属性来指定被包含的页面。
使用include指令,被包含的文件被原封不动的插入到包含页面中使用该指令的位置,然后JSP编译器再对这个合成的文件进行编译,所以在一个JSP页面中使用include指令来包含另一个JSP页面,最终编译后的文件只有一个。(静态包含)
使用<jsp:include>动作包含文件时,当该动作标识执行后,JSP程序会将请求转发到(注意不是重定向)被包含页面,并将执行结果输出到浏览器中,然后返回页面继续执 行后面的代码,以为web容器执行的两个文件,所以JSP编译器会分别对这两个文件进行编译。(动态包含)
注意:(使用<jsp:include>动作通常是包含那些经常改动的文件,因为被包含的文件改动不会影响到包含文件,因此不需要对包含文件进行重新编译)
2)请求转发:<jsp:forward page="要转发的资源" />
11_1.jsp
<h1>11_1</h1>
<jsp:forward page="/11_2.jsp">
<jsp:param value="zhangsan" name="name"/>
</jsp:forward>
11_2.jsp
<h1>11_2</h1>
<%= request.getParameter("name") %>