JSP
java server page 运行在服务器端的页面。本质就是servlet。运行流程:
jsp(第一次访问时) => .java ==> .class ==> 运行
jsp第一次访问时,会生成一个java文件,然后运行对应的class文件。
在
\work\Catalina\localhost\jsp_simple\org\apache\jsp
下面可以看到一个java文件和对应的class文件。
JSP中的脚本
<% java代码 %> <!--该脚本包裹的代码会出现在 service方法中,有分号-->
<%=表达式 %> <!--该脚本是呈现某个值,直接使用会在 out.write()中。而<fmt:formatDate value="<%=new Date()%>" 作为参数直接传入而已,并不在out.write()中显示到页面。无分号-->
<%! 内容 %>: <!--该脚本包裹的内容会出现在类定义中,类方法或者类成员。有分号。>
对比<%-- --%> 注释。被注释的内容 不会出现在java文件中。
注解 <%-- -- %> 和 的区别
当我们在浏览器中审查源码的时候,<%–- -–%> 标记的内容是完全看不到的, 而\ 标记的内容不但可以看到,里面的内容还会被解析。
EL表达式
代替输出脚本 <%= %>
格式: ${表达式}
EL表达式可以在4个域中取数据 => 4个内置对象 requestScope / applicationScope / sessionScope / pageScope
举个例子
<head>
<%
request.setAttribute("name", "requestKey");
application.setAttribute("name", "applicationKey")
%>
</head>
<body>
<!-- 从指定域取值 -->
${requestScope.name}<br />
${applicationScope.name}<br />
<!-- 不从指定域取值,从小域到大域中查找.显示最先找到的,这里显示的是requestScope里的 -->
${name}<br />
</body>
JSP指令
page指令
page : <%@ page language="java" import="java.util.*, java.io.*" pageEncoding="UTF-8" autoFlush="true" buffer="8kb" errorPage="/page/error.jsp" %>
- page指令用于指定页面一些基本属性。
- language="java" :页面中使用的语言为java.
- import="java.util.*" :就是导包. 是所有属性中唯一一个可以在页面中出现多次的属性。比如上面的import="java.util.*, import="java.io.*
- pageEncoding="UTF-8" 页面保存到硬盘编码。
- contentType="text/html; charset=UTF-8" :发送给浏览器的编码.以上两个码表最好一致. 但是一般设置一个属性即可.另外一个属性自动设置
- autoFlush="true" buffer="8kb" :如果缓冲区装满是否自动刷新到浏览器. 如果装满并没有选择自动刷新,那么会抛出异常.buffer="8kb", 决定jsp输出缓冲区大小为8kb(默认)
- 在某个页面如index.jsp中配置errorPage="/page/error.jsp"配置当前页面的错误页面;在error.jsp中配置isErrorPage="true" . 指定当前页面是否是一个错误页面。true表示可以使用<%=exception.getMessage() %>打印出错误信息。
- extends="" 决定当前jsp的父类是谁.父类必须是servlet的子类.
- info="" getServletInfo 刚方法的返回值.
- isELIgnored="false" 决定当前页面能否使用 EL表达式. 默认值就是支持EL
- session="true" 当前jsp页面是否可以直接使用session对象.默认值就是true
include指令
include 属于静态包含 , 就是将两个jsp的代码合并。然后在共同编译成一个java文件,再编译成class再执行。
因为会先合并代码,所以变量可以共享。(意思即是两个jsp不能定义相同的变量,比如下面的。)两个jsp都有这个,include就会报错
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
MyJsp1.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'MyJsp1.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
This is my JSP page. <br>
// 静态包含了MyJsp2.jsp, 下面的str可以直接使用
<%@ include file="/include/MyJsp2.jsp" %>
<%=str %>
</body>
</html>
MyJsp2.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<% String str = "MyJsp2"; %>
<html>
<head>
<title>My JSP 'MyJsp2.jsp' starting page</title>
</head>
<body>
This is my 重复 page. <br>
</body>
</html>
访问MyJsp1.jsp就会打印
This is my JSP page.
This is my 重复 page.
MyJsp2
动态包含
<jsp:include page="/index.jsp"></jsp:include>
, 和下面的代码一样的效果。
<%
//request.getRequestDispatcher("").include(request, response);
%>
上面的包含就是之前学习的request的请求包含. 这种包含也叫做动态包含。动态包含的两个页面会分别编译成两个java,class.分别执行. 只是在最后输出时将输出结果合并. 所以页面中的变量不会共享。
九大内置对象
指的在jsp中不加以声明就可以直接使用的9个对象.
原理: 因为我们的代码是写在jsp对应java的service方法中的.所以在service方法中声明的变量,我们可以直接使用.
public void _jspService(final javax.servlet.http.HttpServletRequest ->1 request, final javax.servlet.http.HttpServletResponse response ->2)
throws java.io.IOException, javax.servlet.ServletException {
final java.lang.String _jspx_method = request.getMethod();
if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method) && !javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSPs only permit GET POST or HEAD");
return;
}
final javax.servlet.jsp.PageContext pageContext ->3;
javax.servlet.http.HttpSession session ->4 = null;
final javax.servlet.ServletContext application ->5;
final javax.servlet.ServletConfig config ->6;
javax.servlet.jsp.JspWriter out = null_ ->7;
final java.lang.Object page = this ->8;
javax.servlet.jsp.JspWriter _jspx_out -> 9 = null;
javax.servlet.jsp.PageContext _jspx_page_context = null;
这是第一次访问jsp自动生成的java文件_jspService方法源码 一部分。
对象名称 对象类型
request HttpServletRequest
response HttpServletResponse
session HttpSession
exception Throwable
application ServletContext
config ServletConfig
以上是以前学过的。
page Object 一般没用.
out JspWriter 用于向浏览器输出信息
pageContext PageContext 9大内置对象的首领.
out
JSPWriter和response.getWriter
<%out.write("a");
response.getWriter().write("b");
out.write("c");
response.getWriter().write("d");
%>
上面回输出bd ac
在输出到浏览器时,会先把两个流合并。再输出.
合并时response的字符流在前。JSPWriter在后。所以不管代码书写顺序如何, 最终
response流的内容总会在JSPwriter流的内容之前。
结论: 在jsp中输出使用out(JSPWriter)输出,不要使用response.getWriter输出。
page
page对象般没有用.
page对象 指向的就是 this(当前jsp生成Servlet对象)。使用情况一般是在开发框架时,框架需要用到JSP对象,进行一些页面的操作时,将page对象传过去即可。
pageContext
本身是一个域对象. 在pageContext对象上有一个map,这个Map就是Page。
范围: 就只在当前页面中有用,范围甚至比request域范围还小。
page域 < request域 < session域 < application域
就是在一个页面内的共享数据,本质是jsp编译后的java文件中service中的一个变量而已,只在当前页面的service方法内有效。
操作其他三个域
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<%
// PageContext可以操作4个域
//增
pageContext.setAttribute("name","requestTom", PageContext.REQUEST_SCOPE);
pageContext.setAttribute("name","sessionTom", PageContext.SESSION_SCOPE);
pageContext.setAttribute("name","applicationTom", PageContext.APPLICATION_SCOPE);
pageContext.setAttribute("name","pageTom", PageContext.PAGE_SCOPE);
//根据键获得值
pageContext.getAttribute("name", PageContext.REQUEST_SCOPE);
//删
// pageContext.removeAttribute("name",PageContext.REQUEST_SCOPE);
//查所有键
pageContext.getAttributeNamesInScope(PageContext.REQUEST_SCOPE);
// find 从4个域中找 ,从小到大.一旦找到立刻停止,并返回找到的.
pageContext.findAttribute("name");
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'page.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
request: <%=request.getAttribute("name") %><br>
session: <%=session.getAttribute("name") %><br>
application: <%=application.getAttribute("name") %><br>
findAttribute:<%=pageContext.findAttribute("name") %><br>
</body>
</html>
获得其他八个内置对象
<%
// PageContext可以获得其他8个内置对象
pageContext.getRequest();
pageContext.getResponse();
pageContext.getSession();
pageContext.getServletContext();
pageContext.getServletConfig();
pageContext.getException();
pageContext.getPage();
pageContext.getOut();
%>
page域的作用
用于标签处理类与JSP之前交互数据的"桥梁"。在jsp中应避免在页面上书写任何java代码,EL表达式的出现就是避免这种情况,将java代码写到EL表达式中而不是jsp。EL表达式中就有标签处理类。标签处理器要给jsp看的东西,只要放在page域中,jsp从中取出就行;反之,jsp也可以向page域放东西,让标签处理器拿到。
JSP中的动作标签
<%-- Jsp动作标签,分担jsp页面的java代码 --%>
<jsp:forward page="/index.jsp"></jsp:forward>
<%-- 上面的标签和下面转发的代码是一个效果 --%>
<%-- request.getRequestDispatcher("/index.jsp").forward(request, response); --%>
<jsp:include page="/index.jsp"></jsp:include>
<%-- request.getRequestDispatcher("/index.jsp").include(request, response) --%>
BeanUtils的使用
主要作用:把对象的属性比如键值对数据封装到对象中
这里把表单数据封装到user对象中。BeanUtils设置属性的时候依赖于底层的getter和setter方法
package beanUtils;
public class User {
private String name;
private String password;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User [name=" + name + ", password=" + password + "]";
}
}
表单的参数的键必须与Bean中属性名称对应。注意这里的name属性,name和password要和上面User的字段一样
<body>
<form action="/jsp/BeanUtils" method="post">
账号:<input type="text" name="name" /><br>
密码:<input type="password" name="password"> <br>
<input type="submit" value="登录">
</form>
</body>
package beanUtils;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
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 org.apache.commons.beanutils.BeanUtils;
@WebServlet("/BeanUtils")
public class Bean extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
User user = new User();
try {
BeanUtils.populate(user, request.getParameterMap());
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
System.out.println(user);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
BeanUtils.populate(bean,Map)
,其中Map中的key必须与目标对象中的属性名相同,否则不能实现拷贝。
JavaBean
- 要求为属性提供get/set方法任意之一
- 需要有空参构造
- 实现串行化接口(可选)
上面的User类
就是一个JavaBean。实现了getter和setter,默认空参。所以它是。
getParameter
获取到的表单数据都是String类型。加入输入年龄,想以int存入,自动完成。BeanUtils 可以自动帮你转换8个基本数据类型。但是输入日期,以Date存入怎么办?如果遇到自定义类型需要转换,我们要自己写一个转换器并注册。
自定义转换器并注册
1.自定义转换器
package beanUtils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.commons.beanutils.Converter;
public class MyConverter implements Converter {
/* 参数1: 需要转换成什么类型,register(new MyConverter(), Date.class)传进来的,这里就是Date.class
参数2: 待转换表单参数
返回值: 转换结果
*/
@Override
public Object convert(Class arg0, Object transIn) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
String dateString = transIn.toString();
try {
Date date = sdf.parse(dateString);
return date;
} catch (ParseException e) {
e.printStackTrace();
return null;
}
}
}
2. 转换器注册
// 参数1,转换器;参数2,支持的转换类型。即可以转换日期格式
// 注意:注册类型转换器,必须写在populate方法之前.
ConvertUtils.register(new MyConverter(), Date.class);
BeanUtils.populate(user, request.getParameterMap());
新增了填写年龄和入职日期(分别对应int和Date)
<form action="/jsp/BeanUtils" method="post" >
用户名:<input type="text" name="name" /><br>
密码:<input type="password" name="password" /><br>
年龄:<input type="text" name="age" /><br>
入职日期:<input type="text" name="date" /><br>
<input type="submit" value="登录" />
</form>
同时User也新增
private int age;
private Date date;
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
注意,导包Date都是java.util.Date而不是java.sql.Date,去import下检查!
JavaBean的属性只看get或者set属性,和成员变量个数没有关系。
JSTL
Java standard Tag Library => java标准标签库
JSTL标签用于代替,简化页面中的java代码。是apache组织提供一套已经开发好的标签库.
使用最多是core库,需要用到taglib指令。prefix="c"
表示前缀,自定义。我这里用c表示core的意思。
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
if标签
<body>
<!-- core库
c:if 判断 标签
-->
<%
request.setAttribute("num1",10);
request.setAttribute("num2", 100);
%>
<c:if test="${num1 > num2}">
num1 比较大!!
</c:if>
<c:if test="${num1 < num2}">
num2 比较大!!
</c:if>
</body>
if-else标签
<body>
<!-- core库
ifelse 标签
choose
when(可以出现多次)
otherwise
根据num1 num2 的大小,在页面提示值大的那个
-->
<%
request.setAttribute("num1",1000);
request.setAttribute("num2", 100);
%>
<c:choose>
<c:when test="${num1>num2}">
num1 比较大!
</c:when>
<c:when test="${num1==num2}">
num1 与num2 相等!
</c:when>
<c:otherwise>
num2 比较大!
</c:otherwise>
</c:choose>
</body>
forEach标签
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib prefix= "c" uri="http://java.sun.com/jsp/jstl/core" %>
...
<%-- 类选择器 --%>
<style type="text/css">
.one{
background-color: yellow;
}
.two{
background-color: blue;
}
</style>
</head>
<body>
<%-- <c:forEach>(常用) 遍历标签
items="${requestScope.list}" 要遍历的集合设置给该属性
var="abc" 每次遍历集合中元素 该属性值作为键放入page域
varStatus="st" 每次遍历的状态,会封装成一个对象 以该属性值为键 放入page域
数数的功能
begin="1" 从几开始数
end="100" 数到几
step="1" 每次数几个数
var="num" 将当前数的数以该属性值作为键放入page域
--%>
<%
List<String> list = new ArrayList<>();
list.add("tom");
list.add("jerry");
list.add("jack");
list.add("rose");
request.setAttribute("list", list);
%>
<table border="1">
<tr>
<th>用户名</th>
<th>当前遍历索引</th>
<th>当前遍历计数</th>
<th>是否是集合第一个元素</th>
<th>是否是集合最后一个元素</th>
</tr>
<%-- 奇数行选择one即黄色,偶数行选择two即蓝色 --%>
<%-- var这里是为传入的列表里面的子项定义一个键,可以看成是for (A a : aList)里面的a -->
<c:forEach items="${list}" var="name" varStatus="st" >
<tr class="${st.index%2==0?"one":"two"}" >
<td>${name}</td>
<td>${st.index}</td>
<td>${st.count}</td>
<td>${st.first}</td>
<td>${st.last}</td>
</tr>
</c:forEach>
</table>
<hr>
<!-- 数数的功能-->
<c:forEach begin="1" end="10" step="2" var="num" >
${num}
</c:forEach>
</body>
</html>
长这样
fmt库 格式化库
格式化日期
<body>
<%--
格式化日期
fmt:formatDate
pattern:转换成的格式 var:将已经格式化好的以这个键(这里是date)放入 scope中(这里是request域
--%>
<fmt:formatDate value="<%=new Date()%>"
pattern="yyyy/MM/dd hh:mm:ss" var="date" scope="request" />
${requestScope.date}
</body>
格式化数字
<body>
<!--
格式化数字
fmt:formatNumber
-->
<fmt:formatNumber value="3.1415926" pattern="0000.00000000000" var="num1" scope="request" ></fmt:formatNumber>
<fmt:formatNumber value="3.1415926" pattern="####.###########" var="num2" scope="request" ></fmt:formatNumber>
${requestScope.num1}<br>
${requestScope.num2}<br>
分别输出,两种方式的区别不言自明。
0003.14159260000
3.1415926
路径填写问题
路径总结
前提: 所有路径都应以"/"开头.
项目名: jsp
资源名: AServlet
客户端路径 => 给浏览器用的路径 => 填写项目名称
<form action="/jsp/AServlet" > 提交到浏览器
![](/jsp/AServlet) 浏览器从哪个路径加载资源
<a href="/jsp/AServlet" > 用户点击链接后,浏览器应该加载什么资源
response.sendRedirect("/day10-jsp/AServlet") 重定向,告诉浏览器转向哪儿
服务器端路径 => 给服务器端使用的路径 => 填写项目下的路径
request.getRequestDispatcher("/AServlet") 转发。服务器内部事务
errorPage="/AServlet" 出现错误,转发到该页面
<location>/AServlet</location> 出现某一个状态码,转发到这个页面
<url-pattern>/Aservlet</url-pattern> 告诉服务器,这个路径能访问到这个资源
MVC思想
javaee三层架构
为什么不在Service中处理所有的业务逻辑而需要DAO呢?
假设一个银行系统,有转账和取钱功能。无非就是减钱和加钱的调用。将取钱和加钱的功能封装,Service中的转账就调用加钱和减钱的方法。取钱功能就调用减钱。做到了进一步的解耦。
javaee三层架构和MVC思想的区别
- 概念不同,一个是架构一个是思想
- MVC范围更大,三层架构是在javaee
- javaee三层架构符合MVC设计思想。如JSP是View部分,Servlet是control部分。而Service和Dao共同组成Model部分
by @sunhaiyu
2017.4.10