一、JSP概述
1.1 、JSP基础
1.1.1 、JSP简介
JSP全称是Java Server Page,它和Servlet一样,也是sun公司推出的一套开发动态web资源的技术,称为JSP/Servlet规范。JSP的本质其实就是一个Servlet。
1.1.2 、JSP和HTML以及Servlet的适用场景
类别 | 适用场景 |
---|---|
HTML | 只能开发静态资源,不能包含java代码,无法添加动态数据。 |
Servlet | 写java代码,可以输出页面内容,但是很不方便,开发效率极低。 |
JSP | 它包括了HTML的展示技术,同时具备Servlet输出动态资源的能力。但是不适合作为控制器来用。 |
1.1.3 、JSP简单入门
创建JavaWeb工程
在index.jsp中填写内容
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>JSP</title>
</head>
<body>
<h1>这是我的第一个jsp页面</h1>
</body>
</html>
部署项目
测试运行
http://localhost:8080/demo01/index.jsp
1.1.4 JSP说明
写在之前: 明确JSP就是一个Servlet。是一个特殊的Servlet。
JSP的原理:
客户端提交请求
——Tomcat服务器解析请求地址
——找到JSP页面
——Tomcat将JSP页面翻译成Servlet的java文件
——将翻译好的.java文件编译成.class文件
——返回到客户浏览器上。
1.2、JSP原理
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>hello jsp</h1>
<%
System.out.println("hello,jsp~");
%>
</body>
</html>
我们之前说 JSP 就是一个页面,那么在 JSP 中写 html
标签,我们能理解,但是为什么还可以写 Java
代码呢?
因为 ==JSP 本质上就是一个 Servlet。==接下来我们聊聊访问jsp时的流程
- 浏览器第一次访问
hello.jsp
页面 tomcat
会将hello.jsp
转换为名为hello_jsp.java
的一个Servlet
tomcat
再将转换的servlet
编译成字节码文件hello_jsp.class
tomcat
会执行该字节码文件,向外提供服务
我们可以到项目所在磁盘目录下找
C:\Users\shixiaochuang\AppData\Local\JetBrains\IntelliJIdea2024.1\tomcat\5ad44f52-f79d-4ce2-b548-fa0e596389c9\work\Catalina\localhost\demo01\org\apache\jsp
目录,而这个目录下就能看到转换后的 servlet
打开 hello_jsp.java
文件,来查看里面的代码
public final class hello_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports {
由上面的类的继承关系可以看到继承了名为 HttpJspBase
这个类,那我们在看该类的继承关系。
https://github.com/apache/tomcat/blob/main/java/org/apache/jasper/runtime/HttpJspBase.java
可以看到该类继承了 HttpServlet
;那么 hello_jsp
这个类就间接的继承了 HttpServlet
,也就说明 hello_jsp
是一个 servlet
。
继续阅读 hello_jsp
类的代码,可以看到有一个名为 _jspService()
的方法,该方法就是每次访问 jsp
时自动执行的方法,和 servlet
中的 service
方法一样 。
而在 _jspService()
方法中可以看到往浏览器写标签的代码:
out.write("\r\n");
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write(" <title>Title</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");
out.write("<h1>hello jsp</h1>\r\n");
out.write("\r\n");
以前我们自己写 servlet
时,这部分代码是由我们自己来写,现在有了 jsp
后,由tomcat完成这部分功能。
讲到这里,我们应该清楚的认识到,JSP它是一个特殊的Servlet,主要是用于展示动态数据。它展示的方式是用流把数据输出出来,而我们在使用JSP时,涉及HTML的部分,都与HTML的用法一致,这部分称为jsp中的模板元素,在开发过程中,先写好这些模板元素,因为它们决定了页面的外观。
二、JSP应用
2.1 、JSP脚本
1)Java代码块
在jsp中,可以使用java脚本代码。形式为:<% 此处写java代码 %>
但是,在实际开发中,极少使用此种形式编写java代码。同时需要注意的是:
<%
在里面写java程序脚本需要注意:这里面的内容由tomcat负责翻译,翻译之后是service方法的成员变量
%>
示例:
<%--Java代码块--%>
<%
System.out.println("这里是Java代码块");
String[] arr = new String[]{"Java","JavaWeb","JSP"};
for (String s : arr) {
System.out.println(s);
}
%>
<hr/>
2)JSP表达式
在jsp中,可以使用特定表达式语法,形式为:<%=表达式%>
jsp在翻译完后是out.print(表达式内容);
所以:<%out.print("当前时间);%>和<%=“当前时间”%>是一样的。
在实际开发中,这种表达式语法用的也很少使用。
示例:
<!--JSP表达式-->
<%="这是JSP表达式"%><br/>
就相当于<br/>
<%out.println("这是没有JSP表达式输出的");%>
3)JSP声明
在JSP中也可以声明一些变量,方法,静态方法,形式为:<%! 声明的内容 %>
使用JSP声明需要注意:
<%!
需要注意的是: 写在里面的内容将会被tomcat翻译成全局的属性或者类方法。
%>
示例:
<!--JSP声明-->
<%! String str = "声明语法格式";%>
<%=str%>
4)JSP注释
在使用JSP时,它有自己的注释,形式为:<%–注释–%>
需要注意的是:
在Jsp中可以使用html的注释,但是只能注释html元素,不能注释java程序片段和表达式。同时,被html注释部分会参与翻译,并且会在浏览器上显示
jsp的注释不仅可以注释java程序片段,也可以注释html元素,并且被jsp注释的部分不会参与翻译成.java文件,也不会在浏览器上显示。
示例:
<%--JSP注释--%>
<!--HTML注释-->
5)语法的示例
JSP语法完整示例代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--Java代码块--%>
<%
System.out.println("这里是Java代码块");
String[] arr = new String[]{"Java", "JavaWeb", "JSP"};
for (String s : arr) {
System.out.println(s);
}
%>
<hr/>
<%--这是JSP表达式--%>
<%=
"这是JSP表达式"
%>
就相当于<br/>
<%out.println("这是没有JSP表达式输出的");%>
<hr/>
<!--JSP声明-->
<%! String str = "声明语法格式";%>
<%=str%>
<%--JSP注释--%>
<!--HTML注释-->
</body>
</html>
JSP语法运行结果
http://localhost:8080/demo01/demo01.jsp
6)区分三大脚本
JSP 脚本有如下三个分类:
- <%…%>:内容会直接放到_jspService()方法之中
- <%=…%>:内容会放到out.print()中,作为out.print()的参数
- <%!…%>:内容会放到_jspService()方法之外,被类直接包含
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>区分三角脚本</title>
</head>
<body>
<h1>hello jsp</h1>
<%
System.out.println("hello,jsp~");
int i = 3;
%>
<%="hello"%>
<%=i%>
<%!
void show() {
}
String name = "zhangsan";
%>
</body>
</html>
2.2、JSP指令
1)page指令
**language:**告知引擎,脚本使用的是java,默认是java,支持java。不写也行。
extends:告知引擎,JSP对应的Servlet的父类是哪个,不需要写,也不需要改。
import:告知引擎,导入哪些包(类)。
注意:引擎会自动导入:java.lang.*,javax.servlet.*,javax.servlet.http.*,javax.servlet.jsp.*
导入的形式:
<%@page import=”java.util.Date,java.util.UUID”%>或者:
<%@page import=”java.util.Date”%>
<%@page import=”java.util.UUID”%> 用Eclipse:Alt+/ 自动导入
session:告知引擎是否产生HttpSession对象,即是否在代码中调用request.getSession()。默认是true。
buffer:JspWriter用于输出JSP内容到页面上。告知引擎,设定他的缓存大小。默认8kb。
errorPage:告知引擎,当前页面出现异常后,应该转发到哪个页面上(路径写法:/代表当前应用)
小贴士:当在errorpage上使用了isErrorPage=true之后,ie8有时候不能正常显示
配置全局错误页面:web.xml
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/error.jsp</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/404.html</location>
</error-page>
当使用了全局错误页面,就无须再写errorPage来实现转到错误页面,而是由服务器负责跳转到错误页面。
isErrorPage:告知引擎,是否抓住异常。如果该属性为true,页面中就可以使用exception对象,打印异常的详细信息。默认值是false。
contentType:告知引擎,响应正文的MIME类型。contentType=“text/html;charset=UTF-8”
相当于response.setContentType(“text/html;charset=UTF-8”);
pageEncoding:告知引擎,翻译jsp时(从磁盘上读取jsp文件)所用的码表。pageEncoding="UTF-8"相当于告知引擎用UTF-8读取JSP
isELIgnored*:告知引擎,是否忽略EL表达式,默认值是false,不忽略。
2)include指令
语法格式:<%@include file=“” %>该指令是包含外部页面。
属性:file,以/开头,就代表当前应用。
使用示例
静态包含的特点
3)taglib指令
语法格式:<%taglib uri=“” prefix=“”%>
作用:该指令用于引入外部标签库。html标签和jsp标签不用引入。
属性:
uri:外部标签的URI地址。
prefix:使用标签时的前缀。
2.3、 JSP细节
1)九大隐式对象
什么是隐式对象呢?它指的是在jsp中,可以不声明就直接使用的对象。它只存在于jsp中,因为java类中的变量必须要先声明再使用。其实jsp中的隐式对象也并非是未声明,只是它是在翻译成.java文件时声明的。所以我们在jsp中可以直接使用。
隐式对象名称 | 类型 | 备注 |
---|---|---|
request | javax.servlet.http.HttpServletRequest | |
response | javax.servlet.http.HttpServletResponse | |
session | javax.servlet.http.HttpSession | Page指令可以控制开关 |
application | javax.servlet.ServletContext | |
page | Java.lang.Object | 当前jsp对应的servlet引用实例 |
config | javax.servlet.ServletConfig | |
exception | java.lang.Throwable | page指令有开关 |
out | javax.servlet.jsp.JspWriter | 字符输出流,相当于printwriter |
pageContext | javax.servlet.jsp.PageContext | 很重要 |
2)PageContext对象
简介
它是JSP独有的对象,Servlet中没有这个对象。本身也是一个域(作用范围)对象,但是它可以操作其他3个域对象中的属性。而且还可以获取其他8个隐式对象。
生命周期
它是一个局部变量,所以它的生命周期随着JSP的创建而诞生,随着JSP的结束而消失。每个JSP页面都有一个独立的PageContext。
常用方法
在上图中,同学们发现没有页面域操作的方法,其实是定义在了PageContext的父类JspContext中,如下图所示:
3)四大域对象
JavaWeb中有四大域对象,分别是:
- page:当前页面有效
- request:当前请求有效
- session:当前会话有效
- application:当前应用有效
域对象名称 | 范围 | 级别 | 备注 |
---|---|---|---|
PageContext | 页面范围 | 最小,只能在当前页面用 | 因范围太小,开发中用的很少 |
ServletRequest | 请求范围 | 一次请求或当期请求转发用 | 当请求转发之后,再次转发时请求域丢失 |
HttpSession | 会话范围 | 多次请求数据共享时使用 | 多次请求共享数据,但不同的客户端不能共享 |
ServletContext | 应用范围 | 最大,整个应用都可以使用 | 尽量少用,如果对数据有修改需要做同步处理 |
三、EL 表达式
3.1、 EL表达式
EL(全称Expression Language )表达式语言,用于简化 JSP 页面内的 Java 代码。
EL 表达式的主要作用是 获取数据。其实就是从域对象中获取数据,然后将数据展示在页面上。
而 EL 表达式的语法也比较简单,== e x p r e s s i o n = = 。例如: {expression}== 。例如: expression==。例如:{brands} 就是获取域中存储的 key 为 brands 的数据。
3.1.1 EL表达式概述
基本概念
EL表达式,全称是Expression Language。意为表达式语言。它是Servlet规范中的一部分,是JSP2.0规范加入的内容。其作用是用于在JSP页面中获取数据,从而让我们的JSP脱离java代码块和JSP表达式。
基本语法
EL表达式的语法格式非常简单,写为 ${表达式内容}
例如:在浏览器中输出请求域中名称为message的内容。
假定,我们在请求域中存入了一个名称为message的数据(request.setAttribute("message","EL");
),此时在jsp中获取的方式,如下表显示:
Java代码块 | JSP表达式 | EL表达式 |
---|---|---|
<%<br/> <br/> String message = (String)request.getAttribute("message");<br/> out.write(message);<br/>%> | <%=request.getAttribute("message")%> | ${message} |
通过上面我们可以看出,都可以从请求域中获取数据,但是EL表达式写起来是最简单的方式。这也是以后我们在实际开发中,当使用JSP作为视图时,绝大多数都会采用的方式。
3.1.2、 EL表达式的入门案例
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>EL表达式快速入门</title>
</head>
<body>
<%--向域对象中添加请求--%>
<%
request.setAttribute("username", "shixiaochuang");
%>
<%--获取数据--%>
Java代码块:
<%
Object username = request.getAttribute("username");
out.println(username);
%>
<hr/>
JSP表达式:
<%=
request.getAttribute("username")
%>
<hr/>
EL表达式:${username}
</body>
</html>
http://localhost:8080/demo02/el01.jsp
3.2、 EL表达式基本用法
在前面的概述介绍中,我们介绍了EL表达式的作用,它就是用于获取数据的,那么它是从哪获取数据呢?
1)获取四大域中的数据
它只能从四大域中获取数据,调用的就是findAttribute(name,value);
方法,根据名称由小到大逐个域中查找,找到就返回,找不到就什么都不显示。
它可以获取对象,可以是对象中关联其他对象,可以是一个List集合,也可以是一个Map集合。具体代码如下:
创建两个实体类,User和Address
package com.jsp.demo02;
import java.io.Serializable;
/**
* 用户的实体类
*/
public class User implements Serializable {
private String name = "史小创";
private int age = 18;
private Address address = new Address();
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 Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
package com.jsp.demo02;
import java.io.Serializable;
/**
* @Author: 史小创
* @Time: 2024/8/30 下午8:33
* @Description: 地址的实体类
*/
public class Address implements Serializable {
private String province = "北京";
private String city = "昌平区";
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
}
<%--
@Author: 史小创
@Time: 2024/8/30 下午8:38
@Description:
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="com.jsp.demo02.User" %>
<%@ page import="java.util.List" %>
<%@ page import="java.util.ArrayList" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.util.Map" %>
<html>
<head>
<title>EL入门</title>
</head>
<body>
<%--EL表达式概念:
它是Expression Language的缩写。它是一种替换jsp表达式的语言。
EL表达式的语法:
${表达式}
表达式的特点:有明确的返回值。
EL表达式就是把内容输出到页面上
EL表达式的注意事项:
1.EL表达式没有空指针异常
2.EL表达式没有数组下标越界
3.EL表达式没有字符串拼接
EL表达式的数据获取:
它只能在四大域对象中获取数据,不在四大域对象中的数据它取不到。
它的获取方式就是findAttribute(String name)
--%>
<br/>-----------获取对象数据---------------------<br/>
<%
//1.把用户的信心存入域中
User user = new User();
pageContext.setAttribute("u", user);
%>
${u}===============输出的是内存地址
<br/>
${u.name}
${u.age}
<hr/>
<%--与下面代码的含义是一致的--%>
<%--<%--%>
<%-- user = (User) pageContext.findAttribute("u");--%>
<%-- out.print(user);--%>
<%-- System.out.println();--%>
<%-- user.getName();--%>
<%-- user.getAge();--%>
<%--%>--%>
<br/>-----------获取关联对象数据------------------<br/>
${u.address}==========输出的address对象的地址<br/>
${u.address.province}${u.address.city}<br/>
${u["address"]['province']}
<hr/>
<br/>-----------获取数组数据---------------------<br/>
<%
String[] strs = new String[]{"He", "llo", "Expression", "Language"};
pageContext.setAttribute("strs", strs);
%>
${strs[0]}==========取的数组中下标为0的元素<br/>
${strs[3]}
${strs[5]}===========如果超过了数组的下标,则什么都不显示<br/>
${strs["2"]}=========会自动为我们转换成下标<br/>
${strs['1']}
<hr/>
<br/>-----------获取List集合数据-----------------<br/>
<%
List<String> list = new ArrayList<String>();
list.add("AAA");
list.add("BBB");
list.add("CCC");
list.add("DDD");
pageContext.setAttribute("list", list);
%>
${list}<br/>
${list[0] }<br/>
${list[3] }<br/>
<hr/>
<br/>-----------获取Map集合数据------------------<br/>
<%
Map<String, User> map = new HashMap<String, User>();
map.put("aaa", new User());
pageContext.setAttribute("map", map);
%>
${map}<br/>
${map.aaa}<%--获取map的value,是通过get(Key) --%><br/>
${map.aaa.name}${map.aaa.age}<br/>
${map["aaa"].name }
<hr/>
</body>
</html>
http://localhost:8080/demo02/el02.jsp
2)EL表达式的注意事项
在使用EL表达式时,它帮我们做了一些处理,使我们在使用时可以避免一些错误。它没有空指针异常,没有数组下标越界,没有字符串拼接。
<%--
@Author: 史小创
@Time: 2024/8/30 下午9:08
@Description: el表达式的注意事项
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>el表达式的注意事项</title>
</head>
<body>
<%--EL表达式的三个没有--%>
第一个:没有空指针异常<br/>
<%
String str = null;
request.setAttribute("testNull", str);
%>
${testNull}
<hr/>
第二个:没有数组下标越界<br/>
<%
String[] strs = new String[]{"a", "b", "c"};
request.setAttribute("strs", strs);
%>
取第一个元素:${strs[0]}
取第六个元素:${strs[5]}
<hr/>
第三个:没有字符串拼接<br/>
<%--${strs[0]+strs[1]}--%>
${strs[0]}+${strs[1]}
</body>
</html>
运行结果图:
http://localhost:8080/demo02/el03.jsp
3)EL表达式的使用细节
EL表达式除了能在四大域中获取数据,同时它可以访问其他隐式对象,并且访问对象有返回值的方法.
4)EL表达式的运算符
EL表达式中运算符如下图所示,它们都是一目了然的:
但是有两个特殊的运算符,使用方式的代码如下:
<%@ page import="java.util.ArrayList" %>
<%@ page import="java.util.List" %><%--
@Author: 史小创
@Time: 2024/8/30 下午9:12
@Description:
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>EL两个特殊的运算符</title>
</head>
<body>
<%--empty运算符:
它会判断:对象是否为null,字符串是否为空字符串,集合中元素是否是0个
--%>
<%
String str = null;
String str1 = "";
List<String> slist = new ArrayList<String>();
pageContext.setAttribute("str", str);
pageContext.setAttribute("str1", str1);
pageContext.setAttribute("slist", slist);
%>
${empty str}============当对象为null返回true<br/>
${empty str1 }==========当字符串为空字符串是返回true(注意:它不会调用trim()方法)<br>
${empty slist}==========当集合中的元素是0个时,是true
<hr/>
<%--三元运算符
条件?真:假
--%>
<% request.setAttribute("gender", "female"); %>
<input type="radio" name="gender" value="male" ${gender eq "male"?"checked":""} >男
<input type="radio" name="gender" value="female" ${gender eq "female"?"checked":""}>女
</body>
</html>
http://localhost:8080/demo02/el04.jsp
3.3、EL表达式的11个隐式对象
EL表达式也为我们提供隐式对象,可以让我们不声明直接来使用,十一个对象见下表,需要注意的是,它和JSP的隐式对象不是一回事:
EL中的隐式对象 | 类型 | 对应JSP隐式对象 | 备注 |
---|---|---|---|
PageContext | Javax.serlvet.jsp.PageContext | PageContext | 完全一样 |
ApplicationScope | Java.util.Map | 没有 | 应用层范围 |
SessionScope | Java.util.Map | 没有 | 会话范围 |
RequestScope | Java.util.Map | 没有 | 请求范围 |
PageScope | Java.util.Map | 没有 | 页面层范围 |
Header | Java.util.Map | 没有 | 请求消息头key,值是value(一个) |
HeaderValues | Java.util.Map | 没有 | 请求消息头key,值是数组(一个头多个值) |
Param | Java.util.Map | 没有 | 请求参数key,值是value(一个) |
ParamValues | Java.util.Map | 没有 | 请求参数key,值是数组(一个名称多个值) |
InitParam | Java.util.Map | 没有 | 全局参数,key是参数名称,value是参数值 |
Cookie | Java.util.Map | 没有 | Key是cookie的名称,value是cookie对象 |
3.4、EL表达式的综合使用
package com.jsp.demo02;
public class Brand {
private int id;
private String brandName;
private String companyName;
private int order;
private String description;
private int status;
// Constructors, getters and setters
public Brand(int id, String brandName, String companyName, int order, String description, int status) {
this.id = id;
this.brandName = brandName;
this.companyName = companyName;
this.order = order;
this.description = description;
this.status = status;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getBrandName() {
return brandName;
}
public void setBrandName(String brandName) {
this.brandName = brandName;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
}
package com.jsp.demo02;
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;
import java.util.ArrayList;
import java.util.List;
/**
* @Author: 史小创
* @Time: 2024/8/30 下午9:20
* @Description:
*/
@WebServlet("/eldemo")
public class ELServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 准备数据
List<Brand> brands = new ArrayList<Brand>();
brands.add(new Brand(1, "三只松鼠", "三只松鼠", 100, "三只松鼠,好吃不上火", 1));
brands.add(new Brand(2, "优衣库", "优衣库", 200, "优衣库,服适人生", 0));
brands.add(new Brand(3, "小米", "小米科技有限公司", 1000, "为发烧而生", 1));
// 2.将数据储存在request请求中
req.setAttribute("brands", brands);
// 3.转发至jsp
// 此处需要用转发,因为转发才可以使用 request 对象作为域对象进行数据共享
req.getRequestDispatcher("/el05.jsp").forward(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
<%--
@Author: 史小创
@Time: 2024/8/30 下午9:28
@Description:
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${brands[0].id} - ${brands[0].brandName} - ${brands[0].companyName} - ${brands[0].order} - ${brands[0].description} - ${brands[0].status} <br/>
${brands[1].id} - ${brands[1].brandName} - ${brands[1].companyName} - ${brands[1].order} - ${brands[1].description} - ${brands[1].status} <br/>
${brands[2].id} - ${brands[2].brandName} - ${brands[2].companyName} - ${brands[2].order} - ${brands[2].description} - ${brands[2].status} <br/>
</body>
</html>
http://localhost:8080/demo02/eldemo
el 表达式获取数据,会依次从JavaWeb中四大域对象中寻找,直到找到为止。
例如: ${brands},el 表达式获取数据,会先从page域对象中获取数据,如果没有再到 requet 域对象中获取数据,如果再没有再到 session 域对象中获取,如果还没有才会到 application 中获取数据。
四、JSTL
4.1、JSTL概述
1)简介
JSTL的全称是:JSP Standard Tag Libary。它是JSP中标准的标签库。它是由Apache实现的。
它由以下5个部分组成:
组成 | 作用 | 说明 |
---|---|---|
Core | 核心标签库。 | 通用逻辑处理 |
Fmt | 国际化有关。 | 需要不同地域显示不同语言时使用 |
Functions | EL函数 | EL表达式可以使用的方法 |
SQL | 操作数据库。 | 不用 |
XML | 操作XML。 | 不用 |
JSTL 提供了很多标签,如下图
2)基本使用
在我们实际开发中,用到的jstl标签库主要以核心标签库为准,偶尔会用到国际化标签库的标签。下表中把我们经常可能用到的标签列在此处,其余标签库请同学们参考【JSTL标签库.doc】文档。
标签名称 | 功能分类 | 分类 | 作用 |
---|---|---|---|
<c:if> | 流程控制 | 核心标签库 | 用于判断 |
<c:choose> ,<c:when>,<c:otherwise> | 流程控制 | 核心标签库 | 用于多个条件判断 |
<c:foreache> | 迭代操作 | 核心标签库 | 用于循环遍历 |
<%@ page import="java.util.List" %>
<%@ page import="java.util.ArrayList" %><%--
@Author: 史小创
@Time: 2024/8/30 下午9:56
@Description: 基本使用
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--导入jstl标签库 --%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<title>基本使用</title>
</head>
<body>
<%-- c:if c:choose c:when c:otherwise --%>
<% pageContext.setAttribute("score", "F"); %>
<c:if test="${pageScope.score eq 'A' }">
优秀
</c:if>
<c:if test="${pageScope.score eq 'C' }">
一般
</c:if>
<hr/>
<c:choose>
<c:when test="${pageScope.score eq 'A' }">
AAA
</c:when>
<c:when test="${pageScope.score eq 'B' }">BBB
</c:when>
<c:when test="${pageScope.score eq 'C' }">CCC
</c:when>
<c:when test="${pageScope.score eq 'D' }">DDD
</c:when>
<c:otherwise>其他</c:otherwise>
</c:choose>
<%-- c:forEach 它是用来遍历集合的
属性:
items:要遍历的集合,它可以是EL表达式取出来的
var:把当前遍历的元素放入指定的page域中。 var的取值就是key,当前遍历的元素就是value
注意:它不能支持EL表达式,只能是字符串常量
begin:开始遍历的索引
end:结束遍历的索引
step:步长。i+=step
varStatus:它是一个计数器对象。里面有两个属性,一个是用于记录索引。一个是用于计数。
索引是从0开始。计数是从1开始
--%>
<hr/>
<% List<String> list = new ArrayList<String>();
list.add("AAA");
list.add("BBB");
list.add("CCC");
list.add("DDD");
list.add("EEE");
list.add("FFF");
list.add("GGG");
list.add("HHH");
list.add("III");
list.add("JJJ");
list.add("KKK");
list.add("LLL");
pageContext.setAttribute("list", list);
%>
<c:forEach items="${list}" var="s" begin="1" end="7" step="2">
${s}<br/>
</c:forEach>
<hr/>
<c:forEach begin="1" end="9" var="num">
<a href="#">${num}</a>
</c:forEach>
<hr/>
<table>
<tr>
<td>索引</td>
<td>序号</td>
<td>信息</td>
</tr>
<c:forEach items="${list}" var="s" varStatus="vs">
<tr>
<td>${vs.index}</td>
<td>${vs.count}</td>
<td>${s}</td>
</tr>
</c:forEach>
</table>
</body>
</html>
http://localhost:8080/demo03/jstl01.jsp
4.2、常用标签
4.2.1、if 标签
<c:if>
:相当于 if 判断
- 属性:test,用于定义条件表达式
<c:if test="${flag == 1}">
男
</c:if>
<c:if test="${flag == 2}">
女
</c:if>
代码演示:
package com.jsp.demo03;
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: 史小创
* @Time: 2024/8/30 下午10:07
* @Description: 定义一个 `servlet` ,在该 `servlet` 中向 request 域对象中添加 键是 `status` ,值为 `1` 的数据
*/
@WebServlet("/jsp01")
public class JSPServlet01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 储存数据到reaquest域中
req.setAttribute("status", 1);
// 2. 转发到 jstl-if.jsp
req.getRequestDispatcher("/jstl-if.jsp").forward(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--
@Author: 史小创
@Time: 2024/8/30 下午10:09
@Description: 定义 `jstl-if.jsp` 页面,在该页面使用 `<c:if>` 标签
--%>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--
c:if:来完成逻辑判断,替换java if else
--%>
<c:if test="${status==1}">
启动
</c:if>
<c:if test="${status==0}">
禁用
</c:if>
</body>
</html>
http://localhost:8080/demo03/jsp01
4.2.1、forEach 标签
<c:forEach>
:相当于 for 循环。java中有增强for循环和普通for循环,JSTL 中的 <c:forEach>
也有两种用法
4.2.1.1、用法一
类似于 Java 中的增强for循环。涉及到的 <c:forEach>
中的属性如下
-
items:被遍历的容器
-
var:遍历产生的临时变量
-
varStatus:遍历状态对象
如下代码,是从域对象中获取名为 brands 数据,该数据是一个集合;遍历遍历,并给该集合中的每一个元素起名为 brand
,是 Brand对象。在循环里面使用 EL表达式获取每一个Brand对象的属性值
<c:forEach items="${brands}" var="brand">
<tr align="center">
<td>${brand.id}</td>
<td>${brand.brandName}</td>
<td>${brand.companyName}</td>
<td>${brand.description}</td>
</tr>
</c:forEach>
package com.jsp.demo03;
/**
* 品牌实体类
*/
public class Brand {
// id 主键
private Integer id;
// 品牌名称
private String brandName;
// 企业名称
private String companyName;
// 排序字段
private Integer ordered;
// 描述信息
private String description;
// 状态:0:禁用 1:启用
private Integer status;
public Brand() {
}
public Brand(Integer id, String brandName, String companyName, String description) {
this.id = id;
this.brandName = brandName;
this.companyName = companyName;
this.description = description;
}
public Brand(Integer id, String brandName, String companyName, Integer ordered, String description, Integer status) {
this.id = id;
this.brandName = brandName;
this.companyName = companyName;
this.ordered = ordered;
this.description = description;
this.status = status;
}
// getId ${brand.id} Id getId
public Integer getId() {
System.out.println("getId方法被调用了...");
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getBrandName() {
return brandName;
}
public void setBrandName(String brandName) {
this.brandName = brandName;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
public Integer getOrdered() {
return ordered;
}
public void setOrdered(Integer ordered) {
this.ordered = ordered;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
@Override
public String toString() {
return "Brand{" +
"id=" + id +
", brandName='" + brandName + '\'' +
", companyName='" + companyName + '\'' +
", ordered=" + ordered +
", description='" + description + '\'' +
", status=" + status +
'}';
}
}
package com.jsp.demo03;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* @Author: 史小创
* @Time: 2024/8/30 下午10:23
* @Description:
*/
@WebServlet("/jsp02")
public class JSPServlet02 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1. 准备数据
List<Brand> brands = new ArrayList<Brand>();
brands.add(new Brand(1,"三只松鼠","三只松鼠",100,"三只松鼠,好吃不上火",1));
brands.add(new Brand(2,"优衣库","优衣库",200,"优衣库,服适人生",0));
brands.add(new Brand(3,"小米","小米科技有限公司",1000,"为发烧而生",1));
//2. 存储到request域中
req.setAttribute("brands",brands);
req.setAttribute("status",1);
// 2.将数据储存在request请求中
req.setAttribute("brands", brands);
// 3.转发至jsp
// 此处需要用转发,因为转发才可以使用 request 对象作为域对象进行数据共享
req.getRequestDispatcher("/jstl-foreach.jsp").forward(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
package com.jsp.demo03;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* @Author: 史小创
* @Time: 2024/8/30 下午10:23
* @Description:
*/
@WebServlet("/jsp02")
public class JSPServlet02 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1. 准备数据
List<Brand> brands = new ArrayList<Brand>();
brands.add(new Brand(1,"三只松鼠","三只松鼠",100,"三只松鼠,好吃不上火",1));
brands.add(new Brand(2,"优衣库","优衣库",200,"优衣库,服适人生",0));
brands.add(new Brand(3,"小米","小米科技有限公司",1000,"为发烧而生",1));
//2. 存储到request域中
req.setAttribute("brands",brands);
req.setAttribute("status",1);
// 2.将数据储存在request请求中
req.setAttribute("brands", brands);
// 3.转发至jsp
// 此处需要用转发,因为转发才可以使用 request 对象作为域对象进行数据共享
req.getRequestDispatcher("/jstl-foreach.jsp").forward(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<table border="1" cellspacing="0" width="80%">
<tr>
<th>序号</th>
<th>品牌名称</th>
<th>企业名称</th>
<th>排序</th>
<th>品牌介绍</th>
<th>状态</th>
</tr>
<c:forEach items="${brands}" var="brand" varStatus="status">
<tr align="center">
<%--<td>${brand.id}</td>--%>
<td>${status.count}</td>
<td>${brand.brandName}</td>
<td>${brand.companyName}</td>
<td>${brand.ordered}</td>
<td>${brand.description}</td>
<c:if test="${brand.status == 1}">
<td>启用</td>
</c:if>
<c:if test="${brand.status != 1}">
<td>禁用</td>
</c:if>
</tr>
</c:forEach>
</table>
</body>
</html>
http://localhost:8080/demo03/jsp02
4.2.1.2、用法二
类似于 Java 中的普通for循环。涉及到的 <c:forEach>
中的属性如下
-
begin:开始数
-
end:结束数
-
step:步长
实例代码:
从0循环到10,变量名是 i
,每次自增1
<c:forEach begin="0" end="10" step="1" var="i">
${i}
</c:forEach>
五、MVC模式和三层架构
MVC 模式和三层架构是一些理论的知识,将来我们使用了它们进行代码开发会让我们代码维护性和扩展性更好。
5.1 、MVC模式
MVC 是一种分层开发的模式,其中:
-
M:Model,业务模型,处理业务
-
V:View,视图,界面展示
-
C:Controller,控制器,处理请求,调用模型和视图
控制器(serlvlet)用来接收浏览器发送过来的请求,控制器调用模型(JavaBean)来获取数据,比如从数据库查询数据;控制器获取到数据后再交由视图(JSP)进行数据展示。
MVC 好处:
-
职责单一,互不影响。每个角色做它自己的事,各司其职。
-
有利于分工协作。
-
有利于组件重用
5.2、三层架构
三层架构是将我们的项目分成了三个层面,分别是 表现层
、业务逻辑层
、数据访问层
。
- 数据访问层:对数据库的CRUD基本操作
- 业务逻辑层:对业务逻辑进行封装,组合数据访问层层中基本功能,形成复杂的业务逻辑功能。例如
注册业务功能
,我们会先调用数据访问层
的selectByName()
方法判断该用户名是否存在,如果不存在再调用数据访问层
的insert()
方法进行数据的添加操作 - 表现层:接收请求,封装数据,调用业务逻辑层,响应数据
而整个流程是,浏览器发送请求,表现层的Servlet接收请求并调用业务逻辑层的方法进行业务逻辑处理,而业务逻辑层方法调用数据访问层方法进行数据的操作,依次返回到serlvet,然后servlet将数据交由 JSP 进行展示。
5.3、MVC 和 三层架构
通过 MVC 和 三层架构 的学习,有些人肯定混淆了。那他们有什么区别和联系?
如上图上半部分是 MVC 模式,上图下半部分是三层架构。 MVC 模式
中的 C(控制器)和 V(视图)就是 三层架构
中的表现层,而 MVC 模式
中的 M(模型)就是 三层架构
中的 业务逻辑层 和 数据访问层。
可以将 MVC 模式
理解成是一个大的概念,而 三层架构
是对 MVC 模式
实现架构的思想。 那么我们以后按照要求将不同层的代码写在不同的包下,每一层里功能职责做到单一,将来如果将表现层的技术换掉,而业务逻辑层和数据访问层的代码不需要发生变化。
六、JSP 缺点
通过上面的案例,我们可以看到 JSP 的很多缺点。
由于 JSP页面内,既可以定义 HTML 标签,又可以定义 Java代码,造成了以下问题:
-
书写麻烦:特别是复杂的页面
既要写 HTML 标签,还要写 Java 代码
-
阅读麻烦
上面案例的代码,相信你后期再看这段代码时还需要花费很长的时间去梳理
-
复杂度高:运行需要依赖于各种环境,JRE,JSP容器,JavaEE…
-
占内存和磁盘:JSP会自动生成.java和.class文件占磁盘,运行的是.class文件占内存
-
调试困难:出错后,需要找到自动生成的.java文件进行调试
-
不利于团队协作:前端人员不会 Java,后端人员不精 HTML
如果页面布局发生变化,前端工程师对静态页面进行修改,然后再交给后端工程师,由后端工程师再将该页面改为 JSP 页面
由于上述的问题, ==JSP 已逐渐退出历史舞台,==以后开发更多的是使用 HTML + Ajax 来替代。Ajax 是我们后续会重点学习的技术。有个这个技术后,前端工程师负责前端页面开发,而后端工程师只负责前端代码开发。下来对技术的发展进行简单的说明
-
第一阶段:使用
servlet
即实现逻辑代码编写,也对页面进行拼接。这种模式我们之前也接触过 -
第二阶段:随着技术的发展,出现了
JSP
,人们发现JSP
使用起来比Servlet
方便很多,但是还是要在JSP
中嵌套Java
代码,也不利于后期的维护 -
第三阶段:使用
Servlet
进行逻辑代码开发,而使用JSP
进行数据展示
- 第四阶段:使用
servlet
进行后端逻辑代码开发,而使用HTML
进行数据展示。而这里面就存在问题,HTML
是静态页面,怎么进行动态数据展示呢?这就是ajax
的作用了。
那既然 JSP 已经逐渐的退出历史舞台,那我们为什么还要学习 JSP
呢?原因有两点:
- 一些公司可能有些老项目还在用
JSP
,所以要求我们必须动JSP
- 我们如果不经历这些复杂的过程,就不能体现后面阶段开发的简单
七、环境搭建
软件 | 版本 | 网址 |
---|---|---|
Maven | 3.8.4 | https://archive.apache.org/dist/maven/maven-3/3.8.4/binaries/ |
Tomcat | 9.0.29 | https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.29/bin/ |
JDK | 1.8.0_411 | https://www.oracle.com/java/technologies/javase/javase8u211-later-archive-downloads.html |
代码汇总:
https://github.com/shixiaochuangjob/markdownfile/tree/main/20240831
https://mp.weixin.qq.com/s?__biz=MzkwOTczNzUxMQ==&mid=2247485225&idx=1&sn=a1adb2bacfa135f2bbb8655a9082d275&chksm=c1376c61f640e57758c6a7f24cfcd581246baff9846b61b809aebfe05d2da18e5e00ffabdae5#rd