JSP 即 Java Server Pages,是一种简化的 Servlet 设计,实现了在 Java 中使用 HTML 标签。JSP 作为一种动态网页技术标准 也是 Java EE 的标准,与Servlet 一样,实在服务器端执行的。
常见动态网站开发技术对比
- JSP:Java 平台,安全性高,适合开发大型、企业级的 Web 应用程序;
- ASP.net:.Net 平台,简单易学,但安全性和跨平台性差;
- PHP:简单高效,成本低、开发周期短,适合中小型企业的 Web 应用开发(LAMP:Linux + Apache + MySQL + PHP)。
页面元素
指令
- page:位于 JSP 页面顶端,一个页面可以有多个;
- include:将外部文件嵌入当前 JSP 中,同时解析这个页面中的 JSP 语句;
- taglib:使用标签库定义新的自定义标签,在 JSP 页面中启用定制行为。
page 指令
<%@ page
contentType="text/html;charset=UTF-8"
language="java"
import="java.util.*"
%>
其中 pageEncoding
是 JSP 文件本身的编码,contentType
的 charset 是指服务端发给客户端时的内容编码,contentType
更常用。
注释
JSP 中的注释有三种
- HTML 注释(客户端/浏览器可见)
- JSP 注释(客户端不可见)
- JSP 脚本注释
<%-- JSP 注释 --%>
<%
// 单行注释
/*
多行注释
*/
%>
脚本
在 JSP 页面中执行 Java 代码。
实例:使用脚本输出九九乘法表
<%!
void printMultiTable(JspWriter out) throws Exception {
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= i; j++)
out.println(i + "*" + "=" + i * j + " ");
out.println("
");
}
}
%>
<% printMultiTable(out); %>
声明
在 JSP 页面中定义变量或方法
<%!
String name = "ywh";
Integer age = 16;
String getDesc(String name, Integer age) {
return "name: " + name + ", age: " + age.toString();
}
%>
表达式
在 JSP 页面中执行表达式,注意不能有分号。
实例:使用表达式打印九九乘法表
<%!
String printMultiTable() {
String s = "";
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= i; j++)
s += i + "*" + "=" + i * j + " ";
s += "
";
}
return s;
}
%>
<%=printMultiTable()%>
静态内容
...
生命周期
JSP 实际上是一种 Servlet 对象;通过 tomcat/ work/ Catalina/ localhost 目录可以看到生成的 java 源代码(包含 jsp 初始化、解析执行的方法),其生命周期:
- 用户发出请求(如访问 index.jsp)
- 服务端判断用户是否首次发起请求:
2.1 是,JSP 引擎把该 JSP 文件转换成(先执行构造方法)一个 Servlet 对象,生成字节码文件,并执行jspInit()
,访问字节码文件;
2.2 否,直接访问字节码文件; - 解析执行字节码文件,调用
jspService()
方法。
jspService()
:用于处理客户端请求,对于每个请求都创建一个新的线程来处理,如果多个客户端同时请求,则 JSP 引擎会创建多个线程(每个客户端对应一个);使用多线程可以大大降低对系统的资源需求,提高系统的并发量和减少响应时间。但也可能存在线程同步问题。由于该 Servlet 常驻于内存,响应速度很快。
<%@ page contentType="text/html;charset=UTF-8" language="java" import="java.util.*"%>
<%@ page import="java.text.*" %>
JSP
<%
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String s = sdf.format(new Date());
%>
today: <%=s%>
内置对象
JSP 内置对象是 Web 容器创建的一组对象,不使用 new 关键字就可以使用的内置对象。
- out
- request
- response
- session
- application
- Page, PageContext, exception, config
out 对象
out 对象是缓冲区(内存的一块保存临时数据的区域)相关的 JspWriter
类的实例,向客户端输出内容的对象。
<%
out.println("静夜思
");
out.println("床前明月光
");
out.println("疑是地上霜
");
out.flush();
//out.clear(); //这里会抛出异常。
out.clearBuffer(); //这里不会抛出异常。
out.println("举头望明月
");
out.println("低头思故乡
");
%>
缓冲区大小:<%=out.getBufferSize() %>byte
缓冲区剩余大小:<%=out.getRemaining() %>byte
是否自动清空缓冲区:<%=out.isAutoFlush() %>
request 对象
客户端请求信息被封装在 request 对象中,是 HttpServletRequest
类的实例。request 对象具有请求域,即完成客户端的请求之前该对象会一直有效。
request内置对象
<%
request.setCharacterEncoding("utf-8"); // 无法解决URL传递中文出现的乱码问题,需要修改 Tomcat 配置文件
request.setAttribute("password", "123456");
%>
用户名:<%=request.getParameter("username") %>
爱好 :<%
if (request.getParameterValues("favorite") != null) {
String[] favorites = request.getParameterValues("favorite");
for (int i = 0; i < favorites.length; i++) {
out.println(favorites[i] + " ");
}
}
%>
密码:<%=request.getAttribute("password") %>
请求体的MIME类型:<%=request.getContentType() %>
协议类型及版本号: <%=request.getProtocol() %>
服务器主机名 :<%=request.getServerName() %>
服务器端口号:<%=request.getServerPort() %>
请求文件的长度 :<%=request.getContentLength() %>
请求客户端的IP地址:<%=request.getRemoteAddr() %>
请求的真实路径:<%=request.getRealPath("request.jsp") %>
请求的上下文路径:<%=request.getContextPath() %>
解决 URL 传递中文乱码问题,修改 Tomcat 配置文件:
response 对象
包含了响应客户请求的有关信息,是 HttpServletResponse
类的实例,但在 JSP 中很少使用。reponse
对象具有页面作用域(只对某次访问有效,其他页面的 response
对当前页面无效)。
<%@ page language="java" import="java.util.*,java.io.*" contentType="text/html; charset=utf-8" %>
<%
response.setContentType("text/html;charset=utf-8"); //设置响应的MIMI类型
out.println("response内置对象
");
out.println("
");
// out.flush();
PrintWriter outer = response.getWriter(); //获得输出流对象
outer.println("大家好,我是response对象生成的输出流outer对象");
// response.sendRedirect("reg.jsp"); //请求重定向
// 请求重定向
// response.sendRedirect("request.jsp");
// 请求转发
request.getRequestDispatcher("request.jsp").forward(request, response);
%>
- 请求重定向:客户端行为,
response.sendRedirect()
,本质上是两次请求,前一次请求对象不会保存,地址栏 URL 会改变; - 请求转发:服务端行为,
response.getRequestDispatcher().forward(req, resp);
是一次请求,转发后请求对象会保存,地址栏的 URL 不会改变。
session 对象
在服务端存储的、不同客户端与服务端一次会话的信息(时间概念),第一次 JSP 页面被装载时自动创建 session
对象,是 HttpSession
类的实例,完成会话期管理(从打开浏览器访问服务端到关闭浏览器)。
生命周期:在 Tomcat 后台管理 http://localhost:8080/manager
中可以查看 Session 存储情况。
- 创建:第一次访问某个 JSP 或者 Servlet 时候,服务器会为当前会话创建一个 SessionId,每次客户端向服务端发送请求时,都会将此 SessionId 携带过去,服务端会对此 SessionId 进行校验;
- 活动:某次会话中通过超链接打开新页面属于同一次会话,只要当前会话页面没有关闭,重新打开新的窗口访问同一项目资源也是同一次会话(除非所有页面关闭再重新创建);
- 销毁:调用
session.invalidate()
方法、Session 过期(超时)、服务器重启都会销毁对象。
关于 session
对象:
- Tomcat 默认
session
超时时间为 30min; - 设置超时时间:在代码中设置
session.setMaxInactiveInterval(xxx); // 秒
,或修改 Tomcat 配置文件 web.xml
10
page1
<%
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
Date d = new Date(session.getCreationTime());
session.setAttribute("username", "admin");
session.setAttribute("password", "123456");
session.setAttribute("age", 20);
//设置当前session最大生成期限单位是秒
//session.setMaxInactiveInterval(10);//10秒钟
%>
Session创建时间:<%=sdf.format(d)%>
Session的ID编号:<%=session.getId()%>
从Session中获取用户名:<%=session.getAttribute("username") %>
跳转到Session_page2.jsp
page2
<%
//SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
//Date d = new Date(session.getCreationTime());
//session.setAttribute("username", "admin");
%>
Session的ID编号:<%=session.getId()%>
从Session中获取用户名:<%=session.getAttribute("username") %>
Session中保存的属性有:
<%
String[] names = session.getValueNames();
for (int i = 0; i < names.length; i++) {
out.println(names[i] + " ");
}
%>
application 对象
- 是
ServletContext
类的实例,实现了用户间数据共享,可存放全局变量; - 创建于服务器启动,终止于服务器关闭(服务器启动关闭决定
application
对象); - 在用户前后连接、或不同用户之间的连接中,可以对
application
对象的同一属性进行操作; - 任何地方对
application
对象属性的操作,都影响其他用户对此的访问。
<%
application.setAttribute("city", "北京");
application.setAttribute("postcode", "10000");
application.setAttribute("email", "[email protected]");
%>
所在城市是:<%=application.getAttribute("city") %>
application中的属性有:<%
Enumeration attributes = application.getAttributeNames();
while (attributes.hasMoreElements()) {
out.println(attributes.nextElement() + " ");
}
%>
JSP(SERVLET)引擎名及版本号:<%=application.getServerInfo() %>
page 对象
指向当前 JSP 页面本身,类似类中的 this
指针,是 java.lang.Object
类的实例。
pageContext 对象
- 提供了对 JSP 页面内所有对象及命名空间的访问(汇集了页面中的所有功能);
- 可以访问到本页所在的 Session,可以取本页所在的
application
的某一属性值; - 本类名也为
pageContext
。
pageContext内置对象
用户名是:<%=pageContext.getSession().getAttribute("username") %>
<%
// 跳转到注册页面
// pageContext.forward("reg.jsp");
pageContext.include("include.jsp");
%>
config 对象
在 Servlet 初始化时,JSP 引擎向它传递信息使用到 config
对象,此信息包括 Servlet 初始化时用到的参数(通过属性名称和属性值构成)以及服务器的基本信息(通过传递一个 ServletContext
对象),常用方法:
-
getServletContext
:返回含有服务器相关信息的 ServletContext 对象; -
getInitParameter
:返回初始化参数的值; -
getInitParameterNames
:返回 Servlet 初始化所需所有参数值的枚举; - ...
exception 对象
页面运行异常时产生,当一个 JSP 页面要应用此对象,必须把 isErrorPage
设置为 true
,否则无法通过编译,实际上是 java.lang.Throwable
的对象。
test_page
<%@ page language="java" import="java.util.*" contentType="text/html; charset=utf-8" errorPage="exception.jsp" %>
<%
System.out.println(100 / 0); //抛出运行时异常,算数异常
%>
error_page
<%@ page language="java" import="java.util.*" contentType="text/html; charset=utf-8" isErrorPage="true" %>
...
异常的消息是:<%=exception.getMessage()%>
异常的字符串描述:<%=exception.toString()%>
JSP 实现用户登录
dologin.jsp:登录逻辑处理
<%@ page language="java" import="java.util.*" contentType="text/html; charset=utf-8" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
String username = "";
String password = "";
request.setCharacterEncoding("utf-8"); // 防止中文乱码
username = request.getParameter("username");
password = request.getParameter("password");
if ("admin".equals(username) && "admin".equals(password)) { // 如果用户和密码都等于 admin 则登录成功,服务器转发;失败则重定向到失败页面
session.setAttribute("loginUser", username);
request.getRequestDispatcher("login_success.jsp").forward(request, response);
} else {
response.sendRedirect("login_failure.jsp");
}
%>
login.jsp:登录请求页面
<%@ page language="java" import="java.util.*" contentType="text/html; charset=utf-8" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
%>
login_success.jsp:登录成功转发页面
login_failure.jsp:登录失败重定向页面
登录失败!请检查用户或者密码!
返回登录
JSP 状态管理
HTTP 协议的无状态性:浏览器发送请求给服务器,服务器响应客户端请求,但当同一浏览器再次发送请求给服务器时,服务器并不知道它就是之前那个浏览器。
保存用户状态的两大机制:
- Cookie
- Session
实例:JSP 中使用 Cookie 实现用户登录
dologin.jsp
<%@ page language="java" import="java.util.*,java.net.*" contentType="text/html; charset=utf-8" %>
<%
request.setCharacterEncoding("utf-8");
String[] isUseCookies = request.getParameterValues("isUseCookie");
if (isUseCookies != null && isUseCookies.length > 0) { // 首先判断用户是否选择了记住登录状态
String username = URLEncoder.encode(request.getParameter("username"), "utf-8"); // 把用户名和密码保存在Cookie对象里面
String password = URLEncoder.encode(request.getParameter("password"), "utf-8"); // 使用URLEncoder解决无法在Cookie当中保存中文字符串问题
Cookie usernameCookie = new Cookie("username", username);
Cookie passwordCookie = new Cookie("password", password);
usernameCookie.setMaxAge(864000);
passwordCookie.setMaxAge(864000); // 设置最大生存期限为10天
response.addCookie(usernameCookie);
response.addCookie(passwordCookie);
}
else {
Cookie[] cookies = request.getCookies();
if (cookies != null && cookies.length > 0) {
for (Cookie c : cookies) {
if (c.getName().equals("username") || c.getName().equals("password")) {
c.setMaxAge(0); // 设置Cookie失效
response.addCookie(c); // 重新保存。
}
}
}
}
%>
查看用户信息
login.jsp
<%@ page language="java" import="java.util.*,java.net.*" contentType="text/html; charset=utf-8" %>
<%
request.setCharacterEncoding("utf-8");
String username = "";
String password = "";
Cookie[] cookies = request.getCookies();
if (cookies != null && cookies.length > 0) {
for (Cookie c : cookies) {
if (c.getName().equals("username"))
username = URLDecoder.decode(c.getValue(), "utf-8");
if (c.getName().equals("password"))
password = URLDecoder.decode(c.getValue(), "utf-8");
}
}
%>
users.jsp
<%@ page language="java" import="java.util.*,java.net.*" contentType="text/html; charset=utf-8" %>
<%
request.setCharacterEncoding("utf-8");
String username = "";
String password = "";
Cookie[] cookies = request.getCookies();
if (cookies != null && cookies.length > 0) {
for (Cookie c : cookies) {
if (c.getName().equals("username"))
username = URLDecoder.decode(c.getValue(), "utf-8");
if (c.getName().equals("password"))
password = URLDecoder.decode(c.getValue(), "utf-8");
}
}
%>
用户名:<%=username %>
密码:<%=password %>
指令和动作
include 指令与动作
date.jsp
<%@ page language="java" import="java.util.*" contentType="text/html; charset=utf-8" %>
<%@ page import="java.text.*" %>
<%
Date d = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
String s = sdf.format(d);
out.println(s);
%>
include_command.jsp
<%@ include file="date.jsp" %>
include_action.jsp
forward 动作
user.jsp
<%
request.setCharacterEncoding("utf-8");
String username = "";
String password = "";
String email = "";
if (request.getParameter("username") != null)
username = request.getParameter("username");
if (request.getParameter("password") != null)
password = request.getParameter("password");
if (request.getParameter("email") != null)
email = request.getParameter("email");
%>
用户名:<%=username %>
密码:<%=password %>
电子邮箱:<%=email %>
login.jsp
forward.jsp
param 动作
在 forward
中加入传递参数