JSP速查

One Page速查是将一类技术的全部知识点用一个网页进行归纳整理,便于开发的时候对忘记的知识点进行速查。它起到提示作用,内容涉及知识点罗列,简单示例,原理介绍。但不涉及知识的详细解说。

JSP语法

JSP中存在以下元素,构成了JSP的语法:

  1. Standard directives 标准指令

  2. Standard actions 标准行动

  3. Scripting elements 脚本元素

  4. Expression language 解释语言

  5. Custom Actions 扩展行为(可扩展tag,也可扩展EL函数)

  6. Template content 静态内容

然而,除了JSP的标准语法之外,为了还有更加简洁的页面数据访问和逻辑运算工具存在,比如JSTL,Struts Tags和OGNL.

JSP也需要在web.xml中进行配置。配置的内容包括后缀,编码格式,taglib等等。

生命周期

JSP必须运行于JSP容器中。JSP生命被分为两个阶段——Translation phase和Request phase. 一个JSP在被request前,必须先进行编译,转为jspServlet,这个过程为translation phase. 当用户的请求发送给jsp的时候,请求实际是由jsp编译好的servlet进行处理的。

jspServlet中存在这么几个方法_jspInit(), _jspService(), _jspDestroy(). 跟servlet非常像。

Tag文件在编译极端,则被转为SimpleTagSupport,它存在_jspInit(), _jspDestroy(), doTag(). 

在最后再来探讨JSP如何生成JAVA类的。

Standard Directives

标准指令的语法为<%@ directives {attr="value"}* %>
JSP和Tag文件的标准指令有以下几种,其中Available的值表示此指令是否可以用于tag文件:

其中最复杂的是page指令。page指令用于生命本页面的属性,控制本页面的功能。下面列出了page所包含的所有可用属性:

page_directive_attr_list ::= 
  { language=”scriptingLanguage”}
  { extends=”className” }
  { import=”importList” }
  { session=”true|false” }
  { buffer=”none|sizekb” }
  { autoFlush=”true|false” }
  { isThreadSafe=”true|false” }
  { info=”info_text” }
  { errorPage=”error_url” }
  { isErrorPage=”true|false” }
  { contentType=”ctinfo” }
  { pageEncoding=”peinfo” }
  { isELIgnored=”true|false” }
  { deferredSyntaxAllowedAsLiteral=”true|false”}
  { trimDirectiveWhitespaces=”true|false”}

指令taglib比较简单,定义本页面所要使用的tag,语法如下:

<%@ taglib ( uri=”tagLibraryURI” | tagdir=”tagDir” ) prefix=”tagPrefix” %>

指令include和<jsp:include>很像,区别在于前者先静态导入文件的编码,然后再提交给jsp容器编译,此发生在jsp translation phase;而后者则向jsp容器请求引入jsp片段的request后的内容,此发生在jsp request phase.

<%@ include file=”relativeURLspec” %>
<jsp:include page="" />

指令tag类似于page指令,tag指令只存在于tag文件中。其所有属性均为Optional.

tag_directive_attr_list ::=
  { display-name=”display-name” }
  { body-content=”scriptless|tagdependent|empty” }
  { dynamic-attributes=”name” }
  { small-icon=”small-icon” }
  { large-icon=”large-icon” }
  { description=”description” }
  { example=”example” }
  { language=”scriptingLanguage” }
  { import=”importList” }
  { pageEncoding=”peinfo” }
  { isELIgnored=”true|false” }
  { deferredSyntaxAllowedAsLiteral=”true|false”}
  { trimDirectiveWhitespaces=”true|false”}

指令attribute是tag文件中的指令,用于定义此tag可以接受什么类型的参数:

attribute_directive_attr_list ::=
  name=”attribute-name”
  { required=”true|false” }
  { fragment=”true|false” }
  { rtexprvalue=”true|false” }
  { type=”type” }
  { description=”description” }
  { deferredValue=”true|false” }
  { deferredValueType=”type” }
  { deferredMethod=”true|false” }
  { deferredMethodSignature=”signature” }

Example: <%@ attribute name=”y” type=”java.lang.Integer” %>

指令variable是tag在执行的时候向taghandler所传递的参数:

variable_directive_attr_list ::=
  ( name-given=”output-name”
    | ( name-from-attribute=”attr-name”
        alias=”local-name”
      )
  )
  { variable-class=”output-type” }
  { declare=”true|false” }
  { scope=”AT_BEGIN|AT_END|NESTED” }
  { description=”description” }

ContentType与pageEncoding

在page指令中,存在两个属性:

  • contentType="text/html;charset=utf-8";

  • pageEncoding="GBK"

从上面可以看出,pageEncoding与contentType中的charset都生命了字符集名称,为什么要声明两遍,并且声明的字符集却不同呢?解释如下:

pageEncoding是在translation phase使用的。它声明的是此jsp文件是以什么形式的编码存储的。jsp容器读入jsp文件,编译为class文件,则以utf-16编码格式来存储。contentType的charset是在request phase使用的。当request到来,jsp容易将jspServlet产生的html内容,以charset的编码形式发送给客户。

<%@ page contentType="text/html;charset=utf-8" pageEncoding="GBK" %>这个例子说明,我的jsp文件是GBK编码,jsp编译器应该使用GBK编码形式读取我的jsp文件。当用户请求到来以后,jsp容易必须把jsp产生的内容以utf-8的编码格式发给用户。

由此引出浏览器对中文字符参数的编码格式。

以"http://www.test.com?nihao=你好"为例,当用户第一次把URL输入到浏览器地址栏,并点击发送请求以后,浏览器并不知道该网站的contentType,所以并不知道应该用什么编码来转换“你好”两个字。这时候,浏览器会使用浏览器设置的默认编码格式对“你好”两个字转换。

中文操作系统下浏览器的默认语言编码是GBK,“你好”的GBK编码为“C4 E3 BA C3”,所以,URL被转义为:
http://www.test.com?nihao=%C4%E3%BA%C3

页面请求返回以后,返回的http header中存在contentType="text/html;charset=utf-8". 在此页面上,存在一个<a href="http://www.test.com?nihao=你好"/>,若用户点击这个link,由于浏览器已经知道此网页的编码格式为utf-8,"你好"的UTF-8编码为“e4bda0e5a5bd”,所以URL将被转义为:
http://www.test.com?nihao=%E4%BD%A0%E5%A5%BD"

Standard Actions

以下表格是JSP的标准行为。

  • !表示required

  • =表示此属性为rtexprvalue接受expression



 


Scriptlets

JSP中的java脚本包含4种:

  • 声明 <%! int a=1, b=2, sum;%>

  • 脚本 <% sum=a+b; %>

  • 输出 <%=sum%>

  • 注释 <%--comments --%>

注意<!-- -->与<%-- --%>的区别。前者中的内容会输出给用户,只是在HTML渲染的时候不会显示,后者在jsp编译的时候,直接变成java代码的注释。

在脚本中,有一些隐藏的变量可以直接使用,例如 <%=request.getContextPath()%>. 所有的隐藏变量如下表:


exception变量时在指令page中的isErrorPage="true"的时候,才会存在。

如果脚本中将使用其他类,则必须在<%@ page import=""%>中声明要引入的类。

Expression Language

在MVC的时代,EL+Custom Actions几乎取代了java scriptlets。在JSP中,再也看不到scriptlets的身影了。无论是struts,jstl,EL都大大的规范了JSP的用途,jsp已经变成了纯粹的View层,代码逻辑运算都转到了Controle和Model层。而JSP中简单的逻辑运算都是为展现所服务的。这样的JSP页面非常干净,便于维护。

EL提供了一些隐藏变量。比如${pageContext.request.contextPath}。

注:EL中的function可以通过custom actions来扩展。

下面列表是EL中的隐藏变量:

EL可以出现在两种地方:

  • Template content (isELIgnored=false)

  • Actions' attributes (isELIgnored=false, attribute=rtexprvalue)

JSP Document and Javascript Object

Web service和web 2.0的兴起,是的通过HTTP传输的XML越来越多。JAXP可以产生XML,JSP也可以动态产生XML。因为JSP的所见即所得编辑模式,加上JSP中各种好用EL,actions,使得JSP更加容易生成XML。

JSP编译器判断此JSP页面是不是XML Document的方法在于:

  1. web.xml中的<jsp-property-group>中声明了<is-xml>为true。或者

  2. 在web.xml 2.4版本中,后缀为jspx的jsp页面。或者

  3. 为了兼容jsp1.2,jsp起始位<jsp:root>的jsp页面。

出了XML,现在JSON也比较流行,有时候会使用JSP来产生JSON实例。除此外,JSP可以用于伪装各种javascript的返回值。

<%@ page contentType="text/javascript;charset=utf-8" %>
true&&{a:1, b:2}

<%@ page contentType="text/javascript;charset=utf-8" %>
true&&function(){}

以上两个例子一个返回json,一个返回一个function。为什么要在前面加“true&&"呢?因为eval的原因。AJAX在拿到返回内容以后,会调用eval(responseText)来执行其中含有的javascript。请做个实验,eval方法中直接传function或者json的话,是不会成功的,因为它们不是一个具有返回值得表达式。所以,将它们伪装成一个&&表达式: eval(true&&a)永远返回a。

Custom actions

custom action存在两种写法:

  1. Java Class (tag handler implements Tag, IterationTag, or BodyTag, extends TagSupport, BodyTagSupport, or SimpleTagSupport).
    TLD
    <%@ taglib uri="" prefix="" %>

  2. *.tag (将被编译成java tag handler, extends SimpleTagSupport).
    <%@ taglib tagdir="" prefix="" %>

每当用到custom action的时候,都需要TLD生成的validator来验证语法的正确性。所以在jsp中,必须要能建立action和tld的联系。

TLD的放置位置可以为:

  1. J2EE container自带提供的。(隐式加载)

  2. /WEB-INF/lib/*.jar/META-INF 或者其子目录。(隐式加载)

  3. /WEB-INF/ 或者其子目录。(lib, classes, tags除外)(显示加载)

TLD与uri的建立mapping关系:

  1. J2EE container自带提供的,优先级最高。使用TLD中的uri和本身建立mapping。

  2. web.xml中声明uri和tld位置的对应关系,加载并建立mapping。在web.xml中声明的uri可以为一个别名,不一定要和TLD中的uri相同。

  3. 扫描WEB-INF下面和*.jar/META-INF下面的TLD,使用TLD中的uri和本身建立mapping。

Action的prefix与TLD建立联系:

  1. 基于上一步创建的uri mapping关系,在jsp中声明<%@ taglib uri="http://my.test/core" prefix="c"%>

  2. 或者,撇弃uri mapping关系,直接使用<%@ taglib uri=”/WEB-INF/tlds/core.tld” prefix=”c” %>

编写tag文件,则不需要这么复杂,它不需要java,不需要tld。编写好的tag文件可以放在

  1. WEB-INF/tags或者其子目录中

  2. 也可以放在*.jar/META-INF/tags下面,但这需要相应的tld进行uri mapping的声明。

常用的第三方actions

  1. JSTL, 安装JSTL的支持jar包。TLD应该已经在jar包中,如果没有则导入WEB-INF然后在web.xml中声明。
    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
    <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
    <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
    <%@ taglib uri="http://java.sun.com/jsp/jstl/sql" prefix="sql" %>

  2. Struts2

  3. Spring

Actions当中的几个疑点

1, <c:set var=”v“ value="">生成的变量是否可以在JSP的引入文件,或者tag文件中被访问?

<%@ include file="a.jspf" %> 所引入的文件是可以的,因为a.jspf在父页面编译之前,已经被copy到了父页面中。
<jsp:include page="a.jsp"/>不可以,因为a.jsp是由容器单独编译,虽然父页面的jspContext传给了a.jsp,但是a.jsp的jspContext=wrapper(jspContext). 他们不绝对相等,而是做了一层装饰。这使得c:set产生的变量紧紧局限于当前jsp产生的类中。
<prefix:tag>文件中也不可以,原因同上。
<prefix:tag> ${v}</prefix:tag>中的body可以,因为body在编译过程中,会产生一个当前jsp页面的嵌套类,它们共享一个jspContext。

2, JSP主页面如何和其中的actions进行传值?

在tag中声明:
向内传值,<%@ attribute ...%> JSP可以向tag传值
向外传值,<%@ variable scope="" ... %> tag可以向JSP传值, variable产生的变量会被同步到父页面的pageContext中去。

3, <my:tag>body</my:tag>中,body和tag文件的pageContext是不一样的

body和tag文件会被编译成两个不同的class类,body形成的类嵌套在父页面生成的类中,它们共享一个pageContext。而tag生成的类,有自己的pageContext。

在action的类中,操作body的执行:

  • java 类中,jspFragment=getJspBody(); jspFragment.invoke()

  • tag文件中,<jsp:doBody/>

4, <%@ variable%>的scope表示的意义:

AT_BEGIN variable在doBody之前要写会父页面的pageContext中,在tag结束的时候,再写一次。
AT_END    variable在tag执行结束的时候,写一次。
NESTED    在doBody之前,首先保存父页面的变量状态,然后将variable写回父页面,在执行完doBody之后,tag结束的时候,将variable从父页面抹去,并回复之前父页面的变量状态。可见这个scope是为doBody服务的。把tag中的变量作用域限制在tag执行和doBody之中。

5, Variable属性name-from-attribute的意义:

用于给variable起名字。它的名字是从父页面通过attribute传递进来的。增加了action的通用性,不必把变量的名字写死。例如:

parent.jsp
<my:tag attribute="i">
   variable i = ${i}
</my:tag>

my.tag
<%@ attribute name="varName" required="true" rtexprvalue="false" %>
<%@ variable name-from-attribute="varName" variable-class="java.lang.String" scope="NESTED" alias="j"%>

<c:set var="j" value="hello"/>
<jsp:doBody/>

编译成类

这里并不讲如何用container提供的工具,提前把jsp编译成class.这里讲一个jsp被编译成java代码,是什么样子的。任何一个web container都可以设置参数,是否保留jsp编译以后的java文件。这样可以去debug你的jsp。

接下来讲的是jsp与java代码的转换。这里使用的都是伪代码:

High level来看一个jsp或tag总共会产生2个大类,而大类中会有嵌套类。

<html>
<body>
  <my:tag>tagBody</my:tag>
  <my:tag>tagBody2</my:tag>
</body>
</html>

以上jsp会产生以下类:

parent.jsp

public final class parent_jsp extends HttpJspBase {
  private javax.el.ExpressionFactory _el_expressionfactory;
  
  public void _jspInit() {
    _el_expressionfactory = ...;
  }
  
  public void _jspDestroy() {
  }

  public void _jspService(request, response) {
    pageContext;
    session = null;
    application;
    config;
    out = null;
    page = this;
    _jspx_out = null;
    _jspx_page_context = null;

    try { handle jsp content }
  }

  private class Helper extends JspFragmentHelper {
    Helper(int i, pageContext){}
    invoke0(){ handler tagBody};
    invoke1(){ handler tagBody1};
  }
}

my.tag
public final class webmy_tag extends SimpleTagSupport {
  declare attributes,
  set body, attributes, context
  _jspInit(){}
  _jspDestroy(){}
  doTag(){}
}

由上面的伪代码可以看出,jsp被编译成一个jsp的类,tag文件被编译成simpletagsupport类,而jsp中tag的body部分被封装在嵌套类Helper中的invoke方法中。用伪代码来表示他们之间的关系:
JSP.jspService=
{
  TAG.setJspBody(new Helper(0, pageContext));
  TAG.setJspContext(pageContext);
  TAG.setXXX(xxx);
  TAG.doTag();
};

TAG.doTag=
{
  out.write()...
  this.getJspBody().invoke(out);
  out.write()...
}

以上为最粗略解释。接下来,我将列出jspService,invoke, doTag中所有的jsp与java代码的对照。以下面的jsp为例:

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="my.Hello"%>
<%@ taglib tagdir="/WEB-INF/tags" prefix="my"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<html>
<head><title>test</title></head>
<body>
  <p>Scriptlet context Path <%=request.getContextPath() %></p>
  <p>EL context path ${pageContext.request.contextPath}</p>
  <jsp:useBean id="hello" class="my.Hello"/>
  <jsp:setProperty name="hello" property="text" value="Hello World"/>
  <p><my:my text="${hello.text }">
       invoke successfully.
     </my:my>
  </p>
  <p><jsp:include page="a.jsp" /></p>
</body>
</html>

在jspService中,代码如下:

      response.setContentType("text/html; charset=UTF-8");
      pageContext = _jspxFactory.getPageContext(this, request, response,
      			null, true, 8192, true);
      _jspx_page_context = pageContext;
      application = pageContext.getServletContext();
      config = pageContext.getServletConfig();
      session = pageContext.getSession();
      out = pageContext.getOut();
      _jspx_out = out;

      out.write("\r\n");
      out.write("\r\n");
      out.write("\r\n");
      out.write("\r\n");
      out.write("<html>\r\n");
      out.write("<head><title>test</title></head>\r\n");
      out.write("<body>\r\n");
      out.write("  <p>Scriptlet context Path ");
      out.print(request.getContextPath() );
      out.write("</p>\r\n");
      out.write("  <p>EL context path ");
      out.write((java.lang.String) org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate("${pageContext.request.contextPath}", java.lang.String.class, (javax.servlet.jsp.PageContext)_jspx_page_context, null, false));
      out.write("</p>\r\n");
      out.write("  ");
      my.Hello hello = null;
      hello = (my.Hello) _jspx_page_context.getAttribute("hello", javax.servlet.jsp.PageContext.PAGE_SCOPE);
      if (hello == null){
        hello = new my.Hello();
        _jspx_page_context.setAttribute("hello", hello, javax.servlet.jsp.PageContext.PAGE_SCOPE);
      }
      out.write("\r\n");
      out.write("  ");
      org.apache.jasper.runtime.JspRuntimeLibrary.introspecthelper(_jspx_page_context.findAttribute("hello"), "text", "Hello World", null, null, false);
      out.write("\r\n");
      out.write("  <p>");
      if (_jspx_meth_my_005fmy_005f0(_jspx_page_context))
        return;
      out.write("\r\n");
      out.write("  </p>\r\n");
      out.write("  <p>");
      org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "a.jsp", out, false);
      out.write("</p>\r\n");
      out.write("</body>\r\n");
      out.write("</html>\r\n");

  private boolean _jspx_meth_my_005fmy_005f0(javax.servlet.jsp.PageContext _jspx_page_context)
          throws java.lang.Throwable {
    javax.servlet.jsp.PageContext pageContext = _jspx_page_context;
    javax.servlet.jsp.JspWriter out = _jspx_page_context.getOut();
    //  my:my
    org.apache.jsp.tag.webmy_tag _jspx_th_my_005fmy_005f0 = (new org.apache.jsp.tag.webmy_tag());
    _jsp_instancemanager.newInstance(_jspx_th_my_005fmy_005f0);
    _jspx_th_my_005fmy_005f0.setJspContext(_jspx_page_context);
    // /index.jsp(12,5) name = text type = java.lang.String reqTime = true required = true fragment = false deferredValue = false expectedTypeName = java.lang.String deferredMethod = false methodSignature = null
    _jspx_th_my_005fmy_005f0.setText((java.lang.String) org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate("${hello.text }", java.lang.String.class, (javax.servlet.jsp.PageContext)_jspx_page_context, null, false));
    _jspx_th_my_005fmy_005f0.setJspBody(new Helper( 0, _jspx_page_context, _jspx_th_my_005fmy_005f0, null));
    _jspx_th_my_005fmy_005f0.doTag();
    _jsp_instancemanager.destroyInstance(_jspx_th_my_005fmy_005f0);
    return false;
  }

由于调用action的时候,声明了body,body将会在Helper的invoke方法中产生java代码:

    public boolean invoke0( javax.servlet.jsp.JspWriter out ) 
      throws java.lang.Throwable
    {
      out.write("\r\n");
      out.write("       invoke successfully.\r\n");
      out.write("     ");
      return false;
    }

my.tag的代码如下:

/*
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ attribute name="text" rtexprvalue="true" required="true" %>
==Saying: "${text}".
==<jsp:doBody/>
*/

  public void doTag() throws javax.servlet.jsp.JspException, java.io.IOException {
    javax.servlet.jsp.PageContext _jspx_page_context = (javax.servlet.jsp.PageContext)jspContext;
    javax.servlet.http.HttpServletRequest request = (javax.servlet.http.HttpServletRequest) _jspx_page_context.getRequest();
    javax.servlet.http.HttpServletResponse response = (javax.servlet.http.HttpServletResponse) _jspx_page_context.getResponse();
    javax.servlet.http.HttpSession session = _jspx_page_context.getSession();
    javax.servlet.ServletContext application = _jspx_page_context.getServletContext();
    javax.servlet.ServletConfig config = _jspx_page_context.getServletConfig();
    javax.servlet.jsp.JspWriter out = jspContext.getOut();
    _jspInit(config);
    jspContext.getELContext().putContext(javax.servlet.jsp.JspContext.class,jspContext);
    if( getText() != null ) 
      _jspx_page_context.setAttribute("text", getText());

    try {
      out.write("\r\n");
      out.write("\r\n");
      out.write("==Saying: \"");
      out.write((java.lang.String) org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate("${text}", java.lang.String.class, (javax.servlet.jsp.PageContext)this.getJspContext(), null, false));
      out.write("\".\r\n");
      out.write("==");
      ((org.apache.jasper.runtime.JspContextWrapper) this.jspContext).syncBeforeInvoke();
      _jspx_sout = null;
      if (getJspBody() != null)
        getJspBody().invoke(_jspx_sout);
      jspContext.getELContext().putContext(javax.servlet.jsp.JspContext.class,getJspContext());
      out.write('\r');
      out.write('\n');
    } catch( java.lang.Throwable t ) {
      if( t instanceof javax.servlet.jsp.SkipPageException )
          throw (javax.servlet.jsp.SkipPageException) t;
      if( t instanceof java.io.IOException )
          throw (java.io.IOException) t;
      if( t instanceof java.lang.IllegalStateException )
          throw (java.lang.IllegalStateException) t;
      if( t instanceof javax.servlet.jsp.JspException )
          throw (javax.servlet.jsp.JspException) t;
      throw new javax.servlet.jsp.JspException(t);
    } finally {
      jspContext.getELContext().putContext(javax.servlet.jsp.JspContext.class,super.getJspContext());
      ((org.apache.jasper.runtime.JspContextWrapper) jspContext).syncEndTagFile();
    }
  }




转载于:https://my.oschina.net/xpbug/blog/152922

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值