Jsp与Servlet之EL 与 JSTL
一、EL表达式基本语法
1.EL 表达式语法非常简单,它以“${“开头,以”}"结束,中间为合法的表达式,具体的语法格式如下:
${expression}
expression:用于指定要输出的内容,可以是字符串,也可以是由 EL 运算符组成的表达式。在 EL 表达式中要输出一个字符串,可以将此字符串放在一对单引号或双引号内。
注意:由于 EL 表达式的语法以"KaTeX parse error: Expected '}', got 'EOF' at end of input: …如果在 JSP 网页中要显示"{" 字符串,必须 在前面加上\”符号,即"${“来输出”${" 符号。
2.EL 的保留关键字
在为变量命名时,应该避免使用这些关键字,包括使用 EL输出已经保存在作用城范围内的变量,也不能使用关键字,如果已经定义了,那么需要修改为其他 的变量名。
gt eq or and div empty instanceof false ge le It not
3.通过 EL 访问数据
通过 EL 提供的“[]”和“.”运算符可以访问数据。通常情况下,“[]“和“.”运算符是等价的,可以相互代替。
${user.id}
${user['.name']}
但是也不是所有情况下都可以相互替代,例如,当对象的属性名中包括一些特殊的符号(-或.)时,就只能使用“[”运算符来访问对象的属性。例如,${userInfo[‘user-id’]} 是正确的,而 ${userIfo.user-name} 则是错误的。另外,EL 的“[”运算符还有一个用途,就是用来获取数组或者 List 集合中的数据,下面进行详细介绍。
4.数组元素的获取
应用“[”运算符可以获取数组的指定元素,但是“.”运算符则不能。
${arrBook[0]}
案例:
在 request 域中存入四本书,使用循环将编号和书名输出到页面上。
<%
String[] arr = {"java", "ui", "大数据", "新媒体"};
%>
<%
for (String s : arr) {
request.setAttribute("b", s);
%>
<p>${b}</p>
<%
}
%>
在 EL 中,也可以进行算术运算,同 Java 语言一样,EL 提供了加、减、乘、除和求余 5 种算术运 算符。
+ - * /(div) %(mod)
5.EL 判断非空
在 EL 中,判断对象是否为空,可以通过 empty 运算符实现,该运算符是一个前缀(prefix) 运算符,即 empty 运算符位于操作数前方,用来确定一个对象或变量是否为 null 或空。另外,empty 运算符也可以与 not 运算符结合使用,用于判断一个对象或变量是否为非空。
${empty expression}
${not empty expression}
6.EL 运算符
在 EL 中,提供了 6 种关系运算符。这6种关系运算符不仅可以用来比较整数和浮点数,还可以用来比较字符串。关系运算符的使用格式如下:
==或eq 等于
!=或ne 不等于
<或lt 小于
>或gt 大于
<=或le 小于等于
>=或ge 大于等于
在进行比较运算时,如果涉及两个或两个以上的判断条件时,就需要应用逻辑运算符。逻辑运算符的条件表达式的值必须是 Boolean 型或是可以转换为Boolean 型的字符串,并且返回的结果也是 Boolean 型。
&&或and 与
||或or 或
!或not 非
7.EL 字符串类型的变量拼接时,不能使用 +,需要使用 concat() 方法。
${"2"+"1"} 得到结果为3
${"hello"+" world!"} 报错 java.lang.NumberFormatException: For input string: "hello"
${"hello".concat(" world")} 得到结果为hello world
二、EL 隐含对象
在 EL 中提供了 4 个用于访问作用域范围的隐含对象,即 pageScope、requestScope、sessionScope 和 applicationScope。运用这 4 个隐含对象指定所要查找的标识符的作用域后,系统将不再按照默认的顺序 page、request、session 及 application 来查找相应的标识符。它们与 JSP 中的 page、request、session 及 application 内置对象类似。只不过这 4 个隐含对象只能用来取得 setAttribute 范围内的属性值,而不能取得其他相关信息。
<%
pageContext.setAttribute("id", 8);
session.setAttribute("id", 2);
request.setAttribute("id", 1);
application.setAttribute("id", 3);
%>
${sessionScope.id}
为了能够获得 Web 应用程序中的相关数据,EL 提供了多个隐含对象,这些对象类似于 JSP 的内置对象,也是直接通过对象名进行操作。在 EL 的隐含对象中,除 pageContext 是 JavaBean 对象,其他的隐含对象都对应于 java.util.Map 类型。
pageContext 可以访问 JSP 内置对象(如 request、response、out、session、exception 和 page 等。在获取到这些内置对象后,就可以获取其属性值。这些属性与对象的 getXXX() 方法相对应,在使用时,去掉方法名中的 get,并将首字母改为小写即可。
1.访问 request 对象
通过pageContext获取JSP内置对象中的request对象,可以使用下面的语句:
${pageContext.request}
获取到 request 对象后,就可以通过该对象获取与客户端相关的信息。例如,HTTP 报头信息、客户信息提交方式、客户端主机 IP 地址和端口号等。
${pageContext.request.severPort}
2.访问 response 对象
通过 pageContext 获取JSP内置对象中的 response 对象,可以使用下面的语句:
${pageContext.response}
获取到 response 对象后,就可以通过该对象获取与响应相关的信息。 ${pageContext.response.contentType}这句代码将返回响应的内容类型。
3.访问 out 对象
通过 pageContext 获取 JSP 内置对象中的 out 对象,可以使用下面的语句:
${pageContext.out}
获取到 out 对象后,就可以通过该对象获取与输出相关的信息。 ${pageContext.out.bufferSize } 这句代码将返回输出缓冲区的大小。
4.访问 session 对象
通过 pageContext 获取JSP内置对象中的 session 对象,可以使用下面的语句:
${pageContext.session}
获取到 session 对象后,就可以通过该对象获取与session相关的信息。 ${pageContext.session.maxlnactiveInterval} 这句代码将返回 session 的有效时间。
5.访问 exception 对象
通过 pageContext 获取JSP内置对象中的 exception 对象,可以使用下面的语句:
$(pageContext.exception}
获取到 exception 对象后,就可以通过该对象获取 JSP 页面的异常信息。 ${pageContext.excepion.message}
6.访问 page 对象
通过 pageContext 获取 JSP 内置对象中的 page 对象,可以使用下面的语句:
${pageContext.page}
获取到 page 对象后,就可以通过该对象获取当前页面的类文件。${pageContext.page.class} 这句代码将返回当前页面的类文件。
7.访问 servletContext 对象
通过 pageContext 获取 JSP 内置对象中的 servletContext 对象,可以使用下面的语句: ${pageContext.servletContext}
获取到 servletContext 对象后,就可以通过该对象获取 servlet 上下文信息。 ${pageContex.request.contextPath} 这句代码将返回当前页面的上下文路径。
8.param 可以获取请求里面的参数。如:${param.id} 可以获取请求 XXX?id=12 的这种参数。
三、相对路径与绝对路径
绝对路径:绝对路径就是你的主页上的文件或目录在硬盘上真正的路径,(URL和物理路径)
例如: C://xyz/test.txt 代表了 test.txt 文件的绝对路径。http://www.sun.com/index.html 也代表了一个 URL 的绝对路径。
相对路径:相对与某个基准目录的路径。包含 Web 的相对路径(HTML 中的相对目录),例如:在 Servlet 中,"/"代表 Web 应用的跟目录。和物理路径的相对表示。
例如:"./" 代表当前目录,"…/"代表上级目录。这种类似的表示,也是属于相对路径。
注意 JSP/Servlet 中的相对路径分为服务端和客户端路径。
1.服务器端的地址
服务器端的相对地址指的是相对于 web 应用的地址,这个地址是在服务器端解析的(不同于 html 和 javascript 中的相对地址,他们是由客户端浏览器解析的)也就是说这时候在 jsp 和 servlet 中的相对地址应该是相对于 web 应用,即相对于 http: //192.168.0.1/webapp/ 的。
其用到的地方有:
转发:servlet 中的 request.getRequestDispatcher(address);这个 address 是在服务器端解析的,所以,要转发到 jsp/a.jsp 应该这么写:request.getRequestDispatcher(“/jsp/a.jsp”) 前面 / 相对于当前的 web 应用 webapp,其绝对地址就是:http://192.168.0.1/webapp/jsp/a.jsp。
重定向:在 jsp 或 Servlet 中 response.sendRedirect("/上下文路径/jsp/a.jsp");
2.客户端的地址
所有的 html 页面中的相对地址都是相对于服务器根目录(http://192.168.0.1/)的,而不是(跟目录下的该 Web 应用的目录)http://192.168.0.1/webapp/ 的。 Html 中的 form 表单的 action 属性的地址应该是相对于服务器根目录(http://192.168.0.1/)的,所以,如果提交到 a.jsp 为:action="/webapp/user/a.jsp"或 action=“上下文路径/user/a.jsp; 提交到 servlet 为 actiom=”/webapp/handleservlet" Javascript 也是在客户端解析的,所以其相对路径和 form 表单一样。 因此,一般情况下,在 JSP/HTML 页面等引用的 CSS,Javascript 等属性前面最好都加上上下文路径,以确保所引用的文件都属于 Web 应用中的目录。另外,应该尽量避免使用类似"."、"./"、"…/…/" 等类似的相对该文件位置的相对路径,这样当文件移动时,很容易出问题。
<img src="${pageContext.request.contextPath}/img/1.jpg">
<form action="${pageContext.request.contextPath}/login">
<input type="submit" value="提交">
</form>
四、ajax 提交数据
ajax 提交数据一般需要 jquery,而且页面不再使用 submit 类型的按钮,数据在点击按钮时便会使用 jquery 发送请求。发送的数据会找到对应的 Servlet 获得后台返回的数据,并且调用回调方法将数据放在 response 变量中。
<form>
<input name="name" id="name">
<input name="password" id="password">
<p id="msg"></p>
<input type="button" value="提交" onclick="sub()">
</form>
<script>
function sub(){
let name = $("#name").val();
let password = $("#password").val();
//回调
$.post("login", {name, password}, function (response) {
$("#msg").html(response)
})
}
</script>
LoginServlet
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("utf-8");
String name = req.getParameter("name");
String password = req.getParameter("password");
System.out.println(name);
System.out.println(password);
resp.getWriter().write("登录成功");
}
}
案例:全 HTML 的登录退出系统
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
</head>
<body>
<h1>欢迎<span id="user"></span>登录!</h1>
<a href="logout">退出登录</a>
<script>
$.post("userInfo", {}, function (response) {
$("#user").html(response)
})
</script>
</body>
</html>
login.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
</head>
<body>
<form>
<input name="name" id="name">
<input name="password" id="password">
<p id="msg"></p>
<input type="button" value="提交" onclick="sub()">
</form>
<script>
function sub() {
let name = $("#name").val();
let password = $("#password").val();
//回调
$.post("login", {name, password}, function (response) {
if (response.trim() == '登录成功') {
location.href = "index.html";
} else {
$("#msg").html(response)
}
})
}
</script>
</body>
</html>
loginServlet
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("utf-8");
String name = req.getParameter("name");
String password = req.getParameter("password");
if ("123".equals(password)) {
req.getSession().setAttribute("userInfo", name);
resp.getWriter().println("登录成功");
} else {
resp.getWriter().println("账号或密码错误");
}
}
}
LogoutServlet
@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.getSession().invalidate();
resp.sendRedirect("login.html");
}
}
UserInfoServlet
@WebServlet("/userInfo")
public class UserInfoServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("utf-8");
resp.getWriter().println(req.getSession().getAttribute("userInfo"));
}
}
LoginFilter
@WebFilter("/*")
public class LoginFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
if (request.getRequestURI().contains("login")) {
filterChain.doFilter(request, response);
return;
}
if (request.getSession().getAttribute("userInfo") != null) {
filterChain.doFilter(request, response);
return;
}
response.sendRedirect("login.html");
}
@Override
public void destroy() {
}
}
五、JSTL 常用标签库
JSTL 全称为 JSP Standard [ˈstændəd] Tag Library [ˈlaɪbrəri]即 JSP 标准标签库。JSTL 作为最基本的标签库,提供了一系列的 JSP 标签,实现了基本的功能:集合的遍历、数据的输出、字符串的处理、数据的格式化等等。使用 JSTL 标签库步骤如下:
1.导入 jstl-1.2.jar 开发包。
2.在 JSP 页面中用 tablib 指令引入需要用到的 JSTL 标签。
core 标签库
core 标签库是 JSTL 的核心标签库,实现了最基本的功能:流程控制、迭代输出等操作。core 标签库的前缀一般是 c
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
c:out
案例:
<%
request.setAttribute("name", "zhongfucheng");
%>
//<c:out/>标签支持标签体,default属性上的数据可以写在标签体中
//<c:out value="${name}" escapeXml="true">您要的数据找不着</c:out>
<c:out value="${name}" default="您要的数据找不着" escapeXml="true"/>
我们发现上面的代码实现的效果和 EL 表达式是一样的,它出色的地方就多了两个属性 default 和 escapeXml [ɪˈskeɪp]属性。如果我们用到这两个属性,我们就使用该标签,如果没有用到这两个属性就用 EL 表达式就可以了。
c:if
JSTL 提供了 if 标签完成分支语句的实现,test 属性是不可或缺的。 var 和 scope 属性我看来好像没什么用的(保存执行结果有什么用?)
案例:
<%--如果带过来的名字是zhongfucheng,那么可以登陆--%>
<c:if test="${param.name=='zhongfucheng'}">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="登陆">
</c:if>
c:choose
if 标签没有 else 的功能,如果需要类似于 java 中的 if else 流程就需要使用 choose 标签。 choose 标签需要联合 when 和 otherwise 标签一起使用!
案例:
<c:choose>
<c:when test="${param.name=='zhongfucheng'}">
你好啊,zhongfucheng
</c:when>
<c:when test="${param.name=='ouzicheng'}">
你好啊,ouzicheng
</c:when>
<c:otherwise>
你是谁啊?别随便过来!
</c:otherwise>
</c:choose>
c:forEach
forEach 为循环标签,相当于 Java 中的 while 和 for
案例:
<%
List list = new ArrayList<>();
list.add("zhongfucheng");
list.add("ouzicheng");
list.add("xiaoming");
request.setAttribute("list", list);
%>
<c:forEach var="list" items="${list}" >
${list}<br>
</c:forEach>
遍历Map对象有稍微地不一样,我们来看一下,var 属性保存的不是每个迭代的对象,而是 Map.Entry。
<%
Map map = new HashMap();
map.put("1", "zhongfucheng");
map.put("2", "xiaohong");
map.put("3", "xiaoming");
request.setAttribute("map",map);
%>
<c:forEach var="me" items="${map}" >
${me.key} ${me.value}<br>
</c:forEach>
begin 默认从 0 开始、end 默认为集合的最后一个元素、step 默认为 1
案例:
<c:forEach var="i" begin="1" end="10" step="1">
<c:out value="${i}" /><br>
</c:forEach>
varStatus 代表着当前对象被迭代的信息,它有以下的属性。
案例:
<c:forEach var="list" items="${list}" varStatus="varStatus" >
${list}您的下标是:${varStatus.index}<br>
</c:forEach>
练习:
1.创建项目 demo8,使用安全 jsp 与 servlet 完成用户登录(登录时账号密码回填)。登录成功后才能上传图片的图片管理系统。上传图片的操作在 servlet 中操作即可,登录用户采用前面案例的数据库表。再增加一张 image 表里面的字段有:id、original_file_name(原始名字)、relative_paths(相对地址),create_time(上传时间),user_id (上传用户的id)。
2.用户搜索分页练习:创建项目 user-test,准备如下测试数据 100 条,使用注解 Servlet 与 jsp 结合的方式完成单表的分页和搜索,搜索时要保证也能分页,每页默认显示 15 条数据。暂时不用完成添加、修改和删除,大致效果如下:
步骤如下:
一、根据页面设计数据表并编写实体。
二、根据图中显示编写 dao 层业务逻辑(1.分页查询集合。2.根据名字的模糊匹配分页查询集合。3.查询全部数量。4.根据名字模糊查询数量)。
三、根据设计图编写 service 层代码,同时封装分页对象返回 Servlet 层,方便 Servlet 与 jsp 配合渲染页面。
四、jsp 与 Servlet 相互配合完成分页及条件查询。
3.阅读项目:智原用户系统(zy-user-system)。参照码云上的项目,完成相应的操作。码云地址
第一步:下载对应数据库脚本分析数据结构。
第二步:下载项目源码并,测试相关的数据方法 dao 层是否可用,及其作用。
第三步:查看相应的 jsp 页面所用到的 jstl 和 el 的语法。
第四步:根据浏览器的请求路径查看对应 servlet 中所做的事。
第五步:参照该项目的写法完成一个简单的员工管理业务,员工 employee(id 自增主键,name 姓名,age 年龄,dept 部门,create_time 创建时间,update_time 修改时间,add_user 添加的用户名,update_user 最后修改用户的用户名)。
六、JSTL 扩展标签库(自)
c:forTokens
该标签类似于 String 类的 split() 和 for 循环的一种集合 它与 forEach 标签非常相似,都有 begin、end、step、items、var、varStatus 属性,不同的是forTokens 标签的 items 属性里面是字符串,这个字符串会被 delims 属性的内容分割成多个字符串!
案例:
<c:forTokens items="zhongfucheng,ouzicheng,xiaoming,xiaohong" var="name" delims="," >
${name}
</c:forTokens>
c:set
该标签有5个属性,用起来有稍微有些复杂了!现在要记住的就是:var属性操作的是Integer、Double、Float、String等类型的数据,target属性操作的是JavaBean或Map对象的数据,scope代表的是Web域,value是值,property是对象的属性!
使用 var 属性
既然var属性只能操作Integer、Double、String等类型,那么存在var属性就一定没有property属性(property代表的是对象的成员属性,Integer、String这些类型哪来的成员变量呀)
下面的案例是这样的:创建了一个name的变量,设置的值为zhongfucheng,范围是page
<c:set var="name" value="fucheng" scope="page"/>
${name}
当然了,set标签也支持标签体,value的值可以写在标签体里边
<c:set var="name" scope="page">
zhongfucheng
</c:set>
使用 var 属性和 scope 属性实现计数器
<%--由于下面变量需要做加法运算,所以要定义出来,不然服务器是不知道我的变量是Integer类型的--%>
<%
Integer sessionCount = 0;
Integer applicationCount = 0;
%>
<c:set var="sessionCount" value="${sessionCount+1}" scope="session"/>
<c:set var="applicationCount" value="${applicationCount+1}" scope="application"/>
使用 target 属性与之配对的是 property 属性,target 属性只能操作 JavaBean 或 Map 对象,property 就是对应的成员变量或 key 了。。
既然 target 属性操作的是 JavaBean或Map对象,那么一定是通过EL表达式来获取到对象了。taget属性如果获取不到数据会抛出异常!使用target属性就一定没有scope属性(scope属性代表的是保存范围,target的值都是获取来的,难道你还能改变人家的范围?)
<%--创建出JavaBean对象,设置为session范围的属性--%>
<jsp:useBean id="person" scope="session"/>
<%--获取到person对象,设置age属性的值为32--%>
<c:set target="${person}" property="age" value="32"/>
${person.age}
c:remove
remove标签就相当简单了,只有 var 和 scope 属性,代表的是删除域范围的属性
案例:
<%--创建出JavaBean对象,设置为session范围的属性--%>
<jsp:useBean id="person" scope="session"/>
<%--获取到person对象,设置age属性的值为32--%>
<c:set target="${person}" property="age" value="32"/>
${person.age}
<br>
<%--删除session属性--%>
<c:remove var="person" scope="session"></c:remove>
${person.age==null?"存在session的person对象被删除了!":"我还在呢!"}
c:catch
该标签主要用来处理程序中产生的异常。 catch标签也十分简单,只有一个var属性,var属性封装了异常的信息!
<%--创建出JavaBean对象,设置为session范围的属性--%>
<jsp:useBean id="person" scope="session"/>
<c:catch var="message">
<%--target属性只能是EL表达式,现在我是字符串,获取不到对象,肯定会抛出异常的!--%>
<c:set target="person" property="age" value="32"/>
</c:catch>
${message}
fmt 标签
jsp 页面需引入 fmt 标签:
<taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt">
使用如下代码将字符串型日期转换为日期型 。
<fmt:parseDate value="${personMaster.masterDate}" pattern="yyyy-MM-dd" var="masterDate"/>
使用如下代码对日期进行格式化。
<fmt:formatDate value="${masterDate}" pattern="yyyy-MM-dd" timeZone="GMT+08:00"/>
fn 标签
jsp 页面需引入 fn 标签:
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
使用如下代码将字符串按照逗号切割成数组并遍历 。
<c:set value="${ fn:split('a,b,c,d,', ',') }" var="arr"/>
<c:forEach items="${arr}" var="s">
<p>${s}</p>
</c:forEach>
项目考核:(5天)
博客系统 blog-system
新闻系统 news-system
项目展示系统 project-display-system
内容管理系统 content-management-system
旅游景区管理系统 travel-system
1.挑选适当的前端页面模板,页面至少包含如下前后台需求,并将其汉化。根据模板设计数据库表(数据库设计文档,在设计时一般增加创建时间、最后修改时间、最后修改人编号、上下线、排序数字等)
2.使用面向对象开发思想完成项目开发,并使用如下的包结构 servlet(控制层),service(服务层),dao(数据库访问层),entity(模型),filter(过滤器层),util(工具类层)
3.挑选合适的后台管理页面模板,对所有的数据表进行增删改查,路径统一放在 /WEB-INF/jsp/admin/ 目录下,所有后台 Servlet 也加上父路径,如:/admin/index。
4.做好相应的权限控制,保证后台管理页面在未登录的情况下不能访问。
前台需求:
1.展示博客列表
2.展示博客详情
3.记录博客评论,注册,登录情况下可以评论
4.留言联系
管理需求:
1.系统大数据统计(博客总数量、上线数量、浏览量 top3、注册人数统计)
2.用户管理(删除,重置密码,锁定和启用)
3.博客管理(增加、删除、修改、上下线,评论管理)
4.留言管理(查看)
最后提交文件:
1.原项目模板
2.数据库设计文档(参考文档)
3.源代码和数据库脚本
4.可运行的项目及脚本
5.专业视频(介绍项目使用,从启动脚本开始演示)
6.专业 ppt