一. 引言
1.1 现有问题
- 在之前学习Servlet时, 服务器通过Servlet响应客户端页面, 有什么不足之处?
- 开发方式麻烦: 继承父类, 覆盖方法, 配置web.xml或注解
- 代码修改麻烦: 重新编译, 部署, 重启服务
- 显示方式麻烦: 获取流, 使用println("")逐行打印
- 协同开发麻烦: UI负责美化页面, 程序员负责编写代码. UI不懂java, 程序员又不能将所有前端页面的内容通过流输出
二. JSP (Java Server Pages)
2.1 概念
- 简化的Servlet设计, 在HTML标签中嵌套Java代码, 用来高效开发Web应用的动态网页
2.2 作用
- 替换显示页面部分的Servlet(使用*.jsp文件替换XxxJSP.java)
三. JSP开发[重点]
3.1 创建JSP
- 在Web目录下新建*.jsp文件 (与WEB-INF平级)
3.1.1 JSP编写Java代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>This is my first page!</title>
</head>
<body>
Now:<%=new java.util.Date() %>
</body>
</html>
- 使用<%=%>标签编写Java代码在页面中打印当前系统时间
3.1.2 访问JSP
- 在浏览器中输入http://ip:port/项目路径/资源名称
3.2 JSP与Servlet
- 关系
- JSP文件在容器中会转换成Servlet执行
- JSP是对Servlet的一种高级封装, 本质还是Servlet
- 区别
- 与Servlet相比LJSP可以很方便的编写或者修改HTML网页而不用去面对大量的println语句
Servlet
- 编码:
- 继承HttpServlet + 注解或配置web.xml
- 修改:
- 重新部署, 重启服务器
- 访问:
- http://ip:port/apps/url-pattern
- 标签:
- printWriter.println("< html>");
JSP
- 编码
- HTML中直接编写Java代码
- 修改:
- 刷新页面
- 访问:
- http://ip:port/apps/路径+*.jsp
- 标签:
- 直接编写HTML标签
3.3 JSP实现原理
- Tomcat会将xxx.jsp转换成Java代码, 进而编译成.class文件运行, 最终将运行结果通过response响应给客户端
3.3.1 JSP.java源文件存放目录
- 使用IDEA开发工具, Tomcat编译后的JSP文件(Xxx_jsp.class和Xxx_jsp.java)的存放地点:
- C:\用户名\账户名\AppData\Local\JetBrains\IntelliJIdea2020.1\tomcat\项目名称\work\Catalina\localhost\应用上下文\org\apache\jsp
- AppData或许需要查看隐藏的项目
- 可以在Tomcat启动后在控制台里找到路径
四. JSP与HTML集成开发
4.1 脚本
- 脚本可以编写Java语句, 变量, 方法或表达式
4.1.1 普通脚本
- 语法: <% Java代码 &>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>脚本的使用</title>
</head>
<body>
<%
//jsp中, 使用小脚本嵌入Java代码
int a = 10;//普通脚本定义的是局部变量
System.out.println(a);//打印内容在控制台
out.println(a);//打印内容在客户端页面
%>
</body>
</html>
- 经验: 普通脚本可以使用所有Java语法, 除了定义函数
- 注意: 脚本与脚本之间不可嵌套, 脚本与HTML标签不可嵌套
4.1.2 声明脚本
- 语法: <%! 定义变量., 函数 %>
<%!
int b = 20;//声明脚本定义的是全局变量
public void test() {//定义一个无返回值的方法
System.out.println("你好");
}
public int test1() {//定义一个有返回值的方法
return 100;
}
%>
- 注意: 声明脚本声明的变量是全局变量
- 声明脚本的内容必须在普通脚本<% % >中调用
- 如果声明脚本中的函数具有返回值, 可以使用输出脚本调用<%= %>
4.1.3 输出脚本
- 语法: <%= Java表达式 %>
<%@ page import="java.util.Date" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>脚本的使用</title>
</head>
<body>
<%=test1()%>
<%="今天天气很好!"%>
<%=666%>
<%=new Date()%>
</body>
</html>
- 经验: 输出脚本可以输出带有返回值的函数
- 注意: 输出脚本中不能加分号;
4.2 JSP注释
- JSP注释有两个作用: 为脚本代码作注释以及HTML内容注释
4.2.1 语法规则
- <%--- 注释 ---%>
- JSP注释, 注释内容不会被发送至浏览器甚至不会被编译
- <%!--- 注释 ---%>
- HTML注释, 通过浏览器查看网页源代码时可以看见注释内容
4.2.2 注释
<%-- JSP注释在网页中不会被显示 --%>
<%!-- HTML注释在网页源代码中会显示 --%>
4.3 JSP指令
- JSP指令用来设置与整个JSP页面相关的属性
- <%@ page ...%>: 定义页面的依赖属性, 比如脚本语言, error页面, 缓存需求等
- <%@ include ...%>: 包含其他文件
- <%@ taglib ...%>: 引入标签库的定义, 可以是自定义标签
4.3.1 page指令
- 语法: <%@ page attribute1="value1" attribute2="value2"%>
- page指令为容器提供当前页面的使用说明, 一个JSP页面可以包含多个page指令
- contentType: 指定当前JSP页面的MIME类型和字符编码格式
- errorPage: 指定当JSP页面发生异常时需要转向的错误处理页面
- isErrorPage: 指定当前页面是否可以作为另一个JSP页面的错误处理页面
- import: 导入要使用的Java类
- language: 定义JSP页面所用的脚本语言, 默认是Java
- session: 指定JSP页面是否使用session, 默认为true立即创建, false为使用时创建
- pageEncoding: 指定JSP页面的解码格式
4.3.2 include指令
- 语法: <%@ include file = "被包含的JSP路径"%>
- 通过include指令来包含其他文件
- 被包含的文件可以是JSP文件, HTML文件或文本文件. 包含的文件就好像是当前JSP文件的一部分, 会被同时编译执行(静态包含)
<%@include file="header.jsp"%>
...
...
<%@include file="footer.jsp"%>
- 注意: 可能会有重名的冲突问题, 不建议使用
4.3.3 taglib指令
- 语法: <%@ taglib url = "外部标签库路径" prefix = "前缀"%>
- 引入JSP的标准标签库
<%@ taglib url="http://java.sun.com/jsp/jstl/core" prefix="c" %>
4.4 动作标签
- 语法: <jsp: action_name attribute="value" />
- 动作标签指的是JSP页面在运行期间的命令
4.4.1 include
- 语法: <jsp: include page = "相对 URL 地址" />
- <jsp: include >动作元素会将外部文件输出结果包含在JSP中 (动态包含)
- page: 包含在页面中的相对URL地址
<jsp:include page="index.jsp"/>
- 注意:前面已经介绍过include指令, 它是将外部文件的输出代码复制到了当前JSP文件中, 而这里的jsp:include动作不同, 是将外部文件的输出结果引入到了当前JSP文件中
4.4.2 useBean
- 语法: <jsp: useBean id="name" class="package.className" />
- jsp:useBean 动作用来加载一个将在JSP页面中使用的JavaBean
<jsp:useBean id="user" class="com.dz.entity.User" />
- 在类载入后, 我们可以通过jsp:setProperty和jsp:getProperty动作来修改和获取bean的属性
4.4.3 setProperty
- 可以在jsp:useBean元素之后使用jsp:setProperty进行属性的赋值
- name: name属性是必需的. 它表示要设置属性的是哪个Bean
- property: property属性是必需的. 它表示要设置哪个属性
- value: value属性是可选的. 该属性用来指定Bean属性的值
<jsp:useBean id="user" class="com.dz.entity.User" />
<jsp:setProperty name="user" property="name" value="mars">
4.4.4 getProperty
- jsp:getProperty动作提取指定Bean属性的值, 转换成字符串, 然后输出
- name: 要检索的Bean属性名称. Bean必须已定义
- property: 表示要提取Bean属性的值
<jsp:useBean id="user" class="com.dz.entity.User" />
<jsp:setProperty name="user" property="name" value="mars" />
<jsp:getProperty name="user" property="name" />
4.4.5 forward
- 语法: <jsp: forward page="相对 URL 地址" />
- jsp:forward动作把请求转到另外的页面
- page: page属性包含的是一个相对URL
<jsp:forward page="index.jsp" />
4.4.6 param
- 语法: <jsp: param name="" value="" />
- 在转发动作内部使用. 做参数传递
<jsp:forward page="index.jsp">
<!--- http请求参数传递 --->
<jsp:param name="sex" value="man" />
</jsp:forward>
- 获取参数的值: <%String result = request.getParameter("sex");%>
4.5 内置对象
-
由JSP自动创建的对象, 可以直接使用
对象名 类型 说明
-
request javax.servlet.http.HttpServletRequest
-
response javax.servlet.http.HttpServletResponse
-
session javax.servlet.http.HttpSession 由session="true"开关
-
**application ** javax.servlet.ServletContext
-
config javax.servlet.ServletConfig
-
exception java.lang.Throwable 由isErrorPage="false"开关
-
out javax.servlet.jsp.JspWriter
-
**pageContext ** javax.servlet.jsp.PageContext
-
page java.lang.Object当前对象this 当前servlet实例
4.5.1 四大域对象
- JSP有四大作用域对象, 存储数据和获取数据的方式一样, 不同的是取值的范围有差别
- pageContext: 当前JSP页面范围
- request: 一次请求有效
- session: 一次会话有效(关闭浏览器失效)
- application: 整个Web应用有效(服务器重启或关闭失效)
4.5.2 pageContext对象
- pageContext对象是javax.servlet.jsp.PageContext类的实例, 拥有作用域, 用来代表整个JSP页面
- 当前页面的作用域对象, 一旦页面跳转则失效
- 通过setAttribute("name",value);存储值
- 通过getAttribute("name");获取值
- 用于获取其他8个内置对象或者操作其他对象的作用域
<%
pageContext.setAttribute("name",value);//当前页面作用域有效
%>
4.5.3 pageContext获取其他内置对象
<%
pageContext.getRequest();//返回request内置对象
pageContext.getResponse();//返回response内置对象
pageContext.getSession();//返回session内置对象
pageContext.getServletContext();//返回application内置对象
pageContext.getOut();//返回out内置对象
pageContext.getException();//返回exception内置对象
pageContext.getPage();//返回page内置对象
pageContext.getServletConfig();//返回config内置对象
%>
4.5.4 pageContext操作其他内置对象的作用域
- pageContext对象可以操作其他作用域存储和获取
<%
/*操作其他作用域存储*/
pageContext.setAttribute("page", "123", PageContext.PAGE_SCOPE);//当前页面作用域
pageContext.setAttribute("req","aaa", PageContext.REQUEST_SCOPE);//request作用域
pageContext.setAttribute("session","bbb", PageContext.SESSION_SCOPE);//session作用域
pageContext.setAttribute("application","ccc", PageContext.APPLICATION_SCOPE);//application作用域
/*操作其他作用域获取*/
String value = (String) pageContext.getAttribute("page");//当前页面作用域
String value1 = (String) request.getAttribute("req");//request作用域
String value2 = (String) session.getAttribute("session");//session作用域
String value3 = (String) application.getAttribute("application");//application作用域
String value4 = (String) pageContext.findAttribute("req");//从pageContext, request, session, application四个作用域中依次查找
%>
4.6 整合
- 将EmpProject项目的所有显示页面JSP的Servlet替换为JSP页面, 使用脚本进行显示
4.6.1 showAllEmp.jsp
<%@ page import="com.dz.emp.entity.Emp" %>
<%@ page import="java.util.List" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>查询所有员工页面</title>
</head>
<body>
<form action=‘/EmpProject/manager/safe/showInsertEmp.jsp‘>
<p><input type=‘submit‘ value=‘新增‘></p>
</form>
<table border=‘1‘>
<tr>
<td>编号</td>
<td>姓名</td>
<td>工资</td>
<td>年龄</td>
<td colspan=‘2‘>操作</td>
</tr>
<%
List<Emp> empList = (List<Emp>) request.getAttribute("empList");//从request中拿数据
for (Emp emp : empList) {
%>
<tr>
<td><%=emp.getId()%></td>
<td><%=emp.getName()%></td>
<td><%=emp.getSalary()%></td>
<td><%=emp.getAge()%></td>
<td><a href="<%=request.getContextPath()+"/manager/safe/removeEmpController?id="+emp.getId()%>">删除</a></td>
<td><a href="<%=request.getContextPath()+"/manager/safe/showEmpController?id="+emp.getId()%>">修改</a></td>
</tr>
<%
}
%>
</table>
</body>
</html>
4.6.2 showInsertEmp.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>新增员工信息页面</title>
</head>
<body>
<form action=‘/EmpProject/manager/safe/insertEmpController‘ method=‘post‘>
<p>姓名:<input type=‘text‘ name=‘name‘></p>
<p>工资:<input type=‘text‘ name=‘salary‘></p>
<p>年龄:<input type=‘text‘ name=‘age‘></p>
<p><input type=‘submit‘ value=‘提交‘></p>
</form>
</body>
</html>
4.6.3 showUpdateEmp.jsp
<%@ page import="com.dz.emp.entity.Emp" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>修改员工信息页面</title>
</head>
<body>
<%
Emp emp = (Emp) request.getAttribute("emp");
%>
<form action=‘/EmpProject/manager/safe/updateEmpController‘ method=‘post‘>
<p>编号:<input type=‘text‘ name=‘id‘ value=<%=emp.getId()%> readonly></p>
<p>姓名:<input type=‘text‘ name=‘name‘ value=<%=emp.getName()%>></p>
<p>工资:<input type=‘text‘ name=‘salary‘ value=<%=emp.getSalary()%>></p>
<p>年龄:<input type=‘text‘ name=‘age‘ value=<%=emp.getAge()%>></p>
<p><input type=‘submit‘ value=‘修改‘></p>
</form>
</body>
</html>
五. El表达式 (Expression Language)
5.1 概念
- EL使JSP写起来更简单, 简洁, 主要用于获取作用域中的数据
5.2 作用
- 用于替换作用域对象.getAttribute("name");
5.3 EL的应用 (获取基本类型, 字符串)
- ${scope.name} 获取具体某个作用域中的数据
- ${name} 获取作用域中的数据, 逐级查找(pageContext, request, session, application) 注意: 使用此方法时要确保name在作用域中是唯一的
5.3.1 EL应用案例
<body>
<%
request.setAttribute("key1","value1");
session.setAttribute("key2", "value2");
application.setAttribute("key3", "value3");
%>
<%--通过作用域对象获取数据--%>
<h1><%=request.getAttribute("key1")%></h1>
<h1><%=session.getAttribute("key2")%></h1>
<h1><%=application.getAttribute("key3")%></h1>
<hr/>
<%--通过EL表达式获取数据--%>
<h1>${requestScope.key1}</h1>
<h1>${sessionScope.key2}</h1>
<h1>${applicationScope.key3}</h1>
<hr/>
<h1>${key1}</h1>
<h1>${key2}</h1>
<h1>${key3}</h1>
</body>
5.3.2 EL和JSP脚本的区别
- <%=request.getAttribute()%>没有找到返回null
- ${requestScope.name}没有找到返回""
5.4 EL的应用 (获取引用类型)
- 使用EL获取作用域中的对象调用属性时, 只能访问对象的get方法, 必须遵守命名规范定义
<%@ page import="com.dz.entity.User" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>El获取对象</title>
</head>
<body>
<%
User user = new User("mars","123");
request.setAttribute("user",user);
%>
${user}<br>
${user.username}<br>
${user.password}<br>
</body>
</html>
5.5 EL的应用 (获取数组, 集合的元素)
- EL可以获取Array, List, Map 中的元素, Set由于无下标, 无法直接访问元素, 后续可遍历
<%
int[] array = new int[]{1,2,3,4,5};
request.setAttribute("array",array);
List<String> nums = new ArrayList<>();
nums.add("A");
nums.add("B");
nums.add("C");
request.setAttribute("nums",nums);
Map<String,String> maps = new HashMap<>();
maps.put("CN","中国");
maps.put("US","美国");
maps.put("UK","英国");
request.setAttribute("maps",maps);
%>
<%--EL访问数据--%>
${array[0]}<br>
${array[1]}<br>
${array[2]}<br>
<hr>
${nums[0]}<br>
${nums[1]}<br>
${nums.get(2)}<br>
<hr>
${maps["CN"]}<br>
${maps["US"]}<br>
${maps.UK}<br>
5.6 EL的运算符
操作符
- .
- 访问一个Bean属性或者一个映射条目
- []
- 访问一个数组或者链表的元素
-
- 加
-
- 减
-
- 乘
- / or div
- 除
- % or mod
- 取模
- == or eq
- 测试是否相等
- != or ne
- 测试是否不等
- < or lt
- 测试是否小于
- (>) or gt
- 测试是否大于
- <= or le
- 测试是否小于等于
- (>=) or ge
- 测试是否大于等于
- && or and
- 测试逻辑与
- || or or
- 测试逻辑或
- ! or not
- 测试取反
- empty
- 测试是否空值
5.6.1 EL表达式执行运算
<%
request.setAttribute("nums",100);
request.setAttribute("n","");
request.setAttribute("m",null);
%>
<h1>算数运算符</h1>
<h1>${nums + 2}</h1>
<h1>${nums - 2}</h1>
<h1>${nums * 2}</h1>
<h1>${nums div 2}</h1>
<h1>${nums mod 2}</h1>
<hr>
<h1>关系运算符</h1>
<h1>${nums eq 101}</h1><%-- = --%>
<h1>${nums ne 101}</h1><%-- != --%>
<h1>${nums lt 101}</h1><%-- < --%>
<h1>${nums gt 101}</h1><%-- > --%>
<h1>${nums le 101}</h1><%-- <= --%>
<h1>${nums ge 101}</h1><%-- >= --%>
<hr>
<h1>逻辑运算符</h1>
<h1>${nums > 100 and nums < 200}</h1>
<h1>${nums > 100 or nums < 200}</h1>
<h1>${not(nums > 100)}</h1>
<hr>
<h1>empty运算符</h1>
<h1>${empty n}</h1>
<h1>${empty m}</h1>
5.6.2 empty关键字
<%
request.setAttribute("n","");
request.setAttribute("m",null);
%>
<%--empty关键字 只要内容为空 就返回true--%>
<h1>empty运算符</h1>
<h1>${empty n}</h1>
<h1>${empty m}</h1>
5.7 隐式对象
- EL表达式语言定义了11个隐式对象
- pageScope
- page作用域
- requestScope
- request作用域
- sessionScope
- session作用域
- applicationScope
- application作用域
- param
- Request对象的参数, 字符串
- paramValues
- Request对象的参数, 字符串
- header
- HTTP信息头, 字符串
- headerValues
- HTTP信息头, 字符串集合
- initParam
- 上下文初始化参数
- cookie
- Cookie值
- pageContext
- 当前页面的pageContext
5.7.1 获得应用上下文
<%=request.getContextPath()%>
${pageContext.request.contextPath}
5.7.2 获取Cookie对象
<h1>${cookie.username}</h1><%--获取名为username的cookie对象--%>
<h1>${cookie.password}</h1><%--获取名为password的cookie对象--%>
<h1>${cookie.username.value}</h1><%--获取名为username的cookie对象的value值--%>
<h1>${cookie.password.value}</h1><%--获取名为password的cookie对象的value值--%>
六. JSTL标准标签库
6.1 现有问题
- EL主要是用于作用域获取数据, 虽然可以做运算判断, 但是得到的都是一个结果, 做展示
- EL不存在流程控制, 比如判断
- EL对于集合只能做单点访问, 不能实现遍历操作, 比如循环
6.2 什么是JSTL
- JSTL: 全称Java Server Pages Standard Tag Library
- JSP标准标签库(JSTL) 是一个JSP标签集合
6.3 JSTL的作用
- 可对EL获取到的数据进行逻辑操作
- 与EL合作完成数据的展示
6.4 JSTL使用
- 导入两个jar文件: standard.jar 和 jstl.jar 文件拷贝到 /WEB-INF/lib/ 下
- 在JSP页面引入标签库<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
6.5 核心标签
6.5.1 条件标签if判断
- 语法: <c:if test="条件">< /c:if>
<%
request.setAttribute("username","dz");
%>
${username}
<%-- test属性中是条件, 但是条件需要使用EL表达式来书写 --%>
<c:if test="${username eq ‘dz‘}">
<h1>欢迎您, ${username}</h1>
</c:if>
<c:if test="${username ne ‘dz‘}">
<h1>请您重新登陆!</h1>
</c:if>
6.5.2 多条件choose判断
语法: < c:choose>
<c:when test="条件1">结果1< /c:when>
<c:when test="条件2">结果2< /c:when>
<c:when test="条件3">结果3< /c:when>
< c:otherwise>结果4< /c:otherwise>
< /c:choose>
<%
request.setAttribute("age",18);
%>
<c:choose>
<c:when test="${age < 18}"><h1>少年</h1></c:when>
<c:when test="${age >= 18 and age < 30}"><h1>中年</h1></c:when>
<c:when test="${age >= 30 and age < 50}"><h1>中年</h1></c:when>
<c:otherwise><h1>老年</h1></c:otherwise>
</c:choose>
6.5.3 迭代foreach标签
语法
<c:forEach var="变量名" items="集合" begin="起始下标" end="结束下标" step="间隔长度" varstatus="遍历状态"> </c:forEach>
<%
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
list.add("D");
list.add("E");
request.setAttribute("list",list);
%>
<%-- varStatus: 变量状态: 遍历出每一项内容的状态
first: 是否是第一行
last: 是否是最后一行
count: 当前行数
index: 当前元素的下标
--%>
<%-- var: 遍历出的每一项使用变量先存储
items: 集合(使用EL表达式)
--%>
<c:forEach var="v" items="${list}" begin="0" end="4" step="1" varStatus="vs">
<h1>${v} ${vs.first} ${vs.last} ${vs.count} ${vs.index}</h1>
</c:forEach>
6.5.4 url标签
- 在Cookie禁用的情况下, 通过重写URL拼接JSESSION来传递ID值, 便于下一次访问时仍可查到上一次的Session对象
<%--重写URL,拼接jsessionid(旧方法)--%>
<%
String newURL = response.encodeRedirectURL(request.getContextPath()+"/jstl/jstl1.jsp");
%>
<%=newURL%>
<a href="<%=response.encodeRedirectURL(request.getContextPath()+"/jstl/jstl1.jsp")%>">跳转</a><br>
<%--重写URL,拼接jsessionid(新方法,使用url标签)--%>
<c:url context=‘${pageContext.request.contextPath}‘ value=‘/jstl/jstl1.jsp‘></c:url>
<a href="<c:url context=‘${pageContext.request.contextPath}‘ value=‘/jstl/jstl1.jsp‘></c:url>">跳转2</a>
<%--在form表单的action中嵌套动态路径--%>
<form action="<c:url context=‘${pageContext.request.contextPath}‘ value=‘/jstl/jstl1.jsp‘></c:url>">
<input type="submit" value="提交">
</form>
- 经验: 所有涉及到页面跳转或者重定向跳转时, 都应该使用URL重写
6.6 整合
- 将现有的EmpProject项目进行整合, 使用EL+JSTL替换脚本代码
6.6.1 showAllEmp.jsp
<%@ page import="com.dz.emp.entity.Emp" %>
<%@ page import="java.util.List" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>查询所有员工页面</title>
</head>
<body>
<form action="<c:url context=‘${pageContext.request.contextPath}‘ value=‘/manager/safe/showInsertEmp.jsp‘></c:url>">
<p><input type=‘submit‘ value=‘新增‘></p>
</form>
<table border=‘1‘>
<tr>
<td>编号</td>
<td>姓名</td>
<td>工资</td>
<td>年龄</td>
<td colspan=‘2‘>操作</td>
</tr>
<c:forEach var="emp" items="${empList}">
<tr>
<td>${emp.id}</td>
<td>${emp.name}</td>
<td>${emp.salary}</td>
<td>${emp.age}</td>
<td><a href="<c:url context=‘${pageContext.request.contextPath}‘ value=‘/manager/safe/removeEmpController?id=${emp.id}‘></c:url>">删除</a></td>
<td><a href="<c:url context=‘${pageContext.request.contextPath}‘ value=‘/manager/safe/showEmpController?id=${emp.id}‘></c:url>">修改</a></td>
</tr>
</c:forEach>
</table>
</body>
</html>
6.6.2 showUpdateEmpInfo.jsp
<%@ page import="com.dz.emp.entity.Emp" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>修改员工信息页面</title>
</head>
<body>
<form action="<c:url context=‘${pageContext.request.contextPath}‘ value=‘/manager/safe/updateEmpController‘></c:url>" method=‘post‘>
<p>编号:<input type=‘text‘ name=‘id‘ value=${emp.id} readonly></p>
<p>姓名:<input type=‘text‘ name=‘name‘ value=${emp.name}></p>
<p>工资:<input type=‘text‘ name=‘salary‘ value=${emp.salary}></p>
<p>年龄:<input type=‘text‘ name=‘age‘ value=${emp.age}></p>
<p><input type=‘submit‘ value=‘修改‘></p>
</form>
</body>
</html>
6.6.3 showInsertEmp.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>新增员工信息页面</title>
</head>
<body>
<form action=‘<c:url context="${pageContext.request.contextPath}" value="/manager/safe/insertEmpController"></c:url>‘ method=‘post‘>
<p>姓名:<input type=‘text‘ name=‘name‘></p>
<p>工资:<input type=‘text‘ name=‘salary‘></p>
<p>年龄:<input type=‘text‘ name=‘age‘></p>
<p><input type=‘submit‘ value=‘提交‘></p>
</form>
</body>
</html>
七. MVC框架(Model-View-Controller)
7.1 MVC概念
- MVC又称为编程模式, 是一种软件设计思想, 将数据操作, 页面展示, 业务逻辑分为三个层级(模块), 独立完成, 相互调用
- 模型层(Model)
- 视图(View)
- 控制器(Controller)
7.2 MVC模式详解
- MVC并不是Java独有的, 现在几乎所有的B/S架构都采用MVC模式
- 视图View层:
- 视图即是用户看到并与之交互的界面, 比如HTML(静态资源), JSP(动态资源) 等等
- 控制器Controller层:
- 控制器即是控制请求的处理逻辑, 对请求进行处理, 负责流程跳转(转发和重定向)
- 模型Model层(Service+DAO+Entity):
- 对客观世界的一种代表和模拟(业务模拟, 对象模拟)
- 视图View层:
7.3 优点
- 低耦合性: 模块与模块之间的关联性不强, 不与某一种具体实现产生密不可分的关联性
- 高维护性: 基于低耦合性, 可做到不同层级的功能模块灵活更换, 插拔
- 高重用性: 相同的数据库操作, 可以服务于不同的业务处理, 将数据作为独立模块, 提高重用性
7.4 MVC在框架中应用
- MVC模式被广泛用于Java的各种框架中, 比如Struct2, SpringMVC等都用到了这种思想
7.5 三层架构与MVC
7.5.1 三层架构
-
View层 (表示|界面层)、Service层 (业务逻辑层)、DAO层(数据访问层)
-
表示层(UI, Main)
- 职责:
- 收集用户输入数据
- 调用业务逻辑层, 完成业务方法
- 展示数据或展示操作结果
- 职责:
-
业务逻辑层(service)
- 职责:
- 开启事务
- 调用DAO层
- 处理数据
- 提交或回滚
- 职责:
-
数据访问层(DAO)
- 职责:
- 查询相关业务逻辑的数据
- 根据相关业务逻辑修改的数据
- 职责:
7.5.2 MVC与三层架构的区别
- MVC强调的是视图和业务代码的分离, 严格的说MVC其实关注的是Web层, View就是单独的页面, 如JSP, HTML等, 不负责业务处理, 只负责数据的展示. 而数据封装到Model里, 由Controller负责在View和Model之间传递, MVC强调业务和视图分离
- 三层架构是 数据访问层(DAO), 业务逻辑层(Service), 表示层(View), 指的是代码之间的解耦, 方便维护和复用
八. 分页
8.1 概念
- 分页是Web应用程序非常重要的一个技术. 数据库中的数据可能是成千上万的, 不可能把这么多的数据一次显示在浏览器上面, 一般根据每行数据在页面上所占的空间设置每页显示若干行, 比如一般20行是一个比较理想的显示状态
8.2 分页实现思路
- 对于海量的数据查询, 需要多少就取多少, 显然是最佳的解决方案, 假如某个表中有200万条记录, 第一页取前20条, 第二页取21~40条记录
select * from 表名 limit 0,20;//第一页
select * from 表名 limit 20,20;//第二页
select * from 表名 limit 40,20;//第三页
8.3 分页代码实现
- 步骤
- 确定每页显示的数据数量
- 确定分页显示所需的总页数
- 编写SQL查询语句, 实现数据查询
- 在JSP页面中进行分页显示设置
8.3.1 数据库准备
CREATE TABLE emp(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20) NOT NULL,
salary DOUBLE NOT NULL,
age INT NOT NULL
)CHARSET=utf8;
#向数据库中添加100条数据
INSERT INTO emp(NAME,salary,age) VALUES(‘dz1‘,1000,18);
......(此处省略,请自行插入数据)
8.3.2 数据库配置文件db.properties
#<!-- 连接设置 -->
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/emp?useUnicode=true&characterEncoding=UTF-8
username=root
password=root
#<!-- 初始化连接 -->
initialSize=10
#<!-- 最大连接数量 -->
maxActive=50
#<!-- 最小空闲连接 -->
minIdle=5
#<!-- 超时等待时间以毫秒为单位 60000毫秒/1000等于60秒 -->
maxWait=5000
8.3.3 PageBean类
- 分页数据所需要的实体类! 其包含页码, 页大小, 总条数, 总页数, 起始行
package com.dz.emp.entity;
public class Page {
private Integer pageIndex;//页码, 第几页
private Integer pageSize;//页大小, 每页显示多少行数据
private Integer totalCounts;//数据的总行数, 一共有多少条数据
private Integer totalPages;//总页数, 数据一共可以分为多少页
private Integer startRows;//起始行, 用户从第几行开始查数据
//单参构造方法,参数为 页码pageIndex,内部调用两参构造方法,且把 页大小pageSize 的值固定为5
public Page(Integer pageIndex) {
this(pageIndex,5);
}
//两参构造方法,参数为 页码pageIndex, 页大小pageSize,并且设置 起始行startRows 为(pageIndex-1)*pageSize
public Page(Integer pageIndex, Integer pageSize) {
this.pageIndex = pageIndex;
this.pageSize = pageSize;
this.setStartRows((pageIndex-1)*pageSize);
}
public Integer getPageIndex() {
return pageIndex;
}
public void setPageIndex(Integer pageIndex) {
this.pageIndex = pageIndex;
}
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
public Integer getTotalCounts() {
return totalCounts;
}
//通过数据总行数对页大小取模是否为0, 从而判断总页数为多少
public void setTotalCounts(Integer totalCounts) {
this.totalCounts = totalCounts;
this.setTotalPages(totalCounts % pageSize == 0?totalCounts/pageSize:totalCounts/pageSize + 1 );
}
public Integer getTotalPages() {
return totalPages;
}
public void setTotalPages(Integer totalPages) {
this.totalPages = totalPages;
}
public Integer getStartRows() {
return startRows;
}
public void setStartRows(Integer startRows) {
this.startRows = startRows;
}
}
8.3.4 创建EmpDao接口
package com.dz.emp.dao;
import com.dz.emp.entity.Emp;
import com.dz.emp.entity.Page;
import java.util.List;
public interface EmpDao {
int insert(Emp emp);
int delete(int id);
int update(Emp emp);
Emp select(int id);
List<Emp> selectAll();
//分页查询所有
List<Emp> selectAll(Page page);
//查询数据总行数
long selectCounts();
}
8.3.5 EmpDaoImpl实现类
public class EmpDaoImpl implements EmpDao {
private QueryRunner queryRunner = new QueryRunner();
//此处省略其他方法
//分页查询所有
@Override
public List<Emp> selectAll(Page page) {
try {
List<Emp> empList = queryRunner.query(DbUtils.getConnection(), "select * from emp limit ?,?", new BeanListHandler<Emp>(Emp.class), page.getStartRows(),page.getPageSize());//两个参数分别为起始行和页大小
return empList;
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null;
}
//查询数据总行数
@Override
public long selectCounts() {
try {
long counts = queryRunner.query(DbUtils.getConnection(), "select count(*) from emp", new ScalarHandler<>());
return counts;
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return 0;
}
}
8.3.6 创建EmpService接口
package com.dz.emp.service;
import com.dz.emp.entity.Emp;
import com.dz.emp.entity.Page;
import java.util.List;
public interface EmpService {
int addEmp(Emp emp);
int removeEmp(int id);
int modify(Emp emp);
Emp showEmp(int id);
List<Emp> showAllEmp();
List<Emp> showAllEmp(Page page);
}
8.3.7 EmpServiceImpl实现类
public class EmpServiceImpl implements EmpService {
private EmpDao empDao = new EmpDaoImpl();
//此处省略其他方法
@Override
public List<Emp> showAllEmp(Page page) {
List<Emp> empList = new ArrayList<>();
try {
DbUtils.begin();
long counts = empDao.selectCounts();//查询共有多少条数据
page.setTotalCounts((int)counts);//将long类型的counts转换为int,并赋值给totalCounts
List<Emp> temp = empDao.selectAll(page);
if (temp != null) {
empList = temp;
}
DbUtils.commit();
} catch (Exception e) {
DbUtils.rollback();
e.printStackTrace();
}
return empList;
}
}
8.3.8 showAllEmpController实现
package com.dz.emp.controller;
import com.dz.emp.entity.Emp;
import com.dz.emp.entity.Page;
import com.dz.emp.service.EmpService;
import com.dz.emp.service.impl.EmpServiceImpl;
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.List;
@WebServlet(name = "ShowAllEmpController",value = "/manager/safe/showAllEmpController")
public class ShowAllEmpController extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String pageIndex = request.getParameter("pageIndex");//获取到页码的值
if (pageIndex == null) {//用户第一次访问页码为空时
pageIndex = "1";//为页码赋值为1
}
Page page = new Page(Integer.valueOf(pageIndex));//将字符串格式的页码转换为Integer类型
EmpService empService = new EmpServiceImpl();
List<Emp> empList = empService.showAllEmp(page);
if (empList != null) {
request.setAttribute("page",page);
request.setAttribute("empList",empList);//存到request作用域中,临时存储
request.getRequestDispatcher("/manager/safe/showAllEmp.jsp").forward(request,response);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
}
8.3.9 showAllEmp.jsp
<%@ page import="com.dz.emp.entity.Emp" %>
<%@ page import="java.util.List" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>查询所有员工页面</title>
</head>
<body>
<form action="<c:url context=‘${pageContext.request.contextPath}‘ value=‘/manager/safe/showInsertEmp.jsp‘></c:url>">
<p><input type=‘submit‘ value=‘新增‘></p>
</form>
<table border=‘1‘>
<tr>
<td>编号</td>
<td>姓名</td>
<td>工资</td>
<td>年龄</td>
<td colspan=‘2‘>操作</td>
</tr>
<c:forEach var="emp" items="${empList}">
<tr>
<td>${emp.id}</td>
<td>${emp.name}</td>
<td>${emp.salary}</td>
<td>${emp.age}</td>
<td><a href="<c:url context=‘${pageContext.request.contextPath}‘ value=‘/manager/safe/removeEmpController?id=${emp.id}‘></c:url>">删除</a></td>
<td><a href="<c:url context=‘${pageContext.request.contextPath}‘ value=‘/manager/safe/showEmpController?id=${emp.id}‘></c:url>">修改</a></td>
</tr>
</c:forEach>
<tr>
<td colspan="6">
<a href="<c:url context=‘${pageContext.request.contextPath}‘ value=‘/manager/safe/showAllEmpController?pageIndex=1‘/> ">首页</a>
<c:if test="${page.pageIndex > 1}">
<a href="<c:url context=‘${pageContext.request.contextPath}‘ value=‘/manager/safe/showAllEmpController?pageIndex=${page.pageIndex-1}‘></c:url> ">上一页</a>
</c:if>
<c:if test="${page.pageIndex == 1}">
<a>上一页</a>
</c:if>
<c:if test="${page.pageIndex < page.totalPages}">
<a href="<c:url context=‘${pageContext.request.contextPath}‘ value=‘/manager/safe/showAllEmpController?pageIndex=${page.pageIndex+1}‘></c:url> ">下一页</a>
</c:if>
<c:if test="${page.pageIndex == page.totalPages}">
<a>下一页</a>
</c:if>
<a href="<c:url context=‘${pageContext.request.contextPath}‘ value=‘/manager/safe/showAllEmpController?pageIndex=${page.totalPages}‘/> ">尾页</a>
</td>
</tr>
</table>
</body>
</html>