JSP

Java Sever Page,本质就是 Servlet,初有 Servlet 时,Servlet既要处理业务逻辑又要负责输出html,由于在其中输出html太过繁琐,于是推出了JSP技术专注于显示。

  • Introduction
  • 指令
    • page 指令
    • include 指令
    • taglib 指令
    • 内置对象
  • 内置标签
    • <jsp:forward标签>
    • <jsp:include>标签
    • JSP中使用JavaBean
  • ExpressionLanguage
    • 使用EL内置对象获得数据
      • EL的内置对象:
      • EL表达式内置对象的操作:
      • 使用EL表达式可以获取JAVABEAN属性、集合项的值
      • 使用EL可以进行逻辑运算
      • EL函数库
        • 自定义EL函数库
        • 系统自带的EL函数.

Introduction

运行原理图 运行原理

  • JSP由 HTML, JSP脚本和标签构成
  • HTML代码使用 out.write()输出
  • JSP脚本使用<% %> 程序代码标签包围Java代码,即JSP脚本,其中不可以定义方法。在该脚本中写的java代码最终会生成到jsp对应Servlet类中的service方法中。
  • 声明标签:<%! int i = 0; %>
  • 表达式: <%= %>out.print();
  • JSP注释
    • 服务器端注释: <%-- --%>被注释掉的内容不会参与编译,转化阶段消失,只能被开发人员看到
    • html注释: <!-- --> 被注释掉的内容不显示但是不会消失

指令

指令元素用于指示JSP执行某些步骤或者指示JSP表现特定行为,语法格式<%@ directive [ attribute =“value” ] * %>

page 指令

用于定义JSP文件的全局属性,属性包含在<%@ page%>之间

属性:

  • language: 声明使用脚本的种类,如language="java"
  • import:用来导包或类的列表,唯一一个可以出现多次的标记属性
  • contentType="text/html;charset=UTF-8":响应浏览器时,告诉浏览器JSP页面响应的MIME类型以及用什么码表解码
  • pageEncoding="UTF-8”:指定服务器读取jsp时采用的编码格式,其优先权高于contentType, 以上两个属性,指定一个,另一个会自动指定
  • session:是否需要使用session对象,设置为false,则session内置对象会消失。默认为true,一般不用修改
  • errorPage:处理异常事件是调用的JSP页面(不常用)
  • isErrorPage:设置此页是否可为其他页errorPage的目标,值取“true”或"false”(不常用)
  • buffer="8kb”:指定对客户端输出缓冲区的大小(不常用)
  • autoFlush: 当buffer溢出时,如果该属性设置为true,会将缓存自动输出到浏览器,设置为false,将会报错。(不常用)
  • extends:指定继承类,需要加入Java class的全名,默认继承org.apache.jasper.runtime.HttpJspBase,通过该属性可以改变,但必须是HTTPServlet的子类。(不用)
<!-- 错误页面可以在web.xml中使用如下统一配置  -->
<error-page>
    <error-code>500</error-code>
    <location>/zhiling/Demo2.jsp</location>
</error-page>

include 指令

静态包含,在JSP页面中静态包含一个文件,同时由JSP解析包含的文件内容

静态包含

语法格式:<%@ include file=“filename” %>

  • file不能为一变量,如:
<% String url="index.html" ; %>
<%@ include file = "<%= url %>" %>
  • file所指定的文件后不可以再接任何参数,如:
<%@ include file = "jw.jsp?nm=browser" %>

demo1.jsp

<body>
常见写法:<br>
<%@ include file="Demo2.jsp" %>
<%=i %>
</body>

demo2.jsp

haha
xixi
heihei
hehe
gaga
hiahia
<%
    int i = 10;
%>

taglib 指令

引入标签指令,允许用户使用标签库自定义新的标签,例如常用的标签库:JSTL

语法格式:<% taglib uri="taglibURI" prefix="tabPrefix"%>

  • uri - 标签文件的URI地址
  • prefix - 标签组的命名空间前缀

taglib指令中的uri属性用于根据标签的前缀对自定义的标签进行唯一的命名,其值可以是相对路径,绝对路径或标签库描述文件。

内置对象

 public void _jspService(HttpServletRequest request, HttpServletResponse response)
            throws java.io.IOException, ServletException {
        PageContext pageContext = null;
        HttpSession session = null;
        Throwable exception = org.apache.jasper.runtime.JspRuntimeLibrary.getThrowable(request);
        if (exception != null) {
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        }
        ServletContext application = null;
        ServletConfig config = null;
        JspWriter out = null;
        Object page = this;
        //……
    }
类型 变量名称
HttpServletRequest request
HttpServletResponse response
HttpSession session
Throwable exception
ServletContext application
ServletConfig config
Object page
JspWriter out
PageContext pageContext
  1. request对象是javax.servlet.http.HttpServletRequest类的实例,它代表了客户端的请求信息,主要用于接受通过HTTP协议传送到服务器的数据(头信息、系统信息、请求方式以及请求参数等)

  2. response对象是javax.servlet.HttpServletResponse类的实例。它代表了客户端的响应,它提供了一系列方法来获取HTTP头信息,cookies,HTTP方法,表单数据等

  3. session对象是javax.servlet.http.HttpSession类的实例,主要用来跟踪在各个客户端请求间的会话

  4. application是javax.servlet.ServletContext类的实例。ServletContext是全局对象,可用于存放共享数据

  5. out对象主要向输出流写入内容,jsp中使用JspWriter在向外输出内容
    工作原理 response.getWriter 和 JspWriter 有什么区别?

    • response.getWriter的输出会出现在JspWriter输出的前面
    • JspWriter缓存会附加到response.getWriter缓存后,最终输出response.getWriter缓存
      注意:JSP中不要直接使用response.getWriter
  6. PageContext对象主要作用是获取任何范围的参数

    1. page域:范围只在当前页面当中(4个域中最小一个域)

    存值: pageContext.setAttribute("", "");
    取值: pageContext.getAttribute("");
    删除一个值: pageContext.removeAttribute("");

    1. pageContext操作其他3个域:

    存值: pageContext.setAttribute("name", "tom",PageContext.REQUEST_SCOPE );
    取值: pageContext.getAttribute("name", PageContext.REQUEST_SCOPE);
    删除一个值: pageContext.removeAttribute("name", PageContext.REQUEST_SCOPE);
    查找各域中的属性:pageContext.findAttribute();
    遍历所有键: pageContext.getAttributeNamesInScope(PageContext.REQUEST_SCOPE);

    1. 获得其他8个内置对象:
      jsp主要负责显示,所以应该尽量减少jsp中Java代码,当逻辑代码转移到Java类中时,需用用到jsp的内置对象,此时就只需要传递一个PageContext对象就可以获得其他对象

    pageContext.getRequest();
    pageContext.getResponse();
    pageContext.getSession();
    pageContext.getServletContext();
    pageContext.getServletConfig();
    pageContext.getOut();
    pageContext.getException();
    pageContext.getPage();

  7. page对象代表JSP本身,是页面实例的引用,指向了当前JSP程序本身的对象(this)

  8. config对象是javax.sevlet.ServletConfig类的实例,主要用于取得服务器的配置信息。servletConfig对象仅对servlet自身有效,一个servlet的ServletConfig对象不能被另一个servlet访问。

  9. exception对象包装了从先前页面中抛出的异常信息,主要用于产生对出错条件的适当响应。

内置标签

内置标签,不需要引入,用于在jsp页面中提供业务逻辑功能,避免在JSP页面中直接编写java代码造成jsp页面难以维护

<jsp:forward标签>

<jsp:forward page="/jspTag/Demo2.jsp"> 
       <jsp:param  name="name" value="tom" ></jsp:param>
</jsp:forward>
<!-- <jsp:forward>之后的代码不执行  -->

接受用户输入并将请求分派给另一页面,相当于如下java代码:

request.getRequestDispatcher("").forward(request, response);

<jsp:include>标签

动态包含

动态包含

<jsp:include page="/jspTag/Demo2.jsp"></jsp:include>

请求时文件包含,相当于如下java代码:

request.getRequestDispatcher("").include(request, response);

动态包含与静态包含的区别: include用法的区别

JSP中使用JavaBean

JSP技术提供了三个关于JavaBean组件的动作元素,即JSP标签,它们分别为:

<jsp:useBean>标签

用于在指定的域范围内查找指定名称的JavaBean对象。如果存在则直接返回该JavaBean对象的引用,如果不存在则实例化一个新的JavaBean对象并将它以指定的名称存储到指定的域范围中。

语法:
<jsp:useBean id="beanName" class="po.User" scope="page|request|session|application"/>

  • id属性用于指定JavaBean实例对象的引用名称和其存储在域范围中的名称
  • class属性用于指定JavaBean的完整类名(即必须带有包名)
  • scope属性用于指定JavaBean实例对象所存储的域范围,其取值只能是page、request、session和application等四个值中的一个,其默认值是page
<jsp:useBean id="u" scope="page" class="cn.beans.User" >
</jsp:useBean>

相当于如下java代码:

User user = new User();
pageContext.setAttribute("u", user);

<jsp:setProperty>标签

用于设置和访问JavaBean对象的属性。

语法:

<jsp:setProperty name="beanName" 
{
  property="propertyName" value="{string | <%= expression %>}" |
  property="propertyName" [ param="parameterName" ] |
  property= "*"
}/>
  • name属性用于指定JavaBean对象的名称。
  • property属性用于指定JavaBean实例对象的属性名。
  • value属性用于指定JavaBean对象的某个属性的值,value的值可以是字符串,也可以是表达式。为字符串时,该值会自动转化为JavaBean属性相应的类型,如果value的值是一个表达式,那么该表达式的计算结果必须与所要设置的JavaBean属性的类型一致。
  • param属性用于将JavaBean实例对象的某个属性值设置为一个请求参数值,该属性值同样会自动转换成要设置的JavaBean属性的类型。
<jsp:setProperty property="name" name="u" param="name" />

相当于如下java代码:

((User)pageContext.getAttribute("u")).setName(request.getParameter("name"));

<jsp:getProperty>标签

用于读取JavaBean对象的属性,也就是调用JavaBean对象的getter方法,然后将读取的属性值转换成字符串后插入进输出的响应正文中。

语法 <jsp:getProperty name="beanInstanceName" property="PropertyName" />

  • name属性用于指定JavaBean实例对象的名称,其值应与jsp:useBean标签的id属性值相同
  • property属性用于指定JavaBean实例对象的属性名
  • 如果一个JavaBean实例对象的某个属性的值为null,那么,使用jsp:getProperty标签输出该属性的结果将是一个内容为“null”的字符串
<jsp:getProperty property="name" name="u"/>

相当于如下java代码

out.print(((User)pageContext.getAttribute("u")).getName());

Demo: demo1.jsp

<body>
<form action="/Review/jsp/Demo4.jsp" method="post">
    用户名:<input type="text" name="name"/><br>
    密码:<input type="password" name="password"/><br>
    <input type="submit"/><br>
</form>
</body>

demo2.jsp

<body>
<jsp:useBean id="u" scope="page" class="po.User"></jsp:useBean>
<jsp:setProperty property="name" name="u" param="name"/>

<jsp:getProperty property="name" name="u"/>
</body>

以上代码相当于:

<%
    po.User user = new po.User();
    pageContext.setAttribute("u", user);

    ((po.User) pageContext.getAttribute("u")).setName(request.getParameter("name"));

    out.print(((po.User) pageContext.getAttribute("u")).getName());
%>

ExpressionLanguage

EL封装内省的操作,可以通过EL替代页面上的代码操作JAVA BEAN

使用EL内置对象获得数据

EL的内置对象:

requestScope 、sessionScope、applicationScope、pageScope 通过以上4个内置对象可以对4大域进行访问 以下内置对象用处不大: param、paramValues(返回string[]) - 这两个对象封装了表单参数: header、headerValues - 这两个对象封装了HTTP请求头 initParam - 封装了web.xml中配置,初始化参数 pageContext - 封装了9大内置对象中的 cookie - 封装了cookie信息

注意事项:

  • 测试headerValues时,如果头里面有“-”,例Accept-Encoding,则要headerValues[“Accept-Encoding”]
  • 测试cookie时,例${cookie.key}取的是cookie对象,如访问cookie的名称和值,须${cookie.key.name}或${cookie.key.value}

EL表达式内置对象的操作:

格式: ${}

<%
  request.setAttribute("name", "requestTom");
  session.setAttribute("name", "sessionTom");
  application.setAttribute("name", "applicationTom");
  pageContext.setAttribute("name", "pageTom");
%>
${requestScope.name} - <% request.getAttribute("name"); %>
${sessionScope.name} - <% session.getAttribute("name"); %>
${applicationScope.name} - <% application.getAttribute("name"); %>
${pageScope.name} - <% pageContext.getAttribute("name");%>

EL表达式如果获得的值是null,那么什么都不打印

从最小的域开始,找key为name的属性值
${name} - <%pageContext.findAttribute("name");
    
下面试试剩下不常用的7个内置对象
param 获得单个值,paramValues 获得多个值,这两个对象封装了表单参数 
${param.name } - <% request.getParameter("name"); %>
${paramValues.habit } - <% request.getParameterValues("habit"); %>

header, headerValues 这两个对象封装了HTTP请求头
${header.Accept } - <%request.getHeader("Accept"); %>
${headerValues.Accept } - <%request.getHeaders("Accept"); %>

initParam 封装了web.xml中配置
${initParam.name } - <% application.getInitParameter("name"); %>

pageContex 封装了9大内置对象中的 pageContext
${pageContext.request.contextPath } - <% ((HttpServletRequest)pageContext.getRequest()).getContextPath(); %>

cookie封装了cookie信息
<%
  Cookie c = new Cookie("name","jerry");
  c.setMaxAge(60*60);
  response.addCookie(c); %>

${cookie.name.value } -->
<%
  for(Cookie cookie : request.getCookies()){
       if(cookie.getName().equals("name")){
            cookie.getName();
       }
  }
%>

使用EL表达式可以获取JAVABEAN属性、集合项的值

使用EL访问User对象:

<%
  User u = new User();
  u.setName("tom");
  request.setAttribute("user", u);
%> 
${requestScope.user.name} - <%((User)request.getAttribute("user")).getName(); %>
${requestScope.user['name']} - <%((User)request.getAttribute("user")).getName(); %>

使用EL访问数组:

<%
  String[] array = new String[]{"tom","jerry","jack","rose"};
  request.setAttribute("array", array);
%>
${requestScope.array[2]} 

使用EL访问List:

<%
    List<String> list = new ArrayList<String>();
    list.add("jack");
    list.add("rose");
    request.setAttribute("list", list);
%>
${requestScope.list[1]}

使用EL访问Map:

 <%
    Map<String,String> map = new HashMap<String,String>();
    map.put("birthday", "now");
    map.put("haha.heihei", "hiahia");
    request.setAttribute("map", map);
%>
${requestScope.map.birthday}
${requestScope.map['birthday']}
${requestScope.map['haha.heihei']}

使用EL可以进行逻辑运算

<%
  request.setAttribute("num1", 10);
  request.setAttribute("num2", 20);
%>
<!-- EL表达式支持如下运算符: -->
${num1 > num2} -> ${num1 gt num2}
${num1 < num2} -> ${num1 lt num2}
${num1 <= num2} -> ${num1 le num2} 
${num1 >= num2} -> ${num1 ge num2} 
${num1 == num2} -> ${num1 eq num2} 
${num1 != num2} -> ${num1 ne num2} 
${true && true}
${true || true}
${!true}
${(num1 > num2)?"num1厉害":"num2厉害" }

EL函数库

简化页面中静态方法的调用,使用EL函数代替JAVA代码

自定义EL函数库

先定义工具类,在类中定义静态方法并填写配置文件xxx.tld放到WEB-INF下除了classes和lib目录之外的任意子目录中 Tool.tool

package tool;

import java.text.SimpleDateFormat;
import java.util.Date;

public class Tool {
    public static String getTime() {
        return new SimpleDateFormat("hh:mm:ss").format(new Date());
    }

    public static void main(String[] args) {
        System.out.println(Tool.getTime());
    }
}

myFn.tld

<?xml version="1.0" encoding="UTF-8" ?>

<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
        version="2.0">
    <!-- 版本号没啥用 -->
    <tlib-version>1.0</tlib-version>
    <!-- 推荐使用的前缀 -->
    <short-name>myFn</short-name>
    <!-- 给该标签库起一个"包名",在页面中引入时使用 -->
    <!--TLD文件中的<uri>元素用指定该TLD文件的URI,在JSP文件中需要通过这个URI来引入该标签库描述文件-->
    <uri>http://www.iamwyj.com/myFn</uri>

    <!-- 注册一个EL函数 -->
    <function>
        <!-- 声明函数名称 -->
        <name>getTime</name>
        <!-- 注册静态方法所在的完整类名 -->
        <function-class>tool.Tool</function-class>
        <!-- 绑定类中的静态方法 -->
        <function-signature>java.lang.String getTime()</function-signature>
    </function>
</taglib>

之后在需要使用的页面中引入

<%@ taglib prefix="myFn" uri="http://www.itcast.cn/myFn"  %>

后再使用${myFn:getTime()}

Demo.jsp

<%@ page language="java" pageEncoding="UTF-8" import="tool.Tool" %>
<%@ taglib prefix="myFn" uri="www.iamwyj.com" %>
<%
    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 'Demo1.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">
</head>

<body>
<!-- 方式一 -->
<%=Tool.getTime() %><br>
<!-- 方式二 -->
${myFn:getTime()}<br>
</body>
</html>

访问后浏览器分别以上诉两种方式显示当前时间

系统自带的EL函数.
<!-- 判断是否包含 -->
${fn:contains("hiahia", "hi")} 
<!-- 判断是否以某字符串结尾(有BUG) -->
${fn:endsWith("abha", "ha")} 
<!-- 自动将html关键字符转义 -->
${fn:escapeXml("<font color='red'>haha</font>")}