JSP
主要内容
JSP
JSP的基础语法
JSP的引入和原理
JSP:Java Server Page SUN 公司提供的动态网页编程技术,是 Java Web 服务器端的动态资源。
它相比 html 而言,html是静态页面,只能为用户提供静态数据,而 Jsp 技术允许在页面中嵌套 java 代码,为用户提供动态数据(比如可以从服务器中获取数据库中数据到jsp中去)。我们不可能所有数据都写死在页面上,所以在这里要学习jsp动态网页技术。
相对servlet而言,servlet也确实可以向页面输出html标签,比如:
但是如果想要自行用代码对数据进行排版,太麻烦了啊!此时就需要jsp,jsp 除了可以用 java 代码产生动态数据的同时,也很容易对数据进行排版,因为在jsp中可以编写html代码的。
所以:
(1)不管是 JSP 还是 Servlet,虽然都可以用于开发动态 web 资源,但由于这 2 门技术各自的特点,在长期的软件实践中,人们逐渐把 servlet 作为 web 应用中的控制器组件来使用(后台代码), 而把 JSP 技术作为数据显示模板来使用(页面模板)。jsp中画页面就好,偶尔写点java代码是可以的,但是成段的java代码,我们还是放在servlet中比较好
(2)同时实现了后台前台代码的解耦。如果在servlet中又写java代码又写页面代码,排版麻烦,调试不方便。如果在jsp中写java代码,页面耦合度也高,调试不方便。
注意:
其实 Jsp 本质就是一个 Servlet,当我们第一次访问 Jsp 的时候,Jsp 引擎都会将这个 Jsp 翻译 成一个 Servlet。
转换/翻译(translation):Tomcat中的JSP引擎将JSP网页转换成Servlet,得到一个.java文件
编译(compile):通过javac命令将.java文件编译成 .class文件
运行:运行.class字节码文件,处理请求
具体运行过程:
- 浏览器发起请求访问jsp
- Tomcat服务器接收到请求后调用对应的Servlet处理请求,调用JspServlet (即JSP引擎,在Tomcat conf/web.xml文件中进行配置了)
- JspServlet将请求的jsp文件转换成对应的Java文件并完成编译
- 将编译后的class文件加载到内存并执行,其实就是运行一个Servlet
- 最后得到结果数据响应浏览器
当再次执行JSP页面时,JSP 引擎会做检查工作,如果JSP页面中没有修改,就直接执行之前.class文件。如果发现JSP页面中有更新修改时,JSP引擎会再次转换JSP为Servlet。
jsp翻译为java文件的存放位置为:C:\Users\zss\AppData\Local\JetBrains\IntelliJIdea2022.3\tomcat\6d33ebb6-fc6a-49c3-8160-5e4b80433c5a\work\Catalina\localhost\s4\org\apache\jsp中,index.jsp转换后的java文件是index_jsp.java,转换后的代码如下:
/*
* Generated by the Jasper component of Apache Tomcat
* Version: Apache Tomcat/9.0.70
* Generated at: 2023-02-06 08:11:44 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.*;
public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports {
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;
private static final java.util.Set<java.lang.String> _jspx_imports_packages;
private static final java.util.Set<java.lang.String> _jspx_imports_classes;
static {
_jspx_imports_packages = new java.util.HashSet<>();
_jspx_imports_packages.add("javax.servlet");
_jspx_imports_packages.add("javax.servlet.http");
_jspx_imports_packages.add("javax.servlet.jsp");
_jspx_imports_classes = null;
}
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 java.util.Set<java.lang.String> getPackageImports() {
return _jspx_imports_packages;
}
public java.util.Set<java.lang.String> getClassImports() {
return _jspx_imports_classes;
}
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 {
if (!javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {
final java.lang.String _jspx_method = request.getMethod();
if ("OPTIONS".equals(_jspx_method)) {
response.setHeader("Allow","GET, HEAD, POST, OPTIONS");
return;
}
if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method)) {
response.setHeader("Allow","GET, HEAD, POST, OPTIONS");
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSP 只允许 GET、POST 或 HEAD。Jasper 还允许 OPTIONS");
return;
}
}
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("\n");
out.write("\n");
out.write("<html>\n");
out.write(" <head>\n");
out.write(" <title>$Title$</title>\n");
out.write(" </head>\n");
out.write(" <body>\n");
out.write(" $END$\n");
out.write(" </body>\n");
out.write("</html>\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);
}
}
}
通过查看代码发现,index_jsp extends org.apache.jasper.runtime.HttpJspBase,通过反编译工具可以看到:(可以把jasper.jar直接拖拽到反编译工具看,或者把jasper.jar拿出来桌面上,变成rar结尾,然后就可以打开,找到HttpJspBase文件,再拖拽到反编译工具查看)
然后看一眼这个标准Servlet中的service方法:
_jspService(request, response)方法重写的位置就是翻译的java文件中的 _jspService方法。
从 _jspService方法中看到,jsp页面上所有的HTML相关代码全部被转化成了字符串,通过流的形式响应给了浏览器。如果jsp中有代码快,那么<%%>中的代码也在该方法中穿插执行。
准备工作
配置IDEA
这一步不是必须的,当然由于 编辑器中有些默认的配置项我们觉得不是很完美,比如"编码格式"、页面模板等。我们可以在新建 JSP 页面之前就先修改为我们需要的。
1.选择"File" —> “Settings…”
2.设置编码格式。搜索"encode",选择"File Encoding"
3.设置页面模板。搜索"template",选择"File and Code Templates",选择右侧的"Other",选择下方的"Jsp File"
新建JSP页面
JSP中包含内容
JSP中包含五部分内容:
- HTML代码
- 注释
- Scriptlet
- 动作标签
- 指令标签
注释
JSP中可以写HTML、Java、CSS、JS代码,那么也就可以写这些语言的注释,并且JSP也有自己的注释方式。
JSP的注释格式是:<%–注释内容–%>
不同的注释是有差异的,通过下面案例对比一下:
<%--
Created by IntelliJ IDEA.
User: zhaoss-msb
Date: 2023/2/6
Time: 20:00
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<style>
/*css的注释*/
<%--JSP自身的注释--%>
</style>
<script>
//js单行注释
/*js多行注释*/
<%--JSP自身的注释--%>
</script>
</head>
<body>
<!-- HTML的注释 -->
<%--JSP自身的注释--%>
<input type="text">
<%--JSP自身的注释--%>
</body>
</html>
通过查看转换后的文件和查看浏览器中的页面源码发现:
注释类型 | 进入转译文件 | 响应给浏览器 |
JSP注释 | no | no |
JAVA注释 | yes | no |
HTML,css,js注释 | yes | yes |
所以我们推荐在jsp中使用jsp自己的注释:<%–注释内容–%>
转译后的文件中注释:
页面查看源代码中注释:
Scriptlet
在 JSP 中最重要的部分就是 Scriptlet(脚本小程序),所有嵌入在 HTML 代码中的 Java 程序。
在 JSP 中一共有三种 Scriptlet 代码:都必须使用 Scriptlet 标记出来
第一种:<% %>: java 脚本段,可以定义局部变量、编写语句
第二种:<%! %>:声明,可以定义全局(成员)变量、方法、类
第三种:<%= %>:表达式,数据一个变量或具体内容
通过观察解析为 java 文件的 jsp 代码理解三种小脚本
<%--
Created by IntelliJ IDEA.
User: zhaoss-msb
Date: 2023/2/6
Time: 20:39
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
//java脚本段,这里可以编写java代码,这是单行注释
/* java的多行注释:a以后在转译后的文件中会变成一个局部变量 */
int a = 10;
%>
<%!
// 声明:以后在转译后的文件中b会变成一个成员变量,servlet是单例的,所以成员变量很少使用。
int b = 20;
public void eat(){
System.out.println("eat....");
}
%>
this is a jsp..局部变量<%=a %> 成员变量<%=b %>
</body>
</html>
查看转译后的java文件:(看a、b变量的位置,java代码的注释)
浏览器右键页面源代码看不到上面的内容和注释:
JSP的动作标签
JSP动作指令主要是一组动态执行的指令,以标记的形式使用。动作指令是运行时候的动作脚本,JSP的动作指令分别如下:(过时了,我们讲解一个即可)
jsp:forward 执行页面转向,将请求的处理转发到下一个页面。(后面用到讲解)
jsp:param 用于传递参数,必须与其它支持参乎的标记一起使用。
jsp:include 用于动态引入一个JSP页面。
jsp:plugin 用于下载JavaBean或Applet到客户端执行。
jsp:useBean 使用JavaBean。
jsp:setProperty 设置JavaBean实例的属性值。
jsp:getProperty 获取JavaBean实例的属性值。
…
**案例:**网站的头部和底部效果无需反复编写,使用动作标签进行操作。(用动作标签可以实现京东的头部效果、底部效果,但是京东不一定是按照动作标签来实现的。)
将图片放入imgs文件夹下:
myjd.jsp:
<%--
Created by IntelliJ IDEA.
User: zhaoss-msb
Date: 2023/2/6
Time: 21:36
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<jsp:include page="header.jsp"></jsp:include>
<div style="height: 800px">
京东页面实际代码逻辑。。。。
</div>
<jsp:include page="foot.jsp"></jsp:include>
</body>
</html>
header.jsp:
<%--
Created by IntelliJ IDEA.
User: zhaoss-msb
Date: 2023/2/6
Time: 21:36
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<img src="imgs/header.png">
</body>
</html>
foot.jsp:
<%--
Created by IntelliJ IDEA.
User: zhaoss-msb
Date: 2023/2/6
Time: 21:36
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<img src="imgs/foot.png">
</body>
</html>
页面效果:
JSP的指令标签
指令标签是JSP页面上的一种特殊标签,JSP指令可以用来设置整个JSP页面相关的属性,如网页的编码方式、网页使用的脚本语言、导包等。
指令标签的语法:
<%@ directive attribute="value" %>
JSP中的三种指令标签:
指令 | 描述 |
<%@ page %> | 定义网页依赖属性,如脚本语言、error页面、缓存需求等等 |
<%@ include %> | 包含其他文件 |
<%@ taglib %> | 引入标签库的定义 |
page指令标签
属性 | 描述 |
buffer | 指定out对象使用缓冲区的大小 |
autoFlush | 控制out对象的 缓存区 |
contentType | 指定当前JSP页面的MIME类型和字符编码 |
errorPage | 指定当JSP页面发生异常时需要转向的错误处理页面 |
isErrorPage | 指定当前页面是否可以作为另一个JSP页面的错误处理页 |
extends | 指定servlet从哪一个类继承 |
import | 导入要使用的Java类 |
info | 定义JSP页面的描述信息 |
isThreadSafe | 指定对JSP页面的访问是否为线程安全 |
language | 定义JSP页面所用的脚本语言,默认是Java |
session | 指定JSP页面是否使用session,默认是使用 |
isELIgnored | 指定是否执行EL表达式 |
isScriptingEnabled | 确定脚本元素能否被使用 |
代码演示:
testjsp04.jsp:
<%--
Created by IntelliJ IDEA.
User: zhaoss-msb
Date: 2023/2/6
Time: 22:25
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java"
pageEncoding="utf-8" import="java.util.*"
errorPage="error.jsp" %>
<%--
contentType:给浏览器看的,浏览器解析jsp的编码格式:utf-8
language:设置jsp中脚本语言:java
pageEncoding:当前jsp页面的编码格式
import:导包
errorPage:指定错误页面,当页面发生错误的时候,显示错误页面的信息
--%>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
List list = new ArrayList();
int a = 1/0;
%>
</body>
</html>
错误页面error.jsp:
<%--
Created by IntelliJ IDEA.
User: zhaoss-msb
Date: 2023/2/6
Time: 22:30
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
现在服务器繁忙,请联系管理员!
</body>
</html>
include指令标签
- JSP可以通过include指令来包含其他文件。被包含的文件可以是jsp文件、HTML文件或文本文件。
- 包含的文件就像是jsp文件的一部分,会被同时编译执行。
- 除了使用include指令标签可以实现引入外,使用jsp:include动作也可以实现引入。
**案例:**依旧是网站的头部和底部效果无需反复编写,使用指令标签进行操作。(用动作标签可以实现京东的头部效果、底部效果,但是京东不一定是按照动作标签来实现的。)
利用3.3节案例中使用的header.jsp和foot.jsp,然后编写myjd2.jsp,利用include指令标签完成其它页面的引入:
<%--
Created by IntelliJ IDEA.
User: zhaoss-msb
Date: 2023/2/7
Time: 13:01
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%@include file="header.jsp"%>
<div style="height: 800px"></div>
<%@include file="foot.jsp"%>
</body>
</html>
访问myjd2.jsp,查看转译后的java文件:
那么与之前的动作标签jsp:include的区别是什么呢?
之前的动作标签案例,访问myjd.jsp后,查看转译文件:
taglib指令标签
taglib就是标签库的意思,我们可以在jsp页面中引入其他的标签库,帮我们去快速简单的做一些事情,后面JSTL标签库会详细介绍。
taglib指令的语法:
<%@ taglib uri="uri" prefix="prefixOfTag" %>
JSP的内置对象
九大内置对象
什么叫内置对象:
因为JSP的本质是Servlet,在JSP文件经过转译之后,生成JAVA代码,在运行时,JSP给我们准备好了九个可以直接使用而不用我们自己去new的对象,这九个对象我们称之为内置对象.
内置对象完全由JSP自行去维护,我们直接使用即可。
九大内置对象:(与之前学习的servlet中的对象,没有区别,就是一个东西。)
可以随便找一个转译java文件,查看_jspService方法,即可看到内置对象:
因为这里名字已经起好了,所以你不能用其它名字,比如request,你不可以用req了。
这些对象相对来说常用一些,所以在jsp中使用的时候不用自己创建了,直接使用就行。
上面只有八个啊?还少一个,少什么了?exception对象。怎么才能有呢:
只有在jsp页面中加入 isErrorPage="true"以后,生成的转译文件中才会有exception对象,才可以使用exception对象。
比如在error.jsp中加入isErrorPage=“true”:
<%--
Created by IntelliJ IDEA.
User: zhaoss-msb
Date: 2023/2/6
Time: 22:30
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" %>
<html>
<head>
<title>Title</title>
</head>
<body>
现在服务器繁忙,请联系管理员!
<%
String message = exception.getMessage();
%>
当前页面的错误是:<%=message%>
</body>
</html>
然后在testjsp05.jsp中加入错误页面:
<%--
Created by IntelliJ IDEA.
User: zhaoss-msb
Date: 2023/2/7
Time: 14:23
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" errorPage="error.jsp" %>
<html>
<head>
<title>Title</title>
</head>
<body>
hello jsp05...
<%
int a = 1/0;
%>
</body>
</html>
然后启动服务器,访问http://localhost:8888/jsp01/testjsp05.jsp
这样就可以显示:
对应error.jsp转译后的文件中可以看到exception对象:
另一种划分方式:
四个域对象
pageContext page域 当前页面内可用
request reqeust域 一次请求
session session域 一次会话
application application域 整个项目运行
响应对象
response 响应对象
输出流对象
out 打印流对象
其他三个对象
servletConfig 由于JSP本身也是一个Servlet,所以容器也会给我们准备一个ServletConfig
page 就是JSP转换的Servlet的对象,也就是当前JSP对象本身
exception 异常对象,在错误提示页上使用,当isErrorpage=true 才具有该对象
四个域对象对应案例说明
在JSP中提供了四种属性的保存范围,所谓的属性保存范围,指的就是一个设置的对象,可以再多少个页面中保存并可以继续使用
- page范围
pageContext : 只在一个页面中保存属性,跳转之后无效 - request范围
request : 只在一次请求中保存,服务器跳转后依然有效 - session范围
session : 在一次会话范围中,无论何种跳转都可以使用 - application范围
application : 在整个服务器上保存
方法 | 类型 | 描述 |
public void setAttribute(String name, Object o) | 普通 | 设置属性的名称及内容 |
public Object getAttribute(String name) | 普通 | 根据属性名称取属性 |
public void removeAttribute(String name) | 普通 | 删除指定的属性 |
**测试pageContext案例:**只在一个页面中保存属性,取出属性:
<%--
Created by IntelliJ IDEA.
User: zhaoss-msb
Date: 2023/2/7
Time: 14:48
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
pageContext.setAttribute("page","hello");
%>
<%=pageContext.getAttribute("page")%>
</body>
</html>
别忘了加入jsp的包:
**测试request案例:**访问servlet,从servlet请求转发到jsp:
Servlet01.java:
package com.msb.testservlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Author: zhaoss
*/
@WebServlet("/servlet01")
public class Servlet01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 通过request作用域存储数据:
req.setAttribute("reqmsg","hirequest");
// 请求转发:
req.getRequestDispatcher("testreq.jsp").forward(req,resp);
}
}
testreq.jsp:
<%--
Created by IntelliJ IDEA.
User: zhaoss-msb
Date: 2023/2/7
Time: 14:54
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%=request.getAttribute("reqmsg")%>
</body>
</html>
**测试session案例:**只要在同一个会话中,访问都可以取出数据。
Servlet02.java:
package com.msb.testservlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Author: zhaoss
*/
@WebServlet("/servlet02")
public class Servlet02 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 利用session作用域来存储数据:
req.getSession().setAttribute("sessionmsg","testsession");
}
}
testsession.jsp:
<%--
Created by IntelliJ IDEA.
User: zhaoss-msb
Date: 2023/2/7
Time: 15:00
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%=session.getAttribute("sessionmsg")%>
</body>
</html>
**测试application案例:**使用不同浏览器创建不同会话测试,数据依然可以取出
Servlet03.java:
package com.msb.testservlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Author: zhaoss
*/
@WebServlet("/servlet03")
public class Servlet03 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 将数据存储在application作用域中:
req.getServletContext().setAttribute("appmsg","testapp");
}
}
testapplication.jsp:
<%--
Created by IntelliJ IDEA.
User: zhaoss-msb
Date: 2023/2/7
Time: 15:03
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%=application.getAttribute("appmsg")%>
</body>
</html>
EL表达式的使用
问题
目前,我们会使用Servlet和jsp技术了。Servlet负责请求的处理;jsp负责请求处理结果的展示。
往往我们是使用Servlet处理完请求,将请求结果数据放在request域中,然后转发到jsp页面,在jsp页面通过<%%>也就是java脚本的方式取出request域中的数据,然后进行展示。
当然了,我们也可以将Servlet处理请求后的结果放在session域中,然后将请求重定向到jsp页面,在jsp页面通过<%%>j也就是java脚本的方式取出session域中的数据,然后进行展示。
虽然Servlet结合jsp的方式已经极大的简化了我们开发功能和页面的操作。但在jsp页面中取域中的数据要使用<%%>java脚本和HTML代码进行拼接的方式展示数据,还是比较麻烦,而且不利于阅读。怎么办?
解决
可以在jsp页面中使用EL表达式来获取域对象中的数据,简化了在jsp页面中使用<%%>java脚本获取域对象数据的方式
EL表达式的语法
语法结构非常简单:
${expression}
域对象的概念在 JSP 中一共有四个:pageContext, request, session, application;范围依次是,本页面,一次请求, 一次会话,整个应用程序。
当需要指定从某个特定的域对象中查找数据时可以使用四个域对象对应的空间对象,分别是:pageScope, requestScope, sessionScope, applicationScope。
使用EL表达式获取域数据的时候,如果没有指明作用域,那会按照作用域由小到大的顺序去找,直到找到为止:
pageContext —> request —> session —> application
当域对象全找完了还未找到则返回空字符串""。
EL表达式的使用
获取数据-基本语法-数据为字符串
设置域对象中的数据
<%
pageContext.setAttribute("uname","zhangsan"); // page作用域
request.setAttribute("uname","lisi"); // request作用域
session.setAttribute("uname","wangwu"); // session作用域
application.setAttribute("uname","zaholiu"); // application
%>
获取域对象的值
<%-- 获取域对象中的数据:默认查找方式为从小到大,找到即止。若四个范围都未找到,则返回空字符串。--%>
${uname} <!-- 输出结果为:zhangsan -->
获取指定域对象的值
${pageScope.uname} <!-- page作用域 -->
${requestScope.uname} <!-- request作用域 -->
${sessionScope.uname} <!-- session作用域 -->
${applicationScope.uname} <!-- application作用域 -->
对应练习代码:
<%--
Created by IntelliJ IDEA.
User: zhaoss-msb
Date: 2023/2/7
Time: 16:22
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
pageContext.setAttribute("uname","lulu");
request.setAttribute("uname","lili");
session.setAttribute("uname","feifei");
application.setAttribute("uname","mingming");
%>
<%--
下面的用小脚本取出数据就是之前的写法
如果数据没有取出,显示的效果为:null
--%>
<%=pageContext.getAttribute("uname3")%>
<%--
下面是el表达式的写法,明显感受到代码简化
--%>
${uname}
<%--如果数据没有取出,那么展示的就是空白的效果,实际取出的就是空字符串--%>
${uname3}
<br> <%--加入换行标签,发现html标签和el表达式可以一起使用,很方便--%>
<%--
使用EL表达式获取域数据的时候,如果没有指明作用域,那会按照作用域由小到大的顺序去找,直到找到为止:
pageContext ---> request ---> session ---> application
--%>
<%--如果我就想要在指定的域中取出数据怎么办呢?
那么就需要从域对象对应的空间对象中取出数据--%>
${sessionScope.uname}
</body>
</html>
获取数据-数据为各种对象
获取List
定义一个jsp:
<%
List<String> list = new ArrayList<String>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
request.setAttribute("list", list);
%>
<%--
获取List中指定下标的数据
${list[下标] }
获取集合的长度
${list.size()}
注:
list代表的是存在域对象中的变量名(限域变量名)
--%>
${list[1] }
获取Map
定义一个jsp:
<%
Map map = new HashMap();
map.put("aaa", "111");
map.put("bbb", 2222);
map.put("ccc-a", 333);
request.setAttribute("map", map);
%>
<%--
获取Map中指定值
${map["key"] } 或 ${map.key }
注:
map代表的是存在域对象中的变量名(限域变量名)
--%>
${map.aaa }
${map["bbb"]}
获取JavaBean对象
User.java
public class User {
private Integer userId;
private String uname;
private String upwd;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public String getUpwd() {
return upwd;
}
public void setUpwd(String upwd) {
this.upwd = upwd;
}
}
定义一个jsp:
<%
User user = new User();
user.setUserId(1);
user.setUname("zhangsan");
user.setUpwd("123456");
request.setAttribute("user",user);
%>
<%-- JavBean中的属性字段需要提供get方法 --%>
${user} <%-- 获取对象 --%>
${user.uname} <%--获取对象中的属性--%>
练习代码:
<%@ page import="java.util.List" %>
<%@ page import="java.util.ArrayList" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.util.Map" %>
<%@ page import="com.msb.pojo.User" %><%--
Created by IntelliJ IDEA.
User: zhaoss-msb
Date: 2023/2/7
Time: 17:13
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
// 创建List集合:
List<String> list = new ArrayList<String>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
// 将list对象存入request作用域中:
request.setAttribute("list",list);
// 创建Map:
Map map = new HashMap();
map.put("a","111");
map.put("b","222");
map.put("c","333");
// 将map对象存入request作用域中:
request.setAttribute("map",map);
// 创建User的对象:
User user = new User(1,"xiaoming","123456");
// 将user对象存入request作用域中:
request.setAttribute("user",user);
%>
<%--利用el表达式获取List中数据--%>
获取List对象的长度:${list.size()}
获取List对象指定索引位置的数据:${list[1]}
<%--利用el表达式获取Map中数据--%>
获取Map对象中的内容,通过键取出对应的值:${map.b} ${map["b"]}
<%--利用el表达式获取User中数据--%>
获取User对象中的内容:${user} ---- ${user.pwd}
</body>
</html>
EL中的运算符
算术运算
<%--
加法: + 只有运算操作,没有字符串拼接操作
减法: -
乘法: *
除法: / 或 div
--%>
${1 + 1 }
${1 + "1" }
${"1" + "1" }
${"1aaa" + "1" }
${1 / 1 } 或 ${1 div 1 }
等值判断
<%--
比较两个值是否相等,返回true或false
== 或 eq
--%>
${"a" == "b" }
${"a" eq "d" }
关系运算符、逻辑运算符
<%--
大于:>
小于:<
大于等于:>=
小于等于:<=
--%>
${a > b}
${a + 1 > 10 }
${a + b >= 10 }
${a > b && b > 5 }
${a + b > 10 || a - b > 5 }
empty运算符
<%--
empty
判断域对象是否为空。为空,返回true;不为空返回false;注意:字符串对象、集合对象、普通对象的empty测试
${empty 限域变量名 }
判断对象是否不为空。
${!empty 限域变量名 }
--%>
${empty uname}
${empty list}
${empty map}
${empty user}
练习代码:
<%@ page import="java.util.List" %>
<%@ page import="java.util.ArrayList" %>
<%@ page import="com.msb.pojo.User" %><%--
Created by IntelliJ IDEA.
User: zhaoss-msb
Date: 2023/2/7
Time: 21:03
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${1 + 1}
${1 + "1"}
${"1" + "1"}
<%--${"1aaa" + "1"} 直接报错--%>
${1 == 1}
${1 eq 2}
<%
// 存储字符串
request.setAttribute("a","str");
request.setAttribute("b","");
request.setAttribute("c",null);
// 存储集合:
List list1 = new ArrayList();
List list2 = new ArrayList();
list2.add("aaa");
List list3 = null;
request.setAttribute("list1",list1);
request.setAttribute("list2",list2);
request.setAttribute("list3",list3);
User user1 = new User();
User user2 = new User(1,"lili","123123");
User user3 = null;
request.setAttribute("user1",user1);
request.setAttribute("user2",user2);
request.setAttribute("user3",user3);
%>
<%--
如果域对象中的数据是字符串:
存在字符串对象:false
空字符串:true
null:true
不存在的对象:true
--%>
<br>
${empty a}
${empty b}
${empty c}
${empty d}
<br>
<%--
对于集合对象来说,如果集合里面的元素为0:true
对于集合对象来说,如果集合里面的元素>0:false
集合是null:true
--%>
${empty list1}
${empty list2}
${empty list3}
<%--对于普通对象来说,只要创建了对象就不是空--%>
<br>
${empty user1}
${empty user2}
${empty user3}
</body>
</html>
JSTL
问题
通过之前的学习,我们知道使用Servlet进行请求处理,使用JSP将请求处理的结果展示,在jsp页面使用EL表达式获取并输出域对象中数据。但是如果要循环输出一个list集合的数据,还得在jsp页面使用<%%>java小脚本的形式写java代码来完成!比较麻烦!
解决
可以使用JSTL标签来完成一些例如循环、判断等情况的与数据输出。
JSTL的作用就是:替换在Jsp中声明的Java逻辑代码。(el表达式只是取出域对象中数据,而jstl是对数据进行各种处理,所以jstl是辅助el的使用。)
介绍
JSTL标签库是第三方发明的自定义的标签,每个标签都有自己特定的含义表示一个具体的java逻辑。我们要在JSP中使用JSTL必须使用taglib标签引入第三方的标签库资源。
标签库的内容有:
1. **核心标签库**
2. SQL标签库
3. 函数标签库
4. **格式化标签库**
5. XML标签库
我们现在只讨论 JSTL 中最重要的标签,迭代集合以及格式化数字和日期几个标签。
核心标签库:
http://java.sun.com/jsp/jstl/core
包含 Web 应用的常见工作,比如:循环、表达式赋值、基本输入输出等。
格式化标签库:
http://java.sun.com/jsp/jstl/fmt
用来格式化显示数据的工作,比如:对不同区域的日期格式化等。
JSTL的使用
使用步骤:
- 在项目中导入JSTL的jar包
- 在jsp页面中通过taglib指令引入jstl标签库
- 使用jstl标签完成开发
为了在 JSP 页面使用 JSTL 类库,必须以下列格式使用 taglib 指令标签:
<%@taglib uri="" prefix="" %>
uri:你需要使用的jstl的库的地址 ,你想用哪个库你就引入哪个库。
prefix:用来自定义标签的前缀,有点像之前学习的表的别名,通过别名可以操作表中字段,这里通过前缀去操作库中的标签。
例如:
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
前缀可以是任意内容,遵循规范可以使团队中由不同人员编写的代码更加相似;所以,建议使用事先设计好的前缀。比如使用核心库,前缀一般使用c,如果使用格式化库,前缀一般是fmt。
对应jar包
从Apache的标准标签库中下载的二进包(jakarta-taglibs-standard-current.zip)。
官方下载地址:http://archive.apache.org/dist/jakarta/taglibs/standard/binaries/
下载 jakarta-taglibs-standard-1.1.2.zip 包并解压,将 jakarta-taglibs-standard-1.1.2/lib/ 下的两个 jar 文件:standard.jar 和 jstl.jar 文件拷贝到项目的指定目录下。
条件动作标签
条件动作指令用于处理页面的输出结果依赖于某些输入值的情况,在 Java 中是利用 if、 if…else 和 switch 语句来进行处理的。在 JSTL 中也有 4 个标签可以执行条件式动作指令:if、 choose、when 和 otherwise。
if 标签
if 标签先对某个条件进行测试,如果该条件运算结果为 true, 则处理它的主体内容,测试结果保存在一个 Boolean 对象中,并创建一个限域变量来引用 Boolean 对象。可以利用 var 属性设置限域变量名,利用 scope 属性来指定其作用范围。
语法格式
<c:if test="<boolean>" var="<string>" scope="<string>">
...
</c:if>
属性
if 标签有如下属性:
属性 | 描述 | 是否必要 | 默认值 |
test | 条件 | 是 | 无 |
var | 用于存储条件结果的变量(限域变量名) | 否 | 无 |
scope | var属性的作用域 可取值:page|request|session|application | 否 | page |
示例
<%--
Created by IntelliJ IDEA.
User: zhaoss-msb
Date: 2023/2/7
Time: 23:16
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
// 在request作用域中存储数据:
// 存储int类型数据,学生的成绩:
request.setAttribute("score",95);
%>
<%--如果成绩在60分以上,就是及格:--%>
<c:if test="${score >= 60}">
成绩及格!!
</c:if>
<c:if test="${score < 60}">
成绩不及格!!
</c:if>
<%--练习var scope属性:
var:接收test表达式的结果
scope:代表var变量的作用域
--%>
<c:if test="${score >= 60}" var="flag" scope="request">
<%--服务器的跳转:类似之前学习的请求转发--%>
<jsp:forward page="index.jsp"></jsp:forward>
成绩及格!!${flag}
</c:if>
</body>
</html>
注: JSTL中没有else标签,为了模拟 else 的情景,需要使用两个 if 标签,并且这两个标签为相反的条件。
choose、when 和 otherwise 标签
choose 和 when 标签的作用与 Java 中的 switch 和 case 关键字相似,用于在众多选项中做出选择。也就是说:他们为相互排斥的条件式执行提供相关内容。
switch语句中有case,而choose标签中对应有when,switch语句中有default,而choose标签中有otherwise。
语法格式
<c:choose>
<c:when test="<boolean>">
...
</c:when>
<c:when test="<boolean>">
...
</c:when>
...
...
<c:otherwise>
...
</c:otherwise>
</c:choose>
属性
- choose标签没有属性。
- when标签只有一个test属性。
- otherwise标签没有属性。
示例
<%--
Created by IntelliJ IDEA.
User: zhaoss-msb
Date: 2023/2/7
Time: 23:16
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
// 在request作用域中存储数据:
// 存储int类型数据,学生的成绩:
request.setAttribute("score",95);
%>
<c:choose>
<c:when test="${score >= 90}">
成绩属于A级别。
</c:when>
<c:when test="${score >= 80 && score < 90}">
成绩属于B级别。
</c:when>
<c:when test="${score >= 70 && score < 80}">
成绩属于C级别。
</c:when>
<c:when test="${score >= 60 && score < 70}">
成绩属于D级别。
</c:when>
<c:otherwise>
成绩属于E级别。
</c:otherwise>
</c:choose>
</body>
</html>
注意点
- choose标签和otherwise标签没有属性,而when标签必须设置test属性
- choose标签中必须有至少一个when标签,可以没有otherwise标签
- otherwise标签必须放在最后一个when标签之后
- choose标签中只能有when标签和otherwise标签,when标签和otherwise标签可以嵌套其他标签
- otherwise标签在所有的when标签不执行的情况下才会执行
注意:如果不想每次改动jsp都要重启服务器或者重新部署,可以进行配置:这个配置只对jsp生效,如果更改servlet还是需要重启的。
迭代标签
forEach 是将一个主体内容迭代多次,或者迭代一个对象集合。可以迭代的对象包括所有的 java.util.Collection 和 java.util.Map 接口的实现,以及对象或者基本类型的数组。他还可 以迭代 java.util.Iterator 和 java.util.Enumeration,但不能在多个动作指令中使用 Iterator 或者 Enumeration,因为 Iterator 或者 Enumeration 都不能重置(reset)。 各属性含义如下:
forEach标签
语法格式
<c:forEach
items="<object>"
begin="<int>"
end="<int>"
step="<int>"
var="<string>"
</c:forEach>
属性
属性 | 描述 | 是否必要 | 默认值 |
items | 要被循环的数据 | 否 | 无 |
begin | 开始的元素(0=第一个元素,1=第二个元素) | 否 | 0 |
end | 最后一个元素(0=第一个元素,1=第二个元素) | 否 | Last element |
step | 每一次迭代的步长 | 否 | 1 |
var | 代表当前条目的变量名称 | 否 | 无 |
示例
1.遍历主体内容多次
<c:forEach var="限域变量名" begin="开始数" end="结束数" step="迭代数" >
</c:forEach>
相当于java的for循环:
for(int i = 0; i < 10; i++) {
}
如下:
<%@ 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: zhaoss-msb
Date: 2023/2/8
Time: 14:33
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--输出0-10--%>
<c:forEach var="i" begin="0" end="10">
${i}
</c:forEach>
<hr>
<%--输出0,2,4,6,8,10--%>
<c:forEach var="i" begin="0" end="10" step="2">
${i}
</c:forEach>
</body>
</html>
2.循环
<c:forEach items="被循环的集合" var="限域变量名">
</c:forEach>
相当于java的foreach循环:
for(String str : list) {
}
如下:
<%@ 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: zhaoss-msb
Date: 2023/2/8
Time: 14:33
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
// 存储字符串
request.setAttribute("str","java,html,js,jsp");
request.setAttribute("str2","java#html#js#jsp");
String[] str3 = {"java","css","servlet"};
request.setAttribute("str3",str3);
List<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
request.setAttribute("list",list);
Map map = new HashMap<>();
map.put("a","1111");
map.put("b","2222");
map.put("c","3333");
request.setAttribute("map",map);
%>
遍历字符串(拼接的符号是逗号):
<c:forEach items="${str}" var="i">
${i}--->
</c:forEach>
<hr>
遍历字符串(拼接的符号是#):delims表示分隔符号:
<c:forTokens items="${str2}" delims="#" var="i">
${i}--->
</c:forTokens>
<hr>
对数组进行遍历:
<c:forEach items="${str3}" var="i">
${i}--->
</c:forEach>
<hr>
对List集合进行遍历:
<c:forEach items="${list}" var="i">
${i}--->
</c:forEach>
<hr>
对Map集合进行遍历:
<c:forEach items="${map}" var="i">
${i}--->${i.key}--->${i.value}
</c:forEach>
</body>
</html>
格式化动作标签
JSTL 提供了格式化和解析数字和日期的标签,我们讨论里面有:formatNumber、formatDate、parseNumber及parseDate。
formatNumber标签
formatNumber标签用于格式化数字,百分比,货币。该标签用指定的格式或精度来格式化数字。(将数值型数据转换成指定格式的字符串类型。)
语法格式
<fmt:formatNumber
value="<string>"
type="<string>"
var="<string>"
scope="<string>"/>
属性
属性 | 描述 | 是否必要 | 默认值 |
value | 要显示的数字 | 是 | 无 |
type | NUMBER,CURRENCY,或 PERCENT类型 | 否 | Number |
var | 存储格式化数字的变量 | 否 | Print to page |
scope | var属性的作用域 | 否 | page |
注意:
- 如果设置了var属性,则格式化后的结果不会输出,需要通过el表达式获取var对应的限域变量名
- 默认的类型(type)的取值为number。可取值:number数值型、percent百分比类型、currency货币型
示例
<%--
Created by IntelliJ IDEA.
User: zhaoss-msb
Date: 2023/2/8
Time: 15:18
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--
value:你要处理的数据
type:数据你要处理为哪种类型
var:数据用var里面指定的变量做接收
如果设置了var属性,则格式化后的结果不会输出,需要通过el表达式获取var对应的限域变量名
如果没有设置var属性,那么就可以自动数据最终结果
scope:变量作用域:4个作用域--%>
<fmt:formatNumber value="10" type="number" var="num"/>${num}
<fmt:formatNumber value="10" type="number"/>
<fmt:formatNumber value="10" type="percent"/>
<fmt:formatNumber value="10" type="currency"/>
<fmt:setLocale value="en_US"/><%--设置时区--%>
<fmt:formatNumber value="10" type="currency"/>
</body>
</html>
结果:
formatDate标签
formatDate标签用于使用不同的方式格式化日期。(将Date型数据转换成指定格式的字符串类型。)
语法格式
<fmt:formatDate
value="<string>"
type="<string>"
dateStyle="<string>"
timeStyle="<string>"
pattern="<string>"
timeZone="<string>"
var="<string>"
scope="<string>"/>
属性
属性 | 描述 | 是否必要 | 默认值 |
value | 要显示的日期 | 是 | 无 |
type | DATE, TIME, 或 BOTH | 否 | date |
dateStyle | FULL, LONG, MEDIUM, SHORT, 或 DEFAULT | 否 | default |
timeStyle | FULL, LONG, MEDIUM, SHORT, 或 DEFAULT | 否 | default |
pattern | 自定义格式模式 | 否 | 无 |
timeZone | 显示日期的时区 | 否 | 默认时区 |
var | 存储格式化日期的变量名 | 否 | 显示在页面 |
scope | 存储格式化日志变量的范围 | 否 | 页面 |
标签格式模式
代码 | 描述 | 实例 |
y | 不包含纪元的年份。如果不包含纪元的年份小于 10,则显示不具有前导零的年份。 | 2002 |
M | 月份数字。一位数的月份没有前导零。 | April & 04 |
d | 月中的某一天。一位数的日期没有前导零。 | 20 |
h | 12 小时制的小时。一位数的小时数没有前导零。 | 12 |
H | 24 小时制的小时。一位数的小时数没有前导零。 | 0 |
m | 分钟。一位数的分钟数没有前导零。 | 45 |
s | 秒。一位数的秒数没有前导零。 | 52 |
示例
<%@ page import="java.util.Date" %><%--
Created by IntelliJ IDEA.
User: zhaoss-msb
Date: 2023/2/8
Time: 15:33
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
// 在作用域中存储日期:
request.setAttribute("date",new Date());
%>
<%--将日期从作用域中取出:--%>
${date}
<hr>
<fmt:formatDate value="${date}" pattern="yyyy-MM-dd"/>
<fmt:formatDate value="${date}" pattern="yyyy年MM月dd日"/>
</body>
</html>
parseNumber标签
parseNumber标签用来解析数字,百分数,货币。(parseNumber 标签可以将数字、货币或百分比类型的字符串转换成数值型。
语法格式
<fmt:parseNumber
value="<string>"
type="<string>"
var="<string>"
scope="<string>"/>
属性
属性 | 描述 | 是否必要 | 默认值 |
value | 要解析的数字 | 否 | Body |
type | NUMBER,,CURRENCY,或 PERCENT | 否 | number |
var | 存储待解析数字的变量 | 否 | Print to page |
scope | var属性的作用域 | 否 | page |
示例
<fmt:parseNumber value="100" /> <br>
<fmt:parseNumber value="100" type="number" /> <br>
<fmt:parseNumber value="100%" type="percent" /> <br>
<fmt:parseNumber value="¥10.00" type="currency" /> <br>
parseDate标签
parseDate标签用于解析日期。(将指定格式的字符串转换成Date类型。)
语法格式
<fmt:parseDate
value="<string>"
type="<string>"
dateStyle="<string>"
timeStyle="<string>"
pattern="<string>"
var="<string>"
scope="<string>"/>
属性
属性 | 描述 | 是否必要 | 默认值 |
value | 要显示的日期 | 是 | 无 |
type | DATE, TIME, 或 BOTH | 否 | date |
dateStyle | FULL, LONG, MEDIUM, SHORT, 或 DEFAULT | 否 | default |
timeStyle | FULL, LONG, MEDIUM, SHORT, 或 DEFAULT | 否 | default |
pattern | 自定义格式模式 | 否 | 无 |
var | 存储格式化日期的变量名 | 否 | 显示在页面 |
scope | 存储格式化日志变量的范围 | 否 | 页面 |
示例
<fmt:parseDate value="2020-01-06" type="date" /> <br>
<fmt:parseDate value="2020/01/06" pattern="yyyy/MM/dd" /> <br>
总结:
formatNumber:将数值 转换为 指定格式的字符串
formatDate:将Date数据转换为 指定格式的字符串
parseNumber:将不同类型的字符串 转换为 数值类型
parseDate:将不同类型的字符串 转换为 Date类型
分层思想案例
实现案例:页面完成登录功能,登录成功跳转到项目首页,登录失败提示信息。
实现逻辑初版思路分析
项目实现
【1】创建数据库表:
【2】创建项目,并导入jar包:
【3】登录页面实现:login.jsp
<%--
Created by IntelliJ IDEA.
User: zhaoss-msb
Date: 2023/2/8
Time: 21:22
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="loginServlet" method="post">
用户名:<input type="text" name="uname"><br>
密码:<input type="password" name="pwd"><br>
<input type="submit" value="登录"><br>
${failmsg}
</form>
</body>
</html>
【4】实体类User.java
package com.msb.pojo;
/**
* @Author: zhaoss
*/
public class User {
private int uid;
private String uname;
private String pwd;
public User(int uid, String uname, String pwd) {
this.uid = uid;
this.uname = uname;
this.pwd = pwd;
}
public User() {
}
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"uid=" + uid +
", uname='" + uname + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
【5】servlet的实现:LoginServlet.java
package com.msb.servlet;
import com.msb.dao.UserDao;
import com.msb.pojo.User;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Author: zhaoss
*/
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 接收页面传送过来的数据:
String uname = req.getParameter("uname");
String pwd = req.getParameter("pwd");
System.out.println(uname);
System.out.println(pwd);
// 2. 连接数据库:
UserDao userDao = new UserDao();
User user = userDao.selectOne(uname, pwd);
System.out.println(user);
// 3. 根据数据库处理的结果 响应数据:
if (user != null){// 数据查询到--》用户存在
// 跳转到项目首页 (重定向)
resp.sendRedirect(req.getContextPath() + "/index.jsp");
}else{// 数据没有查询到---》用户不存在
// 登录错误的提示信息数据存储在request作用域中:
req.setAttribute("failmsg","登录失败");
// 请求转发:
req.getRequestDispatcher("/login.jsp").forward(req,resp);
}
}
}
【6】dao层:UserDao.java
package com.msb.dao;
import com.msb.pojo.User;
import java.sql.*;
/**
* @Author: zhaoss
*/
public class UserDao {
// 查询学生信息:
public User selectOne(String uname,String pwd){
User user = null;
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
// 加载驱动:
Class.forName("com.mysql.cj.jdbc.Driver");
// 获取连接:
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/logintest?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true","root","root");
// 创建会话:
ps = conn.prepareStatement("select * from t_user where uname = ? and pwd = ?");
// 给问号设置数值:
ps.setString(1,uname);
ps.setString(2,pwd);
// 进行查询:
rs = ps.executeQuery();
// 处理结果集:
if(rs.next()){
int uid = rs.getInt("uid");
String username = rs.getString("uname");
String password = rs.getString("pwd");
// 将数据封装为对象:
user = new User(uid,username,password);
}
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
// 数据库资源关闭:
try {
rs.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
try {
ps.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
try {
conn.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
return user;
}
}
【7】测试效果:
访问登录页面:
登录成功:
登录失败:
MVC分层思想
优点:
1:耦合性低
视图层和业务层分离,这样就允许更改视图层代码而不用重新编译模型和控制器代码,同样,一个应用的业务流程或者业务规则的改变只需要改动MVC的模型层即可。因为模型与控制器和视图相分离,所以很容易改变应用程序的数据层和业务规则。
2:重用性高
MVC模式允许使用各种不同样式的视图来访问同一个服务器端的代码,因为多个视图能共享一个模型,它包括任何WEB(HTTP)浏览器或者无线浏览器(wap),比如,用户可以通过电脑也可通过手机来订购某样产品,虽然订购的方式不一样,但处理订购产品的方式是一样的。由于模型返回的数据没有进行格式化,所以同样的构件能被不同的界面使用。
MVC使开发和维护用户接口的技术含量降低。
3:开发效率提高,人员职责明确
使用MVC模式使开发时间得到较大的缩减,它使程序员(Java开发人员)集中精力于业务逻辑,界面程序员(HTML和JSP开发人员)集中精力于表现形式上。
4:耦合度低,可维护性高
分离视图层和业务逻辑层也使得WEB应用更易于维护和修改。每层代码修改可以达到无损替换的效果
5:有利软件工程化管理
由于不同的层各司其职,每一层不同的应用具有某些相同的特征,有利于通过工程化、工具化管理程序代码。控制器也提供了一个好处,就是可以使用控制器来联接不同的模型和视图去完成用户的需求,这样控制器可以为构造应用程序提供强有力的手段。给定一些可重用的模型和视图,控制器可以根据用户的需求选择模型进行处理,然后选择视图将处理结果显示给用户。
JavaBean
符合特定规范的Java类,是一种可重用的组件。
JavaBean的分类
封装数据:数据Bean 实体类。
特定规范
public class,提供无参数构造方法。
属性private
提供public的setter和getter方法。
封装业务: 业务Bean service Mapper dao。
分层过渡演示:
分层模式开发
构建项目,通过构建包进行分层,最终项目结构如下:
login.jsp:
<%--
Created by IntelliJ IDEA.
User: zhaoss-msb
Date: 2023/2/8
Time: 21:22
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="loginServlet" method="post">
用户名:<input type="text" name="uname"><br>
密码:<input type="password" name="pwd"><br>
<input type="submit" value="登录"><br>
${failmsg}
</form>
</body>
</html>
index.jsp:
<%--
Created by IntelliJ IDEA.
User: zhaoss-msb
Date: 2023/2/13
Time: 14:04
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
项目首页,登录成功。
</body>
</html>
com.msb.pojo.User.java:
package com.msb.pojo;
/**
* @Author: zhaoss
*/
public class User {
private int uid;
private String uname;
private String pwd;
public User(int uid, String uname, String pwd) {
this.uid = uid;
this.uname = uname;
this.pwd = pwd;
}
public User() {
}
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"uid=" + uid +
", uname='" + uname + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
com.msb.controller.LoginServlet.java:
package com.msb.controller;
import com.msb.dao.UserDao;
import com.msb.pojo.User;
import com.msb.service.UserService;
import com.msb.service.impl.UserServiceImpl;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Author: zhaoss
*/
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
UserService userService = new UserServiceImpl();
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 接收页面传送过来的数据:
String uname = req.getParameter("uname");
String pwd = req.getParameter("pwd");
System.out.println(uname);
System.out.println(pwd);
// 2. 调用业务层::
User user = userService.selectUserInfo(uname, pwd);
System.out.println(user);
// 3. 根据数据库处理的结果 响应数据:
if (user != null){// 数据查询到--》用户存在
// 跳转到项目首页 (重定向)
resp.sendRedirect(req.getContextPath() + "/index.jsp");
}else{// 数据没有查询到---》用户不存在
// 登录错误的提示信息数据存储在request作用域中:
req.setAttribute("failmsg","登录失败");
// 请求转发:
req.getRequestDispatcher("/login.jsp").forward(req,resp);
}
}
}
com.msb.service.UserService.java:
package com.msb.service;
import com.msb.pojo.User;
/**
* @Author: zhaoss
*/
public interface UserService {
// 根据用户名和密码查询用户信息:
public abstract User selectUserInfo(String uname,String pwd);
}
com.msb.service.UserServiceImpl.java:
package com.msb.service.impl;
import com.msb.dao.UserDao;
import com.msb.dao.impl.UserDaoImpl;
import com.msb.pojo.User;
import com.msb.service.UserService;
/**
* @Author: zhaoss
*/
public class UserServiceImpl implements UserService {
UserDao userDao = new UserDaoImpl();
@Override
public User selectUserInfo(String uname, String pwd) {
return userDao.selectOneUser(uname,pwd);
}
}
com.msb.dao.UserDao.java:
package com.msb.dao;
import com.msb.pojo.User;
/**
* @Author: zhaoss
*/
public interface UserDao {
// 根据用户名和密码查询用户信息:
public abstract User selectOneUser(String uname, String pwd);
}
com.msb.dao.UserDaoImpl.java:
package com.msb.dao.impl;
import com.msb.dao.UserDao;
import com.msb.pojo.User;
import java.sql.*;
/**
* @Author: zhaoss
*/
public class UserDaoImpl implements UserDao {
@Override
public User selectOneUser(String uname, String pwd) {
User user = null;
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
// 加载驱动:
Class.forName("com.mysql.cj.jdbc.Driver");
// 获取连接:
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/logintest?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true","root","root");
// 创建会话:
ps = conn.prepareStatement("select * from t_user where uname = ? and pwd = ?");
// 给问号设置数值:
ps.setString(1,uname);
ps.setString(2,pwd);
// 进行查询:
rs = ps.executeQuery();
// 处理结果集:
if(rs.next()){
int uid = rs.getInt("uid");
String username = rs.getString("uname");
String password = rs.getString("pwd");
// 将数据封装为对象:
user = new User(uid,username,password);
}
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
// 数据库资源关闭:
try {
rs.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
try {
ps.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
try {
conn.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
return user;
}
}
用户非法访问拦截
什么叫用户非法访问:
在没有登录的情况下直接访问项目首页成功,就是非法访问。
比如跳过login.jsp直接访问index.jsp成功了,就是非法访问。
解决:
利用过滤器对非法访问进行拦截,当用户未登录时,拦截请求到登录页面
拦截的资源:拦截所有资源 /*
需要被放行的资源(不需要登录即可访问的资源):
1、放行指定页面,不需要登录可以访问的页面 (例如:登录页面、注册页面等)
2、放行静态资源(例如:css、js、image等资源)
3、放行指定操作,不需要登录即可执行的操作(例如:登录操作、注册操作等)
4、登录状态放行 (登录成功以后,如果存在指定sessuin对象,则为登录状态)
LoginServlet.java:
package com.msb.controller;
import com.msb.dao.UserDao;
import com.msb.pojo.User;
import com.msb.service.UserService;
import com.msb.service.impl.UserServiceImpl;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Author: zhaoss
*/
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
UserService userService = new UserServiceImpl();
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 接收页面传送过来的数据:
String uname = req.getParameter("uname");
String pwd = req.getParameter("pwd");
System.out.println(uname);
System.out.println(pwd);
// 2. 调用业务层::
User user = userService.selectUserInfo(uname, pwd);
System.out.println(user);
// 3. 根据数据库处理的结果 响应数据:
if (user != null){// 数据查询到--》用户存在
// 一旦有这个用户,登录成功,将用户信息存在session作用域中:
req.getSession().setAttribute("user",user);//存储对象
// 跳转到项目首页 (重定向)
resp.sendRedirect(req.getContextPath() + "/index.jsp");
}else{// 数据没有查询到---》用户不存在
// 登录错误的提示信息数据存储在request作用域中:
req.setAttribute("failmsg","登录失败");
// 请求转发:
req.getRequestDispatcher("/login.jsp").forward(req,resp);
}
}
}
过滤器:
package com.msb.filter;
import com.msb.pojo.User;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Author: zhaoss
*/
@WebFilter("/*") //1.首先先将所有的资源都进行拦截
public class LoginFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 3.基于HTTP的请求的对象:
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
// 4.在完成2,3步骤以后,登录index.jsp页面后出现问题: 将您重定向的次数过多。
// 5.所以我们要做的事就是设置放行资源-登录页面:
// 6.通过地址栏请求的地址判断是否携带login.jsp,如果携带的话,那么直接放行:
String uri = request.getRequestURI();
if (uri.contains("/login.jsp")){
// 7.如果携带了/login.jsp,就将资源进行放行操作:
filterChain.doFilter(request,response);
return;
}
// 8. 对静态资源进行放行,否则登录页面中使用的图片、css、js的内容都无法访问:
if (uri.contains("/images")){
filterChain.doFilter(request,response);
return;
}
// 8.2 在登录页面点击登录以后,会访问LoginServlet,所以也要对LoginServlet进行放行:
if (uri.contains("/loginServlet")){
filterChain.doFilter(request,response);
return;
}
// 9.完成到第8步已经可以拦截非法操作了,但是如果登录以后,还访问index.jsp是不可以的
// 10. 如果已经登录了,就创建了会话了,那么就可以随意的访问其他页面了
User user = (User)request.getSession().getAttribute("user");
if (user != null){// 11.如果用户不是空,证明已经登录成功,那么就放行
filterChain.doFilter(request,response);
return;
}
// 2.只要进行了非法访问,就跳转到登录页面:(重定向)
response.sendRedirect("login.jsp");
}
@Override
public void destroy() {
Filter.super.destroy();
}
}