MVC:模型 视图 控制器本质上是组织代码的编程范式
MVC的核心是引入控制器代码,将用户界面的代码和业务逻辑的代码隔离(解耦)
MVC的设计理念,将Model细分为业务模型(Service)、数据访问模型(DAO)、数据模型(JavaBean)
对于Service类,通用技术是java SE
对于DAO模型,通用技术是JDBC
数据模型负责持有业务数据,通用技术是JavaBean
数据模型是既要持有Form表单中的数据,又要持有数据库表中的数据
EntityBean持有数据库表的数据,使用FormBean持有表单数据,这两者都是属于JavaBean
Service和DAO之间的关系
一个业务逻辑对应Service类的一个方法。
为了完成一个业务逻辑需要执行多个数据库访问操作
将业务逻辑与数据库操作解耦
通常做法是在Service和数据库之间引入DAO
设计DAO的基本原则是DAO只封装了增删改查等最基本的数据库元操作,DAO中不会包含与业务逻辑相关的代码
设计Service的基本原则是,Service通常需要调用多个DAO才能完成一个业务
JavaBean命名属性的特殊情况
1.命名属性的惰性计算
利用缓存等记忆化特性,暂存程序的执行结果
如:
private List userName = null;
public List getUserNames(){
if(userNames == null){
userNames = fetchUserNamesFromDatabase();
}
return userNames;
}
这样第一次调用getter方法时,需要从数据库中提取数据。时间成本较高,但第二次调用getter方法会,直接返回第一次缓存结果,降低了时间成本
2.合成属性
一般JavaBean中的命名属性就是JavaBean的私有成员变量,但也有例外,合成属性是JavaBean命名属性的另一种特殊形式。
就比如说JavaBean中有一个Person类,定义一个IDNumber命名属性和三个合成属性(birthday、sex、age)
结论:仅可以通过无参的 getter 方法和 is 方法,就可以定义JavaBean属性
小露身手:创建数据模型JavaBean和业务逻辑Service
准备工作:创建Dynamic Web Project,Servlet-api.jar包导入 Web 项目,jstl项目部署到Tomcat中
Java Resources找到src然后右键选择class,包名为bean,类名为User
package bean;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class User {
private int userID;
private String userName;
private String birthday;
private boolean admin;
//为User类添加带有参数的构造方法,右击代码空白区域选择Source-Generate Constructor using Fields
//constructor,选中所有成员变量,然后点击确定按钮
public User(int userID, String userName, String birthday, boolean admin) {
super();
this.userID = userID;
this.userName = userName;
this.birthday = birthday;
this.admin = admin;
}
//将成员变量定义为只读命名变量,右键空白,Source-Generate getter and setters,选中四个成员变量的
//getter方法和is方法
public int getUserID() {
return userID;
}
public String getUserName() {
return userName;
}
public String getBirthday() {
return birthday;
}
public boolean isAdmin() {
return admin;
}
//添加合成属性age的getter方法,通过出生日期计算年龄
public int getAge() throws Exception{
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
Date date = null;
date = dateFormat.parse(birthday);
Calendar cal = Calendar.getInstance();//当前时间
int yearNow = cal.get(Calendar.YEAR);
int monthNow = cal.get(Calendar.MONTH);
int dayOfMonthNow = cal.get(Calendar.DAY_OF_MONTH);
cal.setTime(date);
int yearBirth = cal.get(Calendar.YEAR);
int monthBirth = cal.get(Calendar.MONTH);
int dayOfMonthBirth = cal.get(Calendar.DAY_OF_MONTH);
int age = yearNow - yearBirth;//计算整岁
if(monthNow <= monthBirth) {
if(monthNow == monthBirth) {
if(dayOfMonthNow < dayOfMonthBirth) {
age--;//当前日期在生日之前,年龄-1
}
}else {
//当前月份在年龄之前,年龄-1
age--;
}
}
return age;
}
}
如法炮制:包名为service,类名为UserService,输入以下代码
package service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import bean.User;
public class UserService {
public static User createRandomUser() {
Random random = new Random();
int rand = random.nextInt(9000)+1000;
User user = new User(rand, "用户名"+rand, "2008-8-8", false);
return user;
}
public static List<User> getUserList(){
List<User> userList = new ArrayList<User>();
for(int i=0;i<=10;i++) {
User user = createRandomUser();
userList.add(user);
}
User admin = new User(1, "admin", "2000-8-8", true);
userList.add(admin);
return userList;
}
public static Map<String, User> getUserMap() {
Map<String, User> userMap = new HashMap<String, User>();
for(int i=0;i<=10;i++) {
User user = createRandomUser();
userMap.put(Integer.toString(user.getUserID()), user);
}
User admin = new User(1, "admin", "2000-8-8", true);
userMap.put("1", admin);
return userMap;
}
//至此创建了一个业务逻辑模型UserService,以备视图层(JSP+EL+JSTL)测试使用
}
JavaBean的分类
分为:FormBean和EntityBean
使用FormBean后,Servlet从表单中获取请求参数的时候,不需要再调用request.getParameter()方法,直接调用FormBean的getter方法即可
FormBean显著特点如下:
FormBean通常提供一组表单验证的方法(validate())
FormBean的成员变量通常与Form表单的表单控件一一对应
FormBean可以借助第三方MVC架构实现,如SpringMVC 、Struts 、JSF等
EntityBean的成员变量通常和数据库表中的字段一一对应
EntityBean需要提供EntityBean与EntityBean之间的各种映射关系
JSP的内置对象
前三个已经学过,request、response和session
另外三个为out、application、pageContext
内置对象out
out与response.getWriter()的执行流程不同
response.getWriter().print()方法的执行流程:将字符数据直接添加到 response 缓存中。
out.print()方法的执行流程:首先将字符数据写入out内置对象的缓存中,out内置对象缓存中的数据再择机写入response的缓存中
例如
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%="a"%>
b
<%
out.write("c");
%>
<%
response.getWriter().write("d");
%>
程序执行的结果是d a b c
因为abc都被加入out的缓存中,而d直接添加到response缓存中
对此,我们可以使用out.flush()方法控制缓存的刷新时间,继而控制字符数据的输出顺序,下面的程序在3个文本型数据a、b、c后各添加了一个out.flush()方法,程序执行的结果是,“abcd”通过采用了3次强制刷新的方法,虽然可以控制字符数据的输出顺序,但是效率低下
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%="a"%>
<% out.flush(); %>
b
<% out.flush(); %>
<%
out.write("c");
out.flush();
%>
<%
response.getWriter().write("d");
%>
内置对象application和Servlet中的ServletContext
JSP的内置对象application实际上是Servlet中servletContext类的实例化对象
这个对象全局唯一,而且工程内部的所有servlet都共享这个对象。所以叫全局应用程序共享对象。
servletContext实例化对象功能主要两个:
1、获取Web项目部署后的绝对物理路径
request.getServletContext.getRealPath("")方法可以获取Web项目部署后的绝对物理路径
等同于application.getRealPath("")
2、作为域对象
(1)向ServletContext对象绑定属性,绑定后全局共享当然也可以换成application.setAttribute(name,object)
(2)获取ServletContext对象绑定的属性,application也可以
内置对象pageContext
JSP的内置对象pageContext提供了当前JSP页面的容器,常用功能有两个
1. 获取其他内置对象(注意是当前的JSP页面的session、request response之类的)
2.作为域对象绑定和解绑属性
pageContext的典型应用是:使用JSTL遍历集合(Collection或者Map)遍历过程中产生的当前元素作为属性被绑定在pageContext域对象上
request针对当前请求有效,作为域对象,主要有请求包含和请求转发
session针对当前的session会话有效,作为域对象,主要用于权限控制
application是对整个Web项目有效,统计在线人数经常使用
EL表达式语言
EL语法格式:以$符号开头,表达式的内容包括在{}之中
EL的功能:在JSP页面中,获取并输出对象的属性值(attribute属性或者JavaBean的property命名属性)通过:“.”运算符或者“[]”运算符获取
例如${user.userName}或者${user["userName"]}
1.“.”运算符后面的属性名,必须符合Java变量名的命名规范,否则必须使用“[]”运算符
2.使用[]运算符,引号不能省略,但可以是单引号
EL的功能和优点
EL作为JSP规范的
一部分,通常用于获取变量的值并输出变量的值,而不用于设置变量值。
例如以红色字体,显示Session会话中的用户名
<font color='red'>
<%
String userName = (String)session.getAttribute("userName");
if(userName == null){
userName = "";
}else{
userName = (String)session.getAttribute("userName");
}
out.print(userName);
%>
</font>
如果替换成EL则为
<font color='red'>${sessionScope.userName}</font>
EL的核心功能就是:获取变量的值,判断是否为null,强制转换类型为字符串,最后输出到HTML页面
不要在EL中试图调用对象的方法
EL内置对象
El一共11个内置对象,无需创建即可使用。这11个内置对象中有10个是Map类型,最后是pageContext对象和intiParam
scope是域对象,param、header和cookie是HTTP请求对象
pageScope:获取pageContext域属性,相当于pageContext.getAttribute("xxx")
requestScope:获取request域属性,相当于request.getAttribute("xxx")
sessionScope:获取session域属性,相当于session.getAttribute("xxx")
applicationScope:获取application域属性,相当于application.getAttribute("xxx")
param:对应参数,它是一个Map,其中key是参数,value是参数值,适用于单值的参数,相当于request.getParameter("xxx")
paramValues:对应参数,她是一个Map,其中key是参数,value是多个参数值,适用于多值的参数,相当于request.getParameterValues("xxx")
header:对应请求头,它是一个Map,其中key表示头名称,value是单个头值,适用于单值的请求头,相当于request.getHeader("xxx")
headerValues:对应请求头,它是一个Map,其中key表示头名称,value是多个头值,适用于多值的请求头,相当于request.getHeaders("xxx")
initParam:获取web.xml中<context-param>内的参数,${ initParam.xxx},xxx就是<param-name>标签内的值,进而得到<param-value>中的值
cookie:用于获取cookie,Map<String,Cookie>,其中key是cookie的name,value是cookie对象,例如${cookie.JSESSIONID.value }就是获取sessionId
pageContext:可以获取JSP九大内置对象,相当于使用该对象调用getxxx()方法,例如pageContext.getRequest()可以写为${pageContext.request)
小露身手:EL的使用
创建controller的JSTLServlet
package controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/JSTLServlet")
public class JSTLServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String action = request.getParameter("action");
response.setContentType("text/html;charset=UTF-8");
if("".equals(action) || action == null) {
response.getWriter()
.append("本页面是程序首页,为浏览器用户提供了若干功能选项<br/>")
.append("在URL后添加<font color='red'>查询字符串</font>,测试EL表达式语言各内置对象<br/>")
.append("?action=scope 使用EL获取并输出4个域对象的属性值<br/>")
.append("?action=param 使用EL获取并输出请求参数的值<br/>")
.append("?action=bean 使用EL获取JavaBean命名属性的值<br/>")
.append("?action=pageContext 使用EL获取pageContext对象<br/>")
.append("?action=header 使用EL获取HTTP请求头和Cookie请求头<br/>")
.append("?action=empty empty运算符的使用<br/>")
.append("?action=foreach JSTL循环的使用<br/>")
;
return;
}
//所有新增的功能代码添加到此处
if("scope".equals(action)) {
request.setAttribute("userName", "张三request");
javax.servlet.http.HttpSession httpSession = request.getSession();
httpSession.setAttribute("userName", "张三session");
javax.servlet.ServletContext servletContext = request.getServletContext();
servletContext.setAttribute("userName", "张三application");
request.getRequestDispatcher("/scope.jsp").include(request, response);
return;
}
if("param".equals(action)) {
String name = java.net.URLEncoder.encode("张三","UTF-8");
String paramPath = "/param.jsp?userName=" + name + "&hobby=shopping&hobby=music";
request.getRequestDispatcher(paramPath).include(request, response);
return;
}
if("bean".equals(action)) {
bean.User user = service.UserService.createRandomUser();
request.setAttribute("user", user);
java.util.List<bean.User> userList = service.UserService.getUserList();
request.setAttribute("userList", userList);
java.util.Map<String, bean.User> userMap = service.UserService.getUserMap();
request.setAttribute("userMap", userMap);
request.getRequestDispatcher("/bean.jsp").include(request, response);
return;
}
if("pageContext".equals(action)) {
request.getRequestDispatcher("/pageContext.jsp").include(request, response);
return;
}
if("header".equals(action)) {
Cookie userNameCookie = new Cookie("userName","userNameCookie");
response.addCookie(userNameCookie);
response.sendRedirect(request.getContextPath()+"/header.jsp");
return;
}
if("empty".equals(action)) {
request.setAttribute("emptyString", "");
request.setAttribute("nullValue", null);
request.setAttribute("emptyList", new java.util.ArrayList());
request.setAttribute("emptyMap", new java.util.HashMap());
request.setAttribute("emptySet", new java.util.HashSet());
request.setAttribute("zeroInt", 0);
request.setAttribute("zeroFloat", 0.0);
request.setAttribute("falseValue", false);
request.setAttribute("blank", " ");
request.getRequestDispatcher("/empty.jsp").include(request, response);
return;
}
if("three".equals(action)) {
String paramPath = "/three.jsp?sex=m";
request.getRequestDispatcher(paramPath).include(request, response);
return;
}
if("foreach".equals(action)) {
bean.User user = service.UserService.createRandomUser();
request.setAttribute("user", user);
java.util.List<bean.User> userList = service.UserService.getUserList();
request.setAttribute("userList", userList);
java.util.Map<String, bean.User> userMap = service.UserService.getUserMap();
request.setAttribute("userMap", userMap);
request.getRequestDispatcher("/foreach.jsp").include(request, response);
return;
}
if("remove".equals(action)) {
request.setAttribute("userName", "zhangsan");
request.getRequestDispatcher("/remove.jsp").include(request, response);
return;
}
//通知浏览器用户,更多功能尚待开发。
response.setContentType("text/html;charset=UTF-8");
response.getWriter().print("该功能尚未提供,期待您的开发升级!<br/>");
}
}
创建scope.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<h3>使用EL获取并输出4个域对象的属性值</h3>
${ userName }<br/>
${ requestScope.userName }<br/>
${ sessionScope.userName }<br/>
${ applicationScope.userName }<br/>
<% pageContext.setAttribute("userName", "张三pageContext"); %>
${ userName }<br/>
${ pageScope.userName }<br/>
COOKIE和SESSION:
服务器存有sessionID,当你浏览器的Cookie的session与之吻合即可 ?
ps.关闭服务器session默认还能坚持20分钟
与上述地址结合自行理解一下
结论:如果不指定作用范围EL将按照pageContext request session ServletContext域对象的顺序,依次查找被绑定的属性
我出现了
javax.servlet.ServletException: 文.件[/product/productList.jsp] 未找到错误,只需要清理一下缓存即可
这一个param.jsp是获取和输出请求参数的值
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<h3>使用EL获取并输出请求参数的值</h3>
${param}<br/>
${paramValues}<br/>
${param.userName}<br/>
${paramValues.hobby}<br/>
${paramValues.hobby[0]}<br/>
${paramValues.hobby[1]}<br/>
下一个是获取javaBean命名属性的值
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<h3>使用EL获取JavaBean命名属性的值</h3>
${requestScope.user}<br/>
${requestScope.user.userID}<br/>
${requestScope.user.userName}<br/>
${requestScope.user.birthday}<br/>
${requestScope.user.age}<br/>
${requestScope.user.admin}<br/>
<h3>List对象</h3>
${requestScope.userList}<br/>
${requestScope.userList[3].userID}<br/>
${requestScope.userList[3].userName}<br/>
${requestScope.userList[3].birthday}<br/>
${requestScope.userList[3].age}<br/>
${requestScope.userList[3].admin}<br/>
<h3>Map对象</h3>
${requestScope.userMap}<br/>
本场景中,由于Map中的键是随机数,EL无法通过键获取Map元素,但可以通过jstl遍历的方式,获取map中的元素。
使用EL获取pageContext对象
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<h3>使用EL获取pageContext对象</h3>
${pageContext['request']}<br/>
${pageContext.session}<br/>
${pageContext['servletContext']}<br/>
${pageContext.request.servletContext}<br/>
${pageContext.response}<br/>
${pageContext.request.contextPath}<br/>
<%= pageContext.getRequest().getServletContext().getContextPath() %><br/>
<%= pageContext.getServletContext().getContextPath() %><br/>
作为EL的内置对象,最常用的还是在jsp页面互殴Web项目虚拟路径${pageContext.request.contextPath}
使用EL获取HTTP请求头和Cookie请求头
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<h3>使用EL获取HTTP请求头和Cookie请求头</h3>
${header}<br/><br/>
${headerValues}<br/><br/>
${header['user-agent']}<br/><br/>
${header.cookie}<br/><br/>
${cookie}<br/><br/>
${cookie.userName}<br/>
${cookie.userName.name}<br/>
${cookie.userName.value}<br/>
${cookie.userName.maxAge}<br/>
${cookie.userName.secure}<br/>
${header['user-agent']}不能写成${header.user-agent},这是因为“user-agent”中包含短横线,这不是有效的Java变量名
我们知道EL的header以Map<String,String> 获取并输出HTTP所有请求头
而headerValues以Map<String,String[]>获取和输出HTTP所有请求头
Cookie以Map<String,String> 获取并输出Cookie所有请求头
${header.cookie}将请求头中的Cookie以字符串的形式输出
empty运算符的使用
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<h3>empty运算符的使用</h3>
${empty requestScope.emptyString}<br/>
${empty requestScope.nullValue}<br/>
${empty requestScope.emptyList}<br/>
${empty requestScope.emptyMap}<br/>
${empty requestScope.emptySet}<br/>
${empty requestScope.notExist}<br/>
${empty requestScope.zeroInt}<br/>
${empty requestScope.zeroFloat}<br/>
${empty requestScope.falseValue}<br/>
${empty requestScope.blank}<br/>
${"1" + "2"}
EL关键字有如下:
and、eq、gt、true、instanceof、or、ne、le、false、empty、not、lt、ge、null、div、mod
注意事项有两点:
1. 0不代表空
2. 加号表示算术运算符
EL支持+、-、* 、/(或者div)、%(或者mod)、<(或者lt)等等运算符,还支持三目运算符就是?:
JSTL
jsp标准标签库
JSTL不是jsp的规范,所以要下载资源放入WEB项目的lib下
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
必须先声明JSTL核心标签库,才能够在JSP页面中使用jstl核心标签库,标签的前缀定义为c(core单词的首字母)
小露身手:JSTL核心标签库的使用
创建if.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<c:if test="${empty param.sex}">
性别:未知
</c:if>
<c:if test="${param.sex=='m'}">
性别:男
</c:if>
<c:if test="${param.sex=='f'}">
性别:女
</c:if>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<c:choose>
<c:when test="${empty param.score}">
成绩未提供
</c:when>
<c:when test="${param.score>=90 and param.score<=100}">
优秀
</c:when>
<c:when test="${param.score>=80 and param.score<90}">
良好
</c:when>
<c:when test="${param.score==85}">
85
</c:when>
<c:when test="${param.score>=60 and param.score<80}">
及格
</c:when>
<c:when test="${param.score<60 and param.score>=0}">
不及格
</c:when>
<c:otherwise>
成绩超出范围!
</c:otherwise>
</c:choose>
localhost:8080/jstl/choose.jsp?score=85
<c:forEach>标签 的使用
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<h3>JSTL循环的使用</h3>
<hr/>
<c:forEach items="${requestScope.userList}" var="user" varStatus="status" >
${status.count}->${user.userID}->${user.userName}->${user.birthday}->${user.age}
<c:if test="${user.admin==true}">是管理员</c:if>
<c:if test="${user.admin!=true}">不是管理员</c:if>
<br/>
</c:forEach>
<hr/>
<c:forEach items="${requestScope.userMap}" var="map" varStatus="status" >
${status.count}->${map.key}->${map.value.userID}->${map.value.userName}->
${map.value.birthday}->${map.value.age}
<c:if test="${map.value.admin==true}">是管理员</c:if>
<c:if test="${map.value.admin!=true}">不是管理员</c:if>
<br/>
</c:forEach>
<hr/>
<c:forEach var="i" begin="1" end="10" step="2">
${i}
</c:forEach>
该标签有两种用法
语法1:是遍历集合(collection或者map)
<c:forEach [var = "item"] items="collection" [varStatus="varStatusName"]>
[]表示可选属性
items的属性值collection用于指定被遍历的集合,注意这个不是字符串啊
var属性的item用于存放遍历过程中的当前元素,item作为属性名,被绑定在了pageContext域对象,作用对象是pageScope
varStatus的属性值varStatusName是一个JavaBean的对象,记录了遍历的状态
varStatusName.index
varStatusName.count
语法2:循环指定的次数
< c: forEach var="每个变量名字" items="要迭代的list" varStatus="每个对象的状态"
begin="循环从哪儿开始" end="循环到哪儿结束" step="循环的步长">
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
${requestScope.userName}
<c:remove var="userName" scope="request"/>
${requestScope.userName}
这个标签的作用是在JSP页面中实现解除绑定作用域中的属性的功能