持续学习&持续更新中…
学习态度:守破离
EL表达式&JSTL标签库_转发&重定向_Servlet与JSP的对比及合作_路径
EL表达式、JSTL标签库
什么是EL表达式、JSTL标签库
下载JSTL核心标签库
EL和JSTL实例
引入JSTL标签:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@taglib prefix="f" uri="http://java.sun.com/jsp/jstl/fmt" %>
使用:
<c:if test="${not empty customer}">
<%-- 隐藏域 --%>
<input type="hidden" name="id" value="${customer.id}">
</c:if>
<c:choose>
<c:when test="${empty user.photo}">
<img src="${ctx}/asset/admin/img/noimage.png" alt="">
</c:when>
<c:otherwise>
<img src="${ctx}/${user.photo}" alt="">
</c:otherwise>
</c:choose>
<td><f:formatDate pattern="yyyy-MM-dd" value="${education.beginDay}"/></td>
<td><f:formatDate pattern="yyyy-MM-dd" value="${education.endDay}"/></td>
<input type="date" name="birthday" value='<f:formatDate pattern="yyyy-MM-dd" value="${user.birthday}"/>'>
<c:forEach var="education" items="${educations}">
<td>${education.typeString}</td>
<td>${education.intro}</td>
</c:forEach>
利用EL表达式从JSP域对象中获取数据
域对象:作用域对象
四大scope:
<input type="text" value="${pageScope.name}">
<input type="text" value="${requestScope.name}">
<input type="text" value="${sessionScope.name}">
<input type="text" value="${applicationScope.name}">
对应的Java代码:
<% pageContext.getAttribute("name"); %>
<% request.getAttribute("name"); %>
<% session.getAttribute("name"); %>
<% application.getAttribute("name"); %>
EL表达式获取请求参数:
<input type="text" value="${param.name}">
对应的Java代码:
<% request.getParameter("name"); %>
注意
如果不写scope的话,那么:
- EL表达式先去pageContext中取,如果取不到。
- EL表达式然后去request中取
${user}
==request.getAttribute("user")
${user.name}
==request.getAttribute("user").getName()
- request中取不到的话,就去session中去取
${user}
==session.getAttribute("user")
${user.name}
==session.getAttribute("user").getName()
- session中取不到的话,就去application中去取
EL表达式中的${obj.property},property指的是属性,而不是obj这个对象所对应类的某个成员变量
举例:
private Integer type;
public Integer getType() {
return type;
}
public void setType(Integer type) {
this.type = type;
}
public String getTypeString() {
String typeString = "其他";
switch (type) {
case 1:
typeString = "小学";
break;
case 2:
typeString = "初中";
break;
case 3:
typeString = "高中";
break;
case 4:
typeString = "中专";
break;
case 5:
typeString = "大专";
break;
case 6:
typeString = "本科";
break;
case 7:
typeString = "硕士";
break;
case 8:
typeString = "博士";
break;
}
return typeString;
}
<td>${education.typeString}</td>
转发(forward)
因为转发并不会响应浏览器,所以,转发后,浏览器地址栏中的url不会改变(浏览器是不知道发生了什么的),依然会是首次请求的url。又由于一次请求并没有结束,所以转发需要使用request进行操作。
转发只能在同一个项目(context)下进行,并且是由服务器来完成操作的,因此转发时使用相对路径即可,不用加context
转发时的相对路径是相对于此时浏览器中的路径的。
转发是同一次完整的请求,一次请求就是一个request,一个request就像下图那样,当把绿色或橙色这个请求和响应流程结束后,它才会消逝。
一个request可以被转发多次,但是每次都会创建新的request。具体看下图:
重定向(redirect)
重定向要先给浏览器一次响应,因此要使用response操作。
重定向的path是交给浏览器去重新请求的。因此需要给浏览器说明context path
重定向可以让浏览器重定向至http://host:port/context/...
这种完整路径,也可以让其重定向至/context/...
这种不完整路径(一个context就代表一个项目),如果给浏览器的是完整路径,那么浏览器就会直接请求该完整路径;如果给浏览器是不完整路径,那么浏览器默认会在该台服务器上去寻找该项目,也就是说,浏览器会在不完整路径之前拼接上http://host:post/
重定向的路径是由客户端(浏览器)发出的新的请求,凡是由客户端发出的新的请求(包括项目中jsp、html页面中的表单标签的action),当客户端发请求时,发现拿到的path是以/开头的,客户端只会通过http://host:port/找到该台Tomcat服务器,并不能知道要访问服务器下的哪个项目,因此需要告诉浏览器context path,向浏览器说明清楚需要访问哪一个项目。
转发与重定向
-
转发:由服务器完成操作,只需要告诉相对路径(相对于浏览器此时地址栏中的URL)
-
重定向:由客户端(浏览器)完成操作,需要告诉绝对路径或者需要告诉context path(比如HTML中的URL)
利用Servlet+JSP实现一个简单的客户关系管理系统
简述:首先访问用户列表页面,然后,用户可以点击添加,点击添加后跳转至添加用户界面,添加完成以后,点击保存,再次出现用户列表页面。
实现效果图
具体代码实现
Customer
public class Customer {
private String name;
private Integer age;
private Float tall;
public Customer(String name, Integer age, Float tall) {
this.name = name;
this.age = age;
this.tall = tall;
}
public Customer() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Float getTall() {
return tall;
}
public void setTall(Float tall) {
this.tall = tall;
}
@Override
public String toString() {
return "Customer{" +
"name='" + name + '\'' +
", age=" + age +
", tall=" + tall +
'}';
}
}
一些代码规范
- 编写JavaBean时,字段使用对象类型能够很好的表达值不存在这个概念。因此建议使用对象类型,不建议使用基本类型。
在Java后台开发中,编写JavaBean时推荐将基本数据类型写为包装类型。
究其原因是因为:
如果使用对象类型的话,当某field为null时(某字段不存在数据时)也可以直接使用该字段。
而使用基本数据类型的话,则无法来表述某个字段为null的场景。
比如有个温度字段,该如何表示温度不存在的情况?
就算可以使用一些规范来约定,也会很麻烦,不如直接使用包装类型
DataManage
public final class DataManage {
private DataManage() {
}
private static final List<Customer> list = new ArrayList<>();
static {
for (int i = 0; i < 10; i++) {
list.add(new Customer("张三" + i, 18 + i, (((i & 1) == 1)) ? 1.7f + i : 1.6f + i));
}
}
public static void addCustomer(String name, Integer age, Float tall) {
list.add(new Customer(name, age, tall));
}
public static List<Customer> getCustomers() {
return list;
}
}
UserListServlet
@WebServlet("/userlist")
public class UserListServlet extends HttpServlet {
private static final String JSP_USERLIST = "pages/userlist.jsp";
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取数据
List<Customer> customers = DataManage.getCustomers();
request.setAttribute("customers", customers);
// 请求转发时是由服务器操作的,知道是在哪个项目中,因此写相对路径即可
request.getRequestDispatcher(JSP_USERLIST).forward(request, response);
}
}
userlist.jsp
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>user list</title>
<style>
th, td {
border: 1px solid black;
}
</style>
</head>
<body>
<!-- a标签跳转至某个页面也是由浏览器执行的,因此需要写context path -->
<a href="/five_crms/pages/adduser.jsp">添加</a>
<table>
<thead>
<tr>
<th>姓名</th>
<th>年龄</th>
<th>身高</th>
</tr>
</thead>
<tbody>
<c:forEach items="${customers}" var="customer">
<tr>
<td>${customer.name}</td>
<td>${customer.age}</td>
<td>${customer.tall}</td>
</tr>
</c:forEach>
</tbody>
</table>
</body>
</html>
adduser.jsp
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>add user</title>
</head>
<body>
<!-- form表单是由浏览器进行请求的,只知道在哪台服务器上,因此需要写上项目context -->
<form action="/five_crms/adduser" method="post">
<div>姓名:<input type="text" name="username"></div>
<div>年龄:<input type="text" name="userage"></div>
<div>身高:<input type="text" name="usertall"></div>
<br>
<br>
<br>
<div>
<button type="submit">保存</button>
</div>
</form>
</body>
</html>
AddUserServlet
@WebServlet("/adduser")
public class AddUserServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
DataManage.getCustomers().add(new Customer(
request.getParameter("username"),
Integer.valueOf(request.getParameter("userage")),
Float.valueOf(request.getParameter("usertall"))
));
// 请求重定向是通知浏览器对某个路径进行重新请求,浏览器只知道在哪台服务器上,因此需要写上项目context
response.sendRedirect("/five_crms/userlist");
// 第二种方式实现重定向 不推荐使用
// response.setStatus(302); // 状态码
// response.setHeader("Location","/five_crms/userlist"); // 响应头Location
}
}
Servlet与JSP的对比及合作
Servlet处理请求的常见过程及弊端
弊端:
-
Servlet中有大量的字符串拼接代码。
-
Servlet即需要和数据打交道,又需要和界面打交道。
-
如果响应给客户端的HTML中需要引入CSS文件呢?这时也只能字符串拼接。
JSP处理请求的常见过程及弊端
弊端:
-
Java和HTML混合在一起,可读性特别差
-
前后端不分离,难以维护
-
依然没有从根本上解决字符串拼接问题
-
变得比直接使用Servlet书写更麻烦,更难以维护(Servlet只需要拼接字符串)。
Servlet+JSP处理请求的常见过程及优势
优势:
-
Servlet负责调度,负责业务逻辑的控制
-
JSP负责页面显示(使用JSTL、EL)。
-
可读性高、易维护、条理清晰。
Servlet+JSP合作开发
目标
-
Java文件中不拼接任何一句HTML,只写Java代码。(让Servlet只控制业务)
-
JSP中不编写一句Java代码,只写标签。
实现
UserListServlet.java:
@WebServlet("/userlist")
public class UserListServlet extends HttpServlet {
private static final String JSP_USERLIST = "pages/userlist.jsp";
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 如果需要从浏览器读取信息的话需要设置请求的字符集编码
// 设置字符集编码
// request.setCharacterEncoding("UTF-8");
// 响应的字符编码是没有必要设置的,因为Servlet只负责业务控制,和数据打交道,不负责展示
// response.setContentType("text/html;charset=UTF-8");
// 获取客户数据
List<Customer> customers = getDataList();
// 将客户数据存储到request中
request.setAttribute("customers", customers);
// 转发到userlist.jsp页面进行数据展示
// 转发时,路径默认是从该项目的webapp下开始寻找的
/*
转发只能在同一个Context中进行转发,也就是说Serlet只会在同一个项目中进行转发。
既然大家都在同一个项目下,因此这儿写相对路径即可。
该相对路径前面会自动被拼接上context path
*/
request.getRequestDispatcher(JSP_USERLIST).forward(request, response);
}
/*实际开发中获取数据肯定是通过数据库来获取的*/
private List<Customer> getDataList() {
List<Customer> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(new Customer("张三" + i, 18 + i, (((i & 1) == 1)) ? 1.7f + i : 1.6f + i));
}
return list;
}
}
userlist.jsp:
<%-- page指令下的contentType相当于response.setContentType --%>
<%-- 因为JSP的本质就是Servlet --%>
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%-- 因为是core库,所以prefix简写为c即可 --%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>user list</title>
<style>
th, td {
border: 1px solid black;
}
</style>
</head>
<body>
<a href="">添加</a>
<table>
<thead>
<tr>
<th>姓名</th>
<th>年龄</th>
<th>身高</th>
</tr>
</thead>
<tbody>
<%--
items中的${argument}相当于
通过request.getAttribute("argument")
得到request的attribute中的对象
${customer.name}相当于customer.getName()
--%>
<c:forEach items="${customers}" var="customer">
<tr>
<td>${customer.name}</td>
<td>${customer.age}</td>
<td>${customer.tall}</td>
</tr>
</c:forEach>
</tbody>
</table>
</body>
</html>
路径
http://host:port/context
对应项目中的webapp目录
相对路径
-
http://host:port/context/
对应webapp目录
-
@WebServlet("/list*"),此时服务器处于根目录(/ 对应 webapp目录)
-
http://localhost:8080/crms/customer/list
对应webapp/customer目录下的list
-
@WebServlet("/customer/list") 此时服务器处于根目录下的customer目录(/customer 对应 webapp/customer目录)
-
转发时,相对的是浏览器此时地址栏中的路径
-
使用forward转发时,getRequestDispatcher中只能填写相对路径。有两种方式:
// ../的意思是跳转至webapp目录下(相对于浏览器此时地址栏的路径)
// 假设此时浏览器地址栏:http://localhost:8080/lp_resume/education/admin
// 此时处于根目录下的education目录,那么../就会跳转到根目录
req.getRequestDispatcher("../WEB-INF/error.jsp").forward(req, resp);
// 使用/代表根目录(webapp)
req.getRequestDispatcher("/WEB-INF/error.jsp").forward(req, resp);
绝对路径
-
以
/context_path
开头的是绝对路径 -
直接带上盘符的路径和直接带上context path的路径。
-
重定向只能使用绝对路径,两种方式:
// 由于context path有可能会改变,因此使用动态context path
resp.sendRedirect(req.getContextPath() + "/company/admin");
resp.sendRedirect("http://localhost:8080/lp_resume/company/admin");
JSP文件中路径的使用
-
JavaWeb开发中,jsp和html中的路径应该使用以
/context_path
开头的绝对路径 -
由于context path有可能会更改,所以应该使用动态的context path
- include指令中的路径使用相对路径
include指令的本质是copy
<%@ include file="common/head.jsp" %>
注意和一些细节
-
表单中"name"存在时,
request.getParameter("name")
在用户没有输入的情况下会返回空字符串 -
name不存在时,
request.getParameter("name")
为null。
-
JSP的本质是Servlet,JSP中的JSTL和EL表达式同样也会被编译为Servlet,最终都是由Servlet输出一个HTML网页响应给浏览器的。
-
EL表达式中的Bean对象默认都会在Java中调用
toString()
转化为字符串发送给浏览器,因此我们可以在BaseBean中添加一个getJson()方法,用于将一个JavaBean对象转为JSON,然后发送给浏览器,浏览器中的JSON可以直接被解析为JS对象使用。
@JsonIgnore
public String getJson() {
try {
return Jsons.objectMapper().writeValueAsString(this).replace("\"", "'");
} catch (JsonProcessingException e) {
return null;
}
}
<button type="button" onclick="edit(${company.json})"><span>编辑</span></button>
function add() {
$addSaveForm[0].reset()
addFormBox.modal()
}
function edit(json) {
add()
for (const k in json) {
$addSaveForm.find('[name=' + k + ']').val(json[k])
}
}
参考
李明杰: Java从0到架构师②JavaEE技术基石.
本文完,感谢您的关注支持!