实际操作
用户的登录和注册
编写 Web层
- web页面添加基础路径
- 注册页面 regist.html
<!-- 添加 base标签,永远固定相对路径跳转的基础路径 -->
<base href="http://localhost:8080/book_project/">
- 添加对应的注册功能 RegistServlet
- 请求转发 req.getRequestDispatcher("/path")
package com.atjava.web;
import com.atjava.pojo.User;
import com.atjava.service.UserService;
import com.atjava.service.impl.UserServiceImpl;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author lv
* @create 2021-08-07 22:21
*/
public class RegistServlet extends HttpServlet {
private UserService us = new UserServiceImpl();
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// super.doPost(req, resp);
// 1.获取请求参数
String username = req.getParameter("username");
String password = req.getParameter("password");
String email = req.getParameter("email");
String code = req.getParameter("code");
// 2.检察验证码是否正确 - 当前写死 12345
if ("12345".equalsIgnoreCase(code)) {
System.out.println("code is right");
// 3.检察用户名是否可用
boolean existsUsername = us.existsUsername(username);
if (existsUsername) {
System.out.println("用户名已存在");
req.getRequestDispatcher("/pages/user/regist.html").forward(req, resp);
} else {
System.out.println("用户名可注册");
us.registUser(new User(null, username, password, email));
req.getRequestDispatcher("/pages/user/regist_success.html").forward(req, resp);
}
} else {
System.out.println("code[ " + code + " ] is error");
// 跳回注册页面
// 请求转发,必须以 ‘/’开始,并且默认在 "web"目录下
req.getRequestDispatcher("/pages/user/regist.html").forward(req, resp);
}
}
}
IDEA中 Debug调试的使用
Debug调试代码,首先需要两个元素: 断点 + Debug启动服务器
- 断点: 只需在代码要停掉的行的左边单击,就可以添加和取消
- Debug启动 Tomcat运行代码
测试工具栏:
- 折弯箭头: 让代码往下执行一行
- 向下箭头: 可以进入当前方法体内 (自己写的代码,非框架源码)
- 向上箭头: 跳出当前方法体外,与 2相反
- (红色)向下箭头: 强制进入当前方法体内
- 斜向下箭头: 跳到光标所在处,相当于临时断点 (一次性)
变量窗口 - variables
- 它可以查看当前方法范围内所有有效的变量
方法调用栈窗口 - Frames
- 它可以查看当前线程有哪些方法调用信息
- 下一行的方法调用上一行的方法,显示方法的调用关系
其它常用调试相关按钮 (左边竖栏)
- 绿色箭头: 程序一直执行,直到遇到下一个断点停止,如果没有断点,就一直执行下去
- 红色斜杠: 临时禁用所有断点
用户登录功能的实现
<base href="http://localhost:8080/book_project/" />
...
<form action="login" method="post">
...
</form>
package com.atjava.web;
import com.atjava.pojo.User;
import com.atjava.service.UserService;
import com.atjava.service.impl.UserServiceImpl;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author lv
* @create 2021-08-08 11:08
*/
public class LoginServlet extends HttpServlet {
private UserService us = new UserServiceImpl();
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// super.doPost(req, resp);
String username = req.getParameter("username");
String password = req.getParameter("password");
User user = us.login(new User(null, username, password, null));
if (null != user) {
System.out.println(user);
req.getRequestDispatcher("/pages/user/login_success.html").forward(req, resp);
} else {
req.getRequestDispatcher("/pages/user/login.html").forward(req, resp);
}
}
}
JSP
jsp及它的作用
jsp的全称是 java serverpages, java的服务器页面;
jsp的主要作用是 代替 Servlet程序回传 html页面数据;
因为 Servlet程序回传 html页面数据是一件非常繁琐的事,开发成本和维护成本极高.
- servlet方式:
package com.atjava.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @author lv
* @create 2021-08-08 14:38
*/
public class PringHtml extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// super.doGet(req, resp);
// 防止乱码
// req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html; charset=UTF-8");
// 方式一:通过响应的输出流回传 html页面数据
PrintWriter respWriter = resp.getWriter();
respWriter.write("<!DOCTYPE html>\r\t");
respWriter.write("<html lang="en">\r\t");
respWriter.write("<head>\r\t");
respWriter.write(" <meta charset="UTF-8">\r\t");
respWriter.write(" <title>jsp</title>\r\t");
respWriter.write("</head>\r\t");
respWriter.write("<body>\r\t");
respWriter.write(" 这是 html页面数据\r\t");
respWriter.write("</body>\r\t");
respWriter.write("</html>\r\t");
// 方式二:通过 jsp回传数据
//<!DOCTYPE html>
//<html lang="en">
//<head>
// <meta charset="UTF-8">
// <title>jsp</title>
//</head>
//<body>
// 这是 html页面数据
// </body>
//</html>
}
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>PringHtml</servlet-name>
<servlet-class>com.atjava.servlet.PringHtml</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>PringHtml</servlet-name>
<url-pattern>/pringHtml</url-pattern>
</servlet-mapping>
</web-app>
- jsp方式:
<%--
Created by IntelliJ IDEA.
User: Weili
Date: 2021/8/8
Time: 14:57
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>jsp回传页面</title>
</head>
<body>
这是 jsp页面数据
</body>
</html>
如何访问 jsp: jsp页面和 html页面一样,都是存放在 web目录下,访问方式和 html页面一样;
- 在 web目录下有如下文件:
- a.html页面: 访问地址是 http://ip:port/工程路径/a.html
- b.jsp页面: 访问地址是 http://ip:port/工程路径/b.html
jsp的本质
jsp页面本质上是一个 servlet程序;
当我们第一次访问 jsp页面时, Tomcat服务器会帮我们把 jsp页面翻译为 java源文件,并且将它编译为 .class字节码程序;
public final class a_jsp extends HttpJspBase {}
我们跟踪源码发现, HttpJspBase类; 它直接继承了 HttpServlet类; 也就是说 jsp翻译出的 java类,间接继承了 HttpServlet类, 也就是说翻译出来的是一个 Servlet程序;
总结: 通过翻译的 java源代码我们就可以得到结果: jsp就是 servlet程序; 通过观察翻译出的 Servlet程序的源码,可以发现,其底层实现也是通过 write(...)输出流,将 html页面数据回传给客户端.
jsp的三种语法
jsp头部的 page指令
jsp的 page指令可以修改 jsp页面中一些重要的属性或行为;
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
-
language: 表示 jsp翻译后是什么语言文件,暂时只支持 java
-
contentType: 表示 jsp返回的数据类型,也是源码中 response.setContentType()的参数值
-
pageEncoding: 表示当前 jsp页面文件本身的字符集
-
import: 跟 java源代码一样,用于导包,导类
-
errorPage: 设置当 jsp页面运行出错,自动跳转去的错误页面路径
-
isErrorPage: 设置当前 jsp页面是否为错误信息页面,默认是 false,如果是 true可以获取异常信息
-
session: 设置访问当前 jsp页面,是否会创建 HttpSession对象,默认是 true
-
extends: 设置 jsp翻译出来的 java类默认继承谁
<%@ page contentType="text/html;charset=UTF-8"
errorPage="/error500.jsp"
extends="javax.servlet.HttpServlet"
language="java" %>
---------------以下两个属性是给 out输出流使用-------------
- autoFlush: 设置当 out输出流缓冲区满了后,是否自动刷新缓冲区,默认值是 true
- buffer: 设置 out缓冲区的大小,默认是 8kb
<%@ page import="javax.lang.model.element.VariableElement" %><%--
Created by IntelliJ IDEA.
User: Weili
Date: 2021/8/8
Time: 15:28
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8"
errorPage="/error500.jsp"
extends="javax.servlet.HttpServlet"
language="java" %>
<%--
errorPage表示错误后自动跳转去的路径
这个路径一般都是以 ‘/’开始,它表示请求地址为 http://ip:port/工程路径/
映射到代码的 web目录
--%>
<html>
<head>
<title>jsp</title>
</head>
<body>
这是 b.jsp页面数据
<%
int num = 12 / 0;
%>
</body>
</html>
jsp中的常用脚本
声明脚本 (极少使用)
声明脚本的格式: <%! 声明 java代码 %>;
作用: 可以给 jsp翻译出来的 java类定义属性和方法,甚至是 静态代码块,内部类等;
- 声明类属性
- 声明 static静态块
- 声明类方法
- 声明内部类
<%@ page import="java.util.Map" %>
<%@ page import="java.util.HashMap" %><%--
Created by IntelliJ IDEA.
User: Weili
Date: 2021/8/8
Time: 14:57
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>jsp回传页面</title>
</head>
<body>
这是 a.jsp页面数据
<%--
1.声明类属性
2.声明 static静态代码块
3.声明类方法
4.声明内部类
--%>
<%-- 1.声明类属性 --%>
<%!
private Integer id;
private String name;
private static Map<String, Object> map;
%>
<%-- 2.声明 static静态代码块 --%>
<%!
static {
map = new HashMap<String, Object>();
map.put("key", "val");
map.put("key2", "val2");
map.put("key3", "val3");
}
%>
<%-- 3.声明类方法 --%>
<%!
public int abc () {
return 12;
}
%>
<%-- 4.声明内部类 --%>
<%!
public static class A {
private Integer id = 12;
private String abc = "abc";
}
%>
</body>
</html>
表达式脚本 (常用)
表达式脚本的格式是: <%= 表达式 %>;
表达式脚本的作用: 在 jsp页面上输出数据;
- 输出整型
- 输出浮点型
- 输出字符型
- 输出对象
<%--
- 输出整型
- 输出浮点型
- 输出字符型
- 输出对象
--%>
<%-- 输出整型 --%>
<br/>
<%=12%><br/>
<%-- 输出浮点型 --%>
<%=12.12%><br/>
<%-- 输出字符型 --%>
<%="中华"%><br/>
<%-- 输出对象 --%>
<%=map%><br/>
<%=request.getParameter("username")%><br/>
表达式脚本的特点:
- 所有的表达式脚本都会被翻译到 _jspService()方法中
- 表达式脚本都会被翻译成为 out.print(...)输出到页面上
- 由于表达式脚本翻译的内容都在 _jspService()方法中,所以 _jspService()中的对象都可以使用
- 表达式脚本中的 表达式不能以分号结束
代码脚本
代码脚本的格式: <% java语句 %>;
代码脚本的作用是: 可以在 jsp页面中,编写我们自己需要的功能 (写的是 java语句);
- 代码脚本 if语句
- 代码脚本 for循环语句
- 翻译后 java文件中 _jspService方法内的代码都可以 使用
<%--
- 代码脚本 if语句
- 代码脚本 for循环语句
- 翻译后 java文件中 _jspService方法内的代码都可以写
--%>
<%-- 代码脚本 if语句 --%>
<%
int i = 12;
if (i == 12) {
System.out.println("中华崛起");
} else {
System.out.println("崛起中华");
}
%>
<%-- 代码脚本 for循环语句 --%>
<%-- 代码脚本块 --%>
<%
for (int num = 0; num < 10; ++num) {
%>
<%
System.out.println(num);
}
%>
<%-- 代码脚本和 表达式脚本 组合使用--%>
<table border="1" cellspacing="0" cellpadding="0">
<%
for (int num = 0; num < 10; ++num) {
%>
<tr>
<td>
第<%=num + 1%>行
</td>
</tr>
<%
}
%>
</>
<%-- 翻译后 java文件中 _jspService方法内的代码都可以写 --%>
<%
String username = request.getParameter("username");
System.out.println(username);
%>
代码脚本的特点是:
- 代码脚本翻译后都在 _jspService() 方法中
- 代码脚本由于翻译到 _jspService() 方法中,所以在 _jspService() 方法中的现有对象都可以直接使用
- 可以由多个 代码脚本块 组合完成一个完整的 java语句
- 代码脚本可以和表达式脚本一起组合使用,在 jsp页面上输出数据
jsp中的三种注释
html 注释
html注释会被翻译到 java源代码中,在 _jspService方法里,以 out.write()输出到客户端
<!-- 这是 html注释 -->
java 注释
<%
// 单行注释
/* 多行注释 */
%>
jsp 注释
- 注:jsp注释可以注释一切
<%-- 这是 jsp注释 --%>
jsp 九大内置对象
jsp中的内置对象,是指 Tomcat在翻译 jsp页面成为 Servle源代码后,内部提供的九大对象,叫内置对象。
| 名称 | 描述 |
|---|---|
| request | 请求对象 |
| response | 响应对象 |
| pageContext | jsp的上下文对象 |
| session | 会话对象 |
| application | ServletContext对象 |
| config | ServletConfig对象 |
| out | jsp输出流对象 |
| page | 指向当前 jsp的对象 |
| exception | 异常对象(isErrorPage="true") |
jsp 四大域对象
域对象是可以像 Map一样存取数据的对象;四个域对象功能都一样,不同的是它们对数据的存取范围。
虽然四个域对象都可以存取数据,但在它们的使用上是由优先顺序的;四个域的优先顺序分别是,它们从小到大的范围顺序(优化内存)。
pageContext -> request -> session -> application
- pageContext:(PageContextImpl类),当前 jsp页面范围内有效,跳出当前也米娜后无效
- request:(HttpServletRequest类),一次请求内有效
- session:(HttpSession类),一个会话范围内有效(从浏览器访问服务器开始,直到浏览器关闭结束)
- application:(ServletContext类),整个 web工程范围内有效(只要 web工程不停止,数据都有效)
// scope.jsp
<%--
Created by IntelliJ IDEA.
User: Weili
Date: 2021/8/10
Time: 22:37
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>域对象</title>
</head>
<body>
<h2>scope.jsp页面</h2>
<%
// 四个域中都分别保存数据
pageContext.setAttribute("key", "pageContext");
request.setAttribute("key", "request");
session.setAttribute("key", "session");
application.setAttribute("key", "application");
%>
pageContext 域是否有值:<%=pageContext.getAttribute("key")%><br/>
request 域是否有值:<%=request.getAttribute("key")%><br/>
session 域是否有值:<%=session.getAttribute("key")%><br/>
application 域是否有值:<%=application.getAttribute("key")%><br/>
<%
request.getRequestDispatcher("/scope2.jsp").forward(request, response);
%>
</body>
</html>
// scope2.jsp
<%--
Created by IntelliJ IDEA.
User: Weili
Date: 2021/8/10
Time: 22:37
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>域对象</title>
</head>
<body>
<h2>scope2.jsp页面</h2>
pageContext 域是否有值:<%=pageContext.getAttribute("key")%><br/>
request 域是否有值:<%=request.getAttribute("key")%><br/>
session 域是否有值:<%=session.getAttribute("key")%><br/>
application 域是否有值:<%=application.getAttribute("key")%><br/>
</body>
</html>
jsp中的 out输出和 response.getWriter输出的区别
jsp中的代码执行时,内存中会有 out缓冲区和 response缓冲区,当 jsp页面中的所有代码执行完成后会做以下两个操作:
- 执行 out.flush()操作,将 out缓冲区中的数据追加到 response缓冲区末尾
- 执行 response的刷新操作,将全部数据写给客户端
由于 jsp翻译后,底层源码都是使用 out来进行输出,所以一般情况下,我们在 jsp页面中统一使用 out.write()输出,避免打乱页面输出内容的顺序。
- out.write() 输出字符串没有问题,只适合输出字符串
- out.print() 输出任意数据都没有问题(都转换成为字符串后调用 write输出)
(char)num; // 将 num 转换为 ASCII字符,后输出
- 结论:在 jsp页面中,可以统一使用 out.print()进行输出
jsp 的常用标签
jsp 静态包含(推荐)
页面中共同的部分做成 "组件",其它页面统一引入,有修改时只修改一个 "组件" 即可
-
语法格式:<%@ include file=""%>
-
file属性指定你要包含的 jsp页面的路径
-
地址中第一个为 "/" 表示为 http://ip:port/工程路径/ 映射到代码的 web目录
-
main.jsp
<%--
C:\Users\Weili\.IntelliJIdea2018.1\system\tomcat\Tomcat_8_0_50_JavaWeb_5\work\Catalina\localhost\ROOT\org\apache\jsp\include
Created by IntelliJ IDEA.
User: Weili
Date: 2021/8/11
Time: 22:31
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>main</title>
</head>
<body>
头部信息<br/>
主体内容<br/>
<%--页脚信息<br/> 将页脚部分抽离出来,使用静态包含方法--%>
<%--
<%@ include file=""%> 这就是静态包含语法,类似于组件的引用
file属性指定你要包含的 jsp页面的路径
地址中第一个为 "/" 表示为 http://ip:port/工程路径/ 映射到代码的 web目录
--%>
<%@ include file="/include/footer.jsp" %>
</body>
</html>
- footer.jsp
<%--
Created by IntelliJ IDEA.
User: Weili
Date: 2021/8/11
Time: 22:34
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>footer</title>
</head>
<body>
页脚信息<br/>
</body>
</html>
- 特点:
- 静态包含不会编译被包含的 jsp页面
- 静态包含只是将被包含的 jsp页面的代码拷贝到包含的位置执行输出
jsp 动态包含
- 语法格式:
<jsp:include page="/...">
<jsp:param name="xxx" value="xxx"></jsp:param>
</jsp:include>
- page:属性是指定你要包含的 jsp页面的路径
- 动态包含也可以像 静态包含一样,将被包含的内容执行输出到包含位置
- main.jsp
<%--
Created by IntelliJ IDEA.
User: Weili
Date: 2021/8/11
Time: 22:31
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>main</title>
</head>
<body>
头部信息<br/>
主体内容<br/>
<%--页脚信息<br/> 将页脚部分抽离出来,使用静态包含方法--%>
<%--
<%@ include file="/..."%> 这就是静态包含语法,类似于组件的引用
file属性指定你要包含的 jsp页面的路径
地址中第一个为 "/" 表示为 http://ip:port/工程路径/ 映射到代码的 web目录
--%>
<%--<%@ include file="/include/footer.jsp" %>--%>
<%--
<jsp:include page="/...">
<jsp:param name="xxx" value="xxx"></jsp:param>
</jsp:include>
page:属性是指定你要包含的 jsp页面的路径
动态包含也可以像 静态包含一样,将被包含的内容执行输出到包含位置
--%>
<jsp:include page="/include/footer.jsp">
<jsp:param name="username" value="root"></jsp:param>
</jsp:include>
</body>
</html>
- footer.jsp
<%--
Created by IntelliJ IDEA.
User: Weili
Date: 2021/8/11
Time: 22:34
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>footer</title>
</head>
<body>
页脚信息<br/>
获取参数:<%=request.getParameter("username")%>
</body>
</html>
-
动态包含的底层原理:
-
特点:
- 动态包含会把包含的 jsp页面也翻译成为 java代码
- 动态包含底层代码使用如下代码去调用被包含的 jsp页面执行输出(静态包含是将被引用的页面完全复制过来输出):
org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "/include/footer.jsp", out, false);- 动态包含,还可以传递参数
jsp 请求转发
- 语法格式:
<jsp:forward page="/..."></jsp:forward> - page表示转发的内容
<%--
请求转发,方式一:
--%>
<%--<%--%>
<%--request.getRequestDispatcher("/scope2.jsp").forward(request, response);--%>
<%--%>--%>
<%--
请求转发,方式二:使用标签
<jsp:forward page="/..."></jsp:forward>
--%>
<jsp:forward page="/scope.jsp"></jsp:forward>
请求转发的具体使用:
一、 客户端:
- 将搜索关键词发送给 SearchServlet程序
二、 服务端:
- 获取请求中参数:关键字
- 发送 sql请求,查询有关数据
- 保存 sql返回的结果,保存到 request域中
- 请求转发
三、jsp页面:
- 获取 request域中的数据,返回到客户端
实例:
- web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>SearchStudentServlet</servlet-name>
<servlet-class>com.atjava.servlet.SearchStudentServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>SearchStudentServlet</servlet-name>
<url-pattern>/sss</url-pattern>
</servlet-mapping>
</web-app>
- SearchStudentServlet.java
package com.atjava.servlet;
import com.atjava.pojo.Student;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* @author lv
* @create 2021-08-16 21:54
*/
public class SearchStudentServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// super.doGet(req, resp);
// 1.获取请求参数
String keywords = req.getParameter("keywords");
System.out.println("keywords: " + keywords);
// 2.发 sql语句,查询信息
// 模拟 sql请求结果
List<Student> studentList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
int a = i + 1;
studentList.add(new Student(a, "vv-" + a, a + 10, "phone-" + a));
}
// 3.保存查询到的结果,放到 request中
req.setAttribute("stuList", studentList);
// 4.请求转发
req.getRequestDispatcher("/include/showStudent.jsp").forward(req, resp);
}
}
- showStudent.jsp
<%@ page import="java.util.List" %>
<%@ page import="com.atjava.pojo.Student" %>
<%@ page import="java.util.ArrayList" %>
<%--
Created by IntelliJ IDEA.
User: Weili
Date: 2021/8/16
Time: 20:45
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>student-info</title>
<style>
table {
width: 560px;
border: 1px #f0f solid;
border-collapse: collapse;
}
tr, td {
border: 1px #000 solid;
}
</style>
</head>
<body>
<%--
请求转发的具体使用:
客户端:
将搜索关键词发送给 SearchServlet程序
服务端:
1.获取请求中参数:关键字
2.发送 sql请求,查询有关数据
3.保存 sql返回的结果,保存到 request域中
4.请求转发
jsp页面:
获取 request域中的数据,返回到客户端
--%>
<%--輸出一個表格,包含10個學生的信息--%>
<%
List<Student> studentList = (ArrayList) request.getAttribute("stuList");
// for (int i = 0; i < 10; i++) {
// int a = i + 1;
// studentList.add(new Student(a, "lv-" + a, a + 10, "phone-" + a));
// }
%>
<table>
<tr>
<td>id:</td>
<td>name:</td>
<td>age:</td>
<td>phone:</td>
</tr>
<%
for (Student student : studentList) {
%>
<tr>
<td><%=student.getId()%></td>
<td><%=student.getName()%></td>
<td><%=student.getAge()%></td>
<td><%=student.getPhone()%></td>
</tr>
<%
}
%>
</table>
</body>
</html>
练习
- 输出乘法口诀:
<%--jsp
Created by IntelliJ IDEA.
User: Weili
Date: 2021/8/12
Time: 22:19
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>九九乘法表</title>
</head>
<body>
<h2>九九乘法表:</h2>
<%--<table border cellpadding="4" cellspacing="0">--%>
<table>
<%
for (int i = 1; i <= 9; ++i) {
%>
<tr>
<%
for (int j = 1; j <= i; ++j) {
%>
<td>
<%--<%=i%> * <%=j%> = <%=i*j%>--%>
<%= i + " X " + j + " = " + (i*j)%>
</td>
<%
}
%>
</tr>
<%
}
%>
</table>
</body>
</html>
- 向客户端输出一个包含 10学生的表格
<%@ page import="java.util.List" %>
<%@ page import="com.atjava.pojo.Student" %>
<%@ page import="java.util.ArrayList" %>
<%--
Created by IntelliJ IDEA.
User: Weili
Date: 2021/8/16
Time: 20:45
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>student-info</title>
<style>
table {
width: 560px;
border: 1px #f0f solid;
border-collapse: collapse;
}
tr, td {
border: 1px #000 solid;
}
</style>
</head>
<body>
<%--
请求转发的具体使用:
客户端:
将搜索关键词发送给 SearchServlet程序
服务端:
1.获取请求中参数:关键字
2.发送 sql请求,查询有关数据
3.保存 sql返回的结果,保存到 request域中
4.请求转发
jsp页面:
获取 request域中的数据,返回到客户端
--%>
<%--輸出一個表格,包含10個學生的信息--%>
<%
List<Student> studentList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
int a = i + 1;
studentList.add(new Student(a, "lv-" + a, a + 10, "phone-" + a));
}
%>
<table>
<tr>
<td>id:</td>
<td>name:</td>
<td>age:</td>
<td>phone:</td>
</tr>
<%
for (Student student : studentList) {
%>
<tr>
<td><%=student.getId()%></td>
<td><%=student.getName()%></td>
<td><%=student.getAge()%></td>
<td><%=student.getPhone()%></td>
</tr>
<%
}
%>
</table>
</body>
</html>
Listener监听器
-
Listener监听器是 JavaWeb的三大组件之一;
-
JavaWeb三大组件分别是: Servlet程序, Filter过滤器, Listener监听器;
-
其中 Listener就是 JavaEE的规范, 就是接口;
-
监听器的作用是, 监听某种事物的变化, 然后通过回调函数,反馈给客户 (程序)去做相应的处理;
-
监听器一共有八个, 现在有用的只有 ServletContextListener监听器还在使用
ServletContextListener 监听器
ServletContextListener它可以监听 ServletContext对象的创建和销毁;
servletContext对象在 web工程启动时创建,在 web工程停止时销毁;
监听到创建和销毁后都会分别调用 ServletContextListener监听器的方法反馈;
这两个方法分别是:
-
public void contextInitialized(ServletContextEvent sce)
- 在 ServletContext 对象创建后马上调用,做初始化
-
public void contextDestroyed(ServletContextEvent sce)
- 在 ServletContext对象销毁之后调用
使用 ServletContextListener监听器监听 ServletContext对象, 使用步骤如下:
- 编写接口实现类去实现 ServletContextListener接口
- 实现 创建 和 销毁回调方法
package com.atjava.listener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
/**
* @author lv
* @create 2021-08-17 21:37
*/
public class MyServletContextListenerImpl implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("ServletContext Object init");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("ServletContext Object destroyed");
}
}
- 到 web.xml 中去配置监听器
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>SearchStudentServlet</servlet-name>
<servlet-class>com.atjava.servlet.SearchStudentServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>SearchStudentServlet</servlet-name>
<url-pattern>/sss</url-pattern>
</servlet-mapping>
<!-- 配置监听器 -->
<listener>
<listener-class>com.atjava.listener.MyServletContextListenerImpl</listener-class>
</listener>
</web-app>
EL表达式 & JSTL标签库
EL表达式
EL表达式的全称是:Expression Language 表达式语言
- EL表达式主要是为了替换 jsp中的表达式脚本
EL表达式及其作用:
EL表达式主要是代替 jsp页面中的表达式脚本在 jsp页面中进行数据的输出,因为 EL表达式在输出数据时,要比 jsp的表达式脚本要简洁很多。
- EL表达式的格式是:
${key} - EL表达式在输出 null值时,输出的是空串;jsp表达式脚本输出 null值时,不做处理输出 null值
<%--
Created by IntelliJ IDEA.
User: Weili
Date: 2021/8/18
Time: 21:53
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>testEl</title>
</head>
<body>
<%
request.setAttribute("key", "EL");
%>
表达式脚本输出 key值:<%=request.getAttribute("key") == null ? "" : request.getAttribute("key")%><br/>
<%--
1.EL表达式对于没有值的处理是:输出空值,而表达式脚本输出的是 null
--%>
EL表达式输出 key值:${key}
</body>
</html>
EL表达式搜索域数据的顺序:
EL表达式主要是在 jsp页面中输出数据;其主要是输出域对象中的数据。
当四个域(pageContext、request、session、application)中都有相同的 key名时,EL表达式会按照四个域从小到大的顺序去搜索,找到后就输出,和代码书写的先后顺序无关。
<%--
Created by IntelliJ IDEA.
User: Weili
Date: 2021/8/18
Time: 22:11
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>testEL2</title>
</head>
<body>
<%
pageContext.setAttribute("key", "pageContext"); // 当前页面
request.setAttribute("key", "request"); // 一次请求
session.setAttribute("key", "session"); // session 浏览器关闭后
application.setAttribute("key", "application"); // 服务关闭
%>
who is priority:${key}
</body>
</html>
EL表达式输出 Bean的普通属性 & 数组属性 & List集合属性 & Map集合属性
注意:EL实际上获取的是 Bean中属性对应的 getXxx方法,没有 getXxx方法就无法获取 Bean中对象的属性。
<%@ page import="com.atjava.pojo.Person" %>
<%@ page import="java.util.ArrayList" %>
<%@ page import="java.util.List" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.util.Map" %><%--
Created by IntelliJ IDEA.
User: Weili
Date: 2021/8/18
Time: 22:35
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>PersonInfo</title>
</head>
<body>
<%
Person p1 = new Person();
p1.setName("wvv");
p1.setAge(12);
p1.setPhones(new String[]{"15666002221", "4512552155", "64513131321"});
List<String> arrayList = new ArrayList<>();
arrayList.add("车夹装");
arrayList.add("贵腚");
p1.setCities(arrayList);
Map<String, Object> map = new HashMap<>();
map.put("key", "person");
map.put("des", "beautifual");
p1.setMap(map);
pageContext.setAttribute("person", p1);
%>
输出 Person全信息(toString):${person}<br/>
输出普通属性 name:${person.name}<br/>
输出普通属性 age:${person.age}<br/>
输出 Array属性中的一个元素:${person.phones[0]}<br/>
输出 List属性:${person.cities}<br/>
输出 List属性的一个元素:${person.cities[1]}<br/>
输出 Map属性:${person.map}<br/>
输出 Map属性的一个键值:${person.map.key}<br/>
</body>
</html>
package com.atjava.pojo;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* @author lv
* @create 2021-08-18 22:29
*/
public class Person {
private String name = "vv";
private int age;
private String[] phones;
private List<String> cities;
private Map<String, Object> map;
public Person() {
}
public Person(String name, int age, String[] phones, List<String> cities, Map<String, Object> map) {
this.name = name;
this.age = age;
this.phones = phones;
this.cities = cities;
this.map = map;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String[] getPhones() {
return phones;
}
public void setPhones(String[] phones) {
this.phones = phones;
}
public List<String> getCities() {
return cities;
}
public void setCities(List<String> cities) {
this.cities = cities;
}
public Map<String, Object> getMap() {
return map;
}
public void setMap(Map<String, Object> map) {
this.map = map;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + ''' +
", age=" + age +
", phones=" + Arrays.toString(phones) +
", cities=" + cities +
", map=" + map +
'}';
}
}
EL表达式 - 运算
- 语法:
${ 运算表达式 }, EL表达式支持如下运算:
关系运算:
| 关系运算符 | 说明 | 范例 | 结果 |
|---|---|---|---|
| == 或 eq | 等于 | ${ 6==6 }, ${ 6 eq 6 } | true |
| != 或 ne | 不等于 | ${ 4!=6 }, ${ 4 ne 6 } | false |
| < 或 lt | 小于 | ${ 3<6 }, ${ 3 lt 6 } | true |
| > 或 gt | 大于 | ${ 5>6 }, ${ 5 gt 6 } | false |
| <= 或 le | 小于等于 | ${ 5<=6 }, ${ 5 le 6 } | true |
| >= 或 ge | 大于等于 | ${ 3>=6 }, ${ 3 ge 6 } | false |
逻辑运算:
| 逻辑运算符 | 说明 | 范例 | 结果 | ||||
|---|---|---|---|---|---|---|---|
| && 或 and | 与运算 | ${ 6==6 && 5 > 6 }, ${ 6==6 and 5 > 6 } | false | ||||
| 或 or | 或运算 | ${ 6==6 | 5 > 6 }, ${ 6==6 or 5 > 6 } | true | |||
| ! 或 not | 取反运算 | ${ !true }, ${ not true } | false |
算数运算:
| 算数运算符 | 说明 | 范例 | 结果 |
|---|---|---|---|
| + | 加法 | ${ 12 + 6 } | 18 |
| - | 减法 | ${ 12 - 6 } | 6 |
| * | 乘法 | ${ 12 * 6 } | 72 |
| / 或 div | 除法 | { 12 div 6 } | 2 |
| % 或 mod | 取模 | ${ 144 % 10 } 或 ${ 144 mod 10 } | 4 |
empty 运算
empty运算可以判断一个数据是否为空, 如果为空输出 true, 反之输出 false
- 值为 null值时,为空
- 值为空串时, 为空
- 值是 Object类型数组, 长度为零时, 为空
- List集合, 元素个数为零, 为空
- map集合, 元素个数为零, 为空
<%
request.setAttribute("emptyNull", null);
request.setAttribute("emptyStr", "");
request.setAttribute("emptyList", new ArrayList<String>());
request.setAttribute("emptyObj", new Object[]{});
request.setAttribute("emptyMap", new HashMap<String, Object>());
%>
emptyNull:${ empty emptyNull }<br/>
emptyStr:${ empty emptyStr }<br/>
emptyList:${ empty emptyList }<br/>
emptyObj:${ empty emptyObj }<br/>
emptyMap:${ empty emptyMap }<br/>
三元运算
- 语法:
${ 表达式1 ? 表达式2 : 表达式3 }
三元表达式:${ 12 == 12 ? "等于" : "不等于"}<br/>
"." 运算符 和 "[]"中括号 运算符
- 点运算,可以输出 Bean对象中某个属性的值
- 中括号运算, 可以输出 Bean对象中某个数组元素的值
- 并且 []括号运算,可以输出 map集合中 key中含有特殊字符的 key值
<%
request.setAttribute("emptyNull", null);
request.setAttribute("emptyStr", "");
List<String> list = new ArrayList<>();
request.setAttribute("emptyList", list);
list.add("list1");
request.setAttribute("emptyObj", new Object[]{});
Map<String, Object> map = new HashMap<String, Object>();
map.put("k1", "map1");
map.put("k==y", "map2");
request.setAttribute("emptyMap", map);
%>
emptyNull:${ empty emptyNull }<br/>
emptyStr:${ empty emptyStr }<br/>
emptyList:${ empty emptyList }<br/>
emptyObj:${ empty emptyObj }<br/>
emptyMap:${ empty emptyMap }<br/>
三元表达式:${ 12 == 12 ? "等于" : "不等于"}<br/>
点运算:${emptyMap.k1}<br/>
点运算(k==y):${emptyMap["k==y"]}<br/>
中括号运算:${emptyList[0]}<br/>
<%--
三元表达式:等于
点运算:map1
点运算(k==y):map2
中括号运算:list1
--%>
EL表达式的 11个隐含对象
EL表达式中 11个隐含对象,是 EL表达式中自己定义的,可以直接使用。
| 变量 | 类型 | 作用 |
|---|---|---|
| pageContext | PageContextImpl | 它可以获取 jsp中的 九大内置对象 |
| pageScope | Map<String, Object> | 它可以获取 pageContext域中的数据 |
| requestScope | Map<String, Object> | 它可以获取 request域中的数据 |
| sessionScope | Map<String, Object> | 它可以获取 session域中的数据 |
| applicationScope | Map<String, Object> | 它可以获取 servletContext域中的数据 |
| param | Map<String, String> | 它可以获取请求参数的值 |
| paramsValues | Map<String, String[]> | 它也可以获取请求参数的值,在获取多个值时使用 |
| header | Map<String, String> | 它可以获取请求头的信息 |
| headerValues | Map<String, String[]> | 它也可以获取请求头的信息,可以获取多个值的情况 |
| cookie | Map<String, Cookie> | 它可以获取当前请求的 Cookie信息 |
| initParam | Map<String, String> | 它可以获取在 web.xml中配置的 上下文参数<context-param> |
EL获取四个特定域中的属性
- pageScope:pageContext域
- requestScope:Request域
- sessionScope:Session域
- applicationScope:ServletContext域
<%--
Created by IntelliJ IDEA.
User: Weili
Date: 2021/8/22
Time: 21:43
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>scope</title>
</head>
<body>
<%
pageContext.setAttribute("key1", "value1");
pageContext.setAttribute("key2", "value2");
request.setAttribute("key1", "value1");
session.setAttribute("key1", "value1");
application.setAttribute("key1", "value1");
%>
pageScope: ${pageScope.key1}<br/>
requestScope: ${requestScope.key1}<br/>
sessionScope: ${sessionScope.key1}<br/>
applicationScope: ${applicationScope.key1}<br/>
</body>
</html>
pageContext对象的使用
- 协议:
- 服务器 ip:
- 服务器端口号:
- 获取工程路径:
- 获取请求方法:
- 获取客户端 ip地址:
- 获取会话的 id编号:
<%--
Created by IntelliJ IDEA.
User: Weili
Date: 2021/8/22
Time: 21:55
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>pageContext</title>
</head>
<body>
${pageContext} <%-- org.apache.jasper.runtime.PageContextImpl@5447dff4 --%>
<%--- 协议:--%>
jsp协议:<%=request.getScheme()%><br/>
EL协议:${pageContext.request.scheme}<br/>
<%--- 服务器 ip:--%>
jsp服务器 ip:<%=request.getServerName()%><br/>
EL服务器 ip:${pageContext.request.serverName}<br/>
<%--- 服务器端口号:--%>
jsp服务器端口号:<%=request.getServerPort()%><br/>
EL服务器端口号:${pageContext.request.serverPort}<br/>
<%--- 获取工程路径:--%>
jsp获取工程路径:<%=request.getContextPath()%><br/>
EL获取工程路径:${pageContext.request.contextPath}<br/>
<%--- 获取请求方法:--%>
jsp获取请求方法:<%=request.getMethod()%><br/>
EL获取请求方法:${pageContext.request.method}<br/>
<%--- 获取客户端 ip地址:--%>
jsp获取客户端 ip地址:<%=request.getRemoteHost()%><br/>
EL获取客户端 ip地址:${pageContext.request.remoteHost}<br/>
<%--- 获取会话的 id编号:--%>
jsp获取会话的 id编号:<%=session.getId()%><br/>
EL获取会话的 id编号:${pageContext.session.id}<br/>
<%--
实际开发中 pageContext的使用技巧:
--%>
<%
pageContext.setAttribute("req", request);
%>
pageContext的使用技巧:${req.serverName}<br/>
<%--
EL协议:http
jsp服务器 ip:localhost
EL服务器 ip:localhost
jsp服务器端口号:8080
EL服务器端口号:8080
jsp获取工程路径:
EL获取工程路径:
jsp获取请求方法:GET
EL获取请求方法:GET
jsp获取客户端 ip地址:127.0.0.1
EL获取客户端 ip地址:127.0.0.1
jsp获取会话的 id编号:5FFE428A14CB6089A690DDF6B5E26FDF
EL获取会话的 id编号:5FFE428A14CB6089A690DDF6B5E26FDF
--%>
</body>
</html>
pageContext在实际开发中使用的技巧
<%--
实际开发中 pageContext的使用技巧:
--%>
<%
pageContext.setAttribute("req", request);
%>
pageContext的使用技巧:${req.serverName}<br/>
EL表达式其它隐含对象的使用
<%--
Created by IntelliJ IDEA.
User: Weili
Date: 2021/8/23
Time: 20:49
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>other</title>
</head>
<body>
<%-- param:请求的参数,Map类型--%>
${param}<br/>
${paramValues}<br/>
param 输出请求参数 username:${param.username}<br/>
param 输出请求参数 password:${param.password}<br/>
paramValues 输出请求参数 username:${paramValues.username[0]}<br/>
paramValues 输出请求参数 password:${paramValues.password[0]}<br/>
paramValues 输出请求参数 hobby:${paramValues.hobby[0]}<br/>
paramValues 输出请求参数 hobby:${paramValues.hobby[1]}<br/>
<%--
{password=456, username=123, hobby=java}
{password=[Ljava.lang.String;@531609fc, username=[Ljava.lang.String;@164293d5, hobby=[Ljava.lang.String;@3c64cd8}
param 输出请求参数 username:123
param 输出请求参数 password:456
paramValues 输出请求参数 username:123
paramValues 输出请求参数 password:456
paramValues 输出请求参数 hobby:java
paramValues 输出请求参数 hobby:cpp
--%>
<%-- header:获取请求头的信息 --%>
${header}<br/>
User-Agent:${header["User-Agent"]}<br/>
Connection:${header.Connection}<br/>
User-Agent:${headerValues["User-Agent"][0]}<br/>
<%--
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36
Connection:keep-alive
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36
--%>
<br/>
${cookie}<br/>
JSESSIONID:${cookie.JSESSIONID}<br/>
获取 cookie的名称:${cookie.JSESSIONID.name}<br/>
获取 cookie的值:${cookie.JSESSIONID.value}<br/>
<%--
{JSESSIONID=javax.servlet.http.Cookie@4a346f24, Idea-6b7e3bf7=javax.servlet.http.Cookie@1bbfc951}
JSESSIONID:javax.servlet.http.Cookie@4a346f24
获取 cookie的名称:JSESSIONID
获取 cookie的值:404BD160A60F46130F80E087A1B88CE1
--%>
<br/>
<%-- 修改配置文件后一定要重新部署才能生效 --%>
${initParam}<br/>
输出 context-param的值 username:${initParam.username}<br/>
输出 context-param的值 url:${initParam.url}<br/>
<%--
{password=root123, initParam=Map, url=jdbc:mysql//localhost:3306/test, username=root}
输出 context-param的值 username:root
输出 context-param的值 url:jdbc:mysql://localhost:3306/test
--%>
</body>
</html>
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<context-param>
<param-name>initParam</param-name>
<param-value>Map</param-value>
</context-param>
<context-param>
<param-name>username</param-name>
<param-value>root</param-value>
</context-param>
<context-param>
<param-name>password</param-name>
<param-value>root123</param-value>
</context-param>
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/test</param-value>
</context-param>
</web-app>
JSTL 标签库(次重点)
- JSTL标签库主要是为了替换代码脚本
JSTL标签库,全称是指 JSP Standard Tag Library,JSP标准标签库,是一个不断完善的开放源代码的 JSP标签库。
EL表达式主要是为了替换 jsp中的表达式脚本,而标签库则是为了替换代码脚本。这样使得整个 jsp 页面变得更加简洁。
JSTL由五个不同功能的标签库组成:
| 功能范围 | uri | 前缀 |
|---|---|---|
核心标签库(重点) | java.sun.com/jsp/jstl/co… | c |
| 格式化 | java.sun.com/jsp/jstl/fm… | fmt |
| 函数 | java.sun.com/jsp/jstl/fu… | fn |
| 数据库(不使用) | java.sun.com/jsp/jstl/sq… | sql |
| XML(不使用) | java.sun.com/jsp/jstl/xm… | x |
在 jsp页面中使用 taglib指令引入标签库:
// core标签库
<%@ taglib prefix="c"uri="http://java.sun.com/jsp/jstl/core" %>
// xml标签库
<%@ taglib prefix="x"uri="http://java.sun.com/jsp/jstl/xml" %>
// fmt标签库
<%@ taglib prefix="fmt"uri="http://java.sun.com/jsp/jstl/fmt" %>
// sql标签库
<%@ taglib prefix="sql"uri="http://java.sun.com/jsp/jstl/sql" %>
// functions标签库
<%@ taglib prefix="fn"uri="http://java.sun.com/jsp/jstl/functions" %>
JSTL标签库的使用步骤:
- 先导入 jstl标签库的 jar包:使用右击选择 add as Library... 配置 jar包
taglibs-standard-impl-1.2.1.jar taglibs-standard-spec-1.2.1.jar - 使用 taglib指令 导入用到的标签库
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
core 核心库的使用
<c:set />
- set标签可以向域中保存数据
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--
Created by IntelliJ IDEA.
User: Weili
Date: 2021/8/23
Time: 22:39
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>core</title>
</head>
<body>
<%--
<c:set />:向域中保存数据
scopeObj.setAttribute(key, value);
scope 属性值表示保存到的域对象
var 属性值设置 key属性名
value属性值 设置属性值
--%>
set之前:${pageScope.key1 ? pageScope.key1 : "无"}<br/>
<c:set scope="page" var="key1" value="abc123" />
set之后:${pageScope.key1}<br/>
<%--
set之前:无
set之后:abc123
--%>
</body>
</html>
<c:if />
- if 标签用来做 if判断
<%--
<c:if />:if 标签用来做 if判断
test属性值表示判断的条件(使用 EL表达式)
--%>
<c:if test="${ 12 == 12}">
<%--if判断成立,执行内部的代码--%>
<h2>if条件成立</h2>
</c:if>
<c:choose><c:when><c:otherwise> 标签
- 多路判断,与 switch ... case ... default 类似
- 注意:
- 标签中是能使用 jsp注释
- when标签的父标签一定是 choose 标签
<%--
<c:choose><c:when><c:otherwise> 标签:多路判断
choose标签:开始选择判断
when标签:表示每种判断情况
test属性值表示当前判断的值
otherwise标签:条件都不符合时的默认情况
--%>
<%
request.setAttribute("height", 172);
%>
<c:choose>
<c:when test="${requestScope.height > 190}">
<h3>小巨人</h3>
</c:when>
<c:when test="${requestScope.height > 180}">
<h3>高人</h3>
</c:when>
<c:when test="${requestScope.height > 170}">
<h3>jental man</h3>
</c:when>
<c:otherwise>
<c:choose>
<c:when test="${requestScope.height > 160}">
<h3>大于 160</h3>
</c:when>
<c:when test="${requestScope.height > 150}">
<h3>大于 150</h3>
</c:when>
<c:when test="${requestScope.height > 140}">
<h3>大于 140</h3>
</c:when>
</c:choose>
</c:otherwise>
</c:choose>
<c:forEach />
- 遍历输出数据
- 遍历数据 1 - 10,输出
- begin设置 开始索引
- end设置 结束索引
- var表示循环中的变量(当前正在遍历的数据)
<%--
1. 遍历数据 1 - 10,输出
begin设置 开始索引
end设置 结束索引
var表示循环中的变量(当前正在遍历的数据)
--%>
<ul>
<c:forEach var="i" begin="1" end="10">
<li>
${i}
</li>
</c:forEach>
</ul>
- 遍历 Object数组
- items 表示遍历的数据源(遍历的集合)
- var 表示当前遍历到的数据
<%--
2. 遍历 Object数组
for (obj : arr)
items 表示遍历的数据源(遍历的集合)
var 表示当前遍历到的数据
--%>
<%
request.setAttribute("strArr", new String[]{"123", "456", "789"});
%>
<table border="1">
<c:forEach var="item" items="${requestScope.strArr}">
<tr>
<td>
${item}
</td>
</tr>
</c:forEach>
</table>
- 遍历 List集合中的对象 Person,有属性:编号、用户名、密码、年龄、电话
- begin 表示遍历的开始索引值
- end 表示结束的索引值
- step 表示步长值,默认为 1
- varStatus 表示当前遍历到的数据状态,JSTL内部类:LoopTagStatus
public interface LoopTagStatus { public Object getCurrent(); 表示当前遍历到的数据 public int getIndex(); 表示获取遍历的索引 public int getCount(); 表示当前遍历的个数 1、2,3 ... public boolean isFirst(); 表示当前遍历到的数据是否为第一条 public boolean isLast(); 表示当前遍历到的数据是否为最后一条 public Integer getBegin(); 获取 设置的 begin属性值 public Integer getEnd(); 获取 设置的 end属性值 public Integer getStep(); 获取 设置的 step属性值 } - 实例:
<%@ page import="com.atjava.pojo.Student" %>
// ...
<%--
3. 遍历 List集合 中存放的 person类,属性:编号、用户名、密码、年龄、电话信息
--%>
<%
List<Student> list = new ArrayList<>();
list.add(new Student(1001, "vv", "123", 12, "15620354698"));
list.add(new Student(1002, "w", "456", 21, "15620794698"));
list.add(new Student(1003, "vv1", "789", 22, "15642354698"));
list.add(new Student(1004, "vv2", "321", 20, "15646354698"));
pageContext.setAttribute("stuList", list);
%>
<table border="1">
<tr>
<td>编号:</td>
<td>用户名:</td>
<td>密码:</td>
<td>年龄:</td>
<td>电话信息:</td>
</tr>
<%--
begin 表示遍历的开始索引值
end 表示结束的索引值
step 表示步长值,默认为 1
varStatus 表示当前遍历到的数据状态,JSTL内部类:LoopTagStatus
--%>
<c:forEach begin="1" end="2" varStatus="status" var="item" items="${pageScope.stuList}">
<tr>
<td>${item.id}</td>
<td>${item.username}</td>
<td>${item.password}</td>
<td>${item.age}</td>
<td>${item.phone}</td>
<td>${status.index}</td>
</tr>
</c:forEach>
</table>
- 遍历 Map集合
<%--
4. 遍历 Map集合
for (Map.Entry<String, Object> entry : map.entrySet()) {
entry.getKey()
entry.getValue()
}
--%>
<%
Map<String, Object> map = new HashMap<>();
map.put("key1", 123);
map.put("key2", "value2");
pageContext.setAttribute("map", map);
%>
<table border="1">
<c:forEach var="entry" items="${pageScope.map}">
<tr>
<td>${entry.key}:</td>
<td> ${entry.value}</td>
</tr>
</c:forEach>
</table>
文件的上传和下载
文件的上传介绍
- 要有一个
form标签,method=post请求 - form标签的
encType属性值必须为 multipart/form-data值 - 在 form标签中使用 input标签
type=file添加上传的文件 - 编写服务器代码(Servlet程序)接收,处理上传的数据
- encType = multipart/form-data表示提交的数据,以多段(一个表单项对应一个数据段)的形式进行拼接,然后
以二进制流的形式发送给服务器 - Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryA0Z1si2nrDg9uUnb 表示提交的数据类型 及数据分段的分隔符
- boundary=----WebKitFormBoundaryA0Z1si2nrDg9uUnb 的值是每次提交时随机生成的,它就是每段数据的分隔符
- 每个分隔符都表示一段数据的开始
- 分隔符的值后面多了两个减号,表示上传数据的结束
commons-fileupload.jar常用 API介绍
-
ServletFileUpload类,用于解析上传的数据
-
isMultipartContent() 判断当前上传的数据格式是否是多端格式
-
FileItem类,表示每一个表单项
-
parseRequest() 解析上传的数据
-
boolean FileItem.isFormField() 判断当前这个表单项,是否为普通表单项,true表示普通类型的表单项,false表示上传的文件类型
-
String FileItem.getFieldName() 获取当前表单项的 name 值
-
String FileItem.getString() 获取当前表单项的值
-
String FileItem.getName() 获取上传的文件名
-
void FileItem.write(file) 将上传的文件写到 参数 file所指向的磁盘位置
boolean ServletFileUpload.isMultipartContent(HttpServletRequest request)
public List<FileItem> parseRequest(HttpServletRequest request)
boolean FileItem.isFormField()
String FileItem.getFieldName()
String FileItem.getString()
String FileItem.getName()
void FileItem.write(file)
文件上传实例:
- UploadServlet.java
package com.atjava.servlet;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.List;
/**
* @author lv
* @create 2021-08-26 21:45
*/
public class UploadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// super.doPost(req, resp);
// System.out.println("收到 upload");
// ServletInputStream inputStream = req.getInputStream();
// byte[] buffer = new byte[102400];
// int read = inputStream.read(buffer);
// System.out.println(new String(buffer, 0, read));
// req.setCharacterEncoding("UTF-8");
if (ServletFileUpload.isMultipartContent(req)) {
// 创建 FileItemFactory工厂实现类
FileItemFactory fif = new DiskFileItemFactory();
// 创建用于解析上传数据的工具类 ServletFileUpload
ServletFileUpload sfu = new ServletFileUpload(fif);
// 解析上传的数据,得到每一个表单项
try {
List<FileItem> list = sfu.parseRequest(req);
// 判断每个表单项的内容
for (FileItem fileItem : list) {
if (fileItem.isFormField()) {
// 普通表单项
System.out.println("上传表单项的 name:" + fileItem.getFieldName());
// 参数 "UTF-8" 解决乱码问题
System.out.println("上传表单项的 value:" + fileItem.getString("UTF-8"));
} else {
// 上传文件
System.out.println("上传表单项的 name:" + fileItem.getFieldName());
// System.out.println("上传文件的 name:" + fileItem.getName());
fileItem.write(new File("F:\Java\java_web\91909529\216\" + fileItem.getName()));
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("done");
}
}
}
}
- upload.jsp
<%--
Created by IntelliJ IDEA.
User: Weili
Date: 2021/8/26
Time: 21:29
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Upload</title>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="username" /><br />
头 像:<input type="file" name="photo"><br />
<input type="submit" value="上传" />
</form>
</body>
</html>
- web.xml
<servlet>
<servlet-name>UploadServlet</servlet-name>
<servlet-class>com.atjava.servlet.UploadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UploadServlet</servlet-name>
<url-pattern>/upload</url-pattern>
</servlet-mapping>
文件的下载介绍
文件下载的操作过程
- 客户端发送请求,通知服务器要下载的文件
- 服务器获取要下载的文件名
- 读取要下载的文件内容
- 将下载的文件内容回传给客户端
- 在回传前,通过
响应头通知客户端返回的数据类型 - 在回传前,要通知客户端收到的数据是用于下载使用(使用响应头)
下载的常用 API说明
response.getOutputStream(); // 获取响应的输出流
servletContext.getResourceAsStream(); // 以流的形式获取文件内容
servletContext.getMimeType(); // 获取要返回的文件类型
response.setContentType(); // 设置返回客户端的数据类型
实例:
- DownloadServlet.java
package com.atjava.servlet;
import org.apache.commons.io.IOUtils;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
/**
* @author lv
* @create 2021-08-31 21:56
*/
public class DownloadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// super.doGet(req, resp);
// 1.获取要下载的文件名
String downLoadFileName = "2.jpg";
// 2.读取要下载的文件内容(通过 ServletContent对象读取)
ServletContext servletContext = getServletContext();
/**
* "/"斜杠被服务器解析表示地址为 http://ip:port/工程名/ 映射到代码的 web目录
*/
InputStream resourceAsStream = servletContext.getResourceAsStream("/file/" + downLoadFileName);
// 4.在回传前,通过响应头告诉客户端返回的数据类型
// 获取要返回的文件类型:image/jpeg = 大类型/小类型
String mimeType = servletContext.getMimeType("/file/" + downLoadFileName);
resp.setContentType(mimeType);
// 5.同时要告诉客户端收到的数据是用于下载的(设置响应头)
// attachment 附件:表示下载使用
// Content-Disposition 响应头属性:表示客户端收到的内容如何处理
// filename 文件名:表示要下载的文件名,可以与源文件名不同,自定义下载的文件名
// resp.setHeader("Content-Disposition", "attachment; filename=22.jpg");
// URLEncoder.encode("中文.xxx", "UTF-8"):是将内容转换为 %xx%xx的格式;(URLEncoder编码:chrome、IE适用)
/**
* 文件名中出现中文,会出现乱码问题,http没有考虑中文情况,默认不支持中文
* 解决:需要对中文进行编码,才能在网络中传输,
* 对文件名进行 url编码:URLEncoder.encode("中文.xxx", "UTF-8");
*/
resp.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(downLoadFileName, "UTF-8"));
/**
* fireFox适用的编码:BASE64Encoder
*/
// String fileName = "中文乱码";
// BASE64Encoder base64Encoder = new BASE64Encoder();
// String encodeStr = base64Encoder.encode(fileName.getBytes("UTF-8"));
// System.out.println(encodeStr);
// 3.将下载的文件内容回传给客户端
// 获取响应的输出流
OutputStream outputStream = resp.getOutputStream();
// copy(): 读取 resourceAsStream中的数据,复制给 outputStream流,输出给客户端
IOUtils.copy(resourceAsStream, outputStream);
}
}
- web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<context-param>
<param-name>initParam</param-name>
<param-value>Map</param-value>
</context-param>
<context-param>
<param-name>username</param-name>
<param-value>root</param-value>
</context-param>
<servlet>
<servlet-name>UploadServlet</servlet-name>
<servlet-class>com.atjava.servlet.UploadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UploadServlet</servlet-name>
<url-pattern>/upload</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>DownloadServlet</servlet-name>
<servlet-class>com.atjava.servlet.DownloadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DownloadServlet</servlet-name>
<url-pattern>/download</url-pattern>
</servlet-mapping>
</web-app>
中文乱码问题:
URLEncoder编码,适用于 Chrome、IE
// 5.同时要告诉客户端收到的数据是用于下载的(设置响应头)
// attachment 附件:表示下载使用
// Content-Disposition 响应头属性:表示客户端收到的内容如何处理
// filename 文件名:表示要下载的文件名,可以与源文件名不同,自定义下载的文件名
// resp.setHeader("Content-Disposition", "attachment; filename=22.jpg");
// URLEncoder.encode("中文.xxx", "UTF-8"):是将内容转换为 %xx%xx的格式;(URLEncoder编码:chrome、IE适用)
/**
* 文件名中出现中文,会出现乱码问题,http没有考虑中文情况,默认不支持中文
* 解决:需要对中文进行编码,才能在网络中传输,
* 对文件名进行 url编码:URLEncoder.encode("中文.xxx", "UTF-8");
*/
resp.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(downLoadFileName, "UTF-8"));
Base64编码、解码,适用于 fireFox
package com.atjava.base64Test;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
/**
* @author lv
* @create 2021-08-31 23:00
*/
public class Base64Test {
public static void main(String[] args) {
/**
* fireFox适用的编码操作:BASE64Encoder
* 解码操作:
*/
String fileName = "中文乱码";
// 创建 Base64编码器
BASE64Encoder base64Encoder = new BASE64Encoder();
String encodeStr = null;
try {
// 执行编码操作:
encodeStr = base64Encoder.encode(fileName.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
System.out.println(encodeStr); // 5Lit5paH5Lmx56CB
// 创建 Base64解码器
BASE64Decoder base64Decoder = new BASE64Decoder();
byte[] decodeBuffer = null;
try {
// 解码操作:
decodeBuffer = base64Decoder.decodeBuffer(encodeStr);
fileName = new String(decodeBuffer, "UTF-8");
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(fileName); // 中文乱码
}
}
BASE64 编解码,解决 火狐浏览器中附件中文名问题
如果客户端浏览器是火狐浏览器,那我们需要对中文名进行 BASE64的编码操作
- Content-Disposition: attachment; filename=
=?charset?B?xxxx?=
- =? 表示编码内容的开始
- charset 表示字符集
- B 表示 BASE64编码
- xxxx 表示 BASE64编码后的中文内容
- ?= 表示编码内容的结束
resp.setHeader("Content-Disposition", "attachment; filename==?UTF-8?B?" + new BASE64Encoder().encode("中文乱码.jpg".getBytes("UTF-8")) + "?=");