1. JSP规范下的标准标签(Standard Tag):

    1) JSP标准标签是JSP早期规范中提出来的,目的是为了协助JSP编写是减少Scriptlet的使用;

    2) 在J2EE规范下所有Web容器都必须支持标准标签;

    3) 标准标签都以jsp:打头,比如<jsp:include>等;

    4) 虽然后来提出的JSTL和EL表达式等一些更加便捷的功能来取代标准标签,但必须还要对这些标准标签有所认识;


2. 请求调配标签:

    1) 即实现了include和forward请求调配的标签,那么以后进行请求调配是就无需在Scriptlet中使用Dispatcher等进行调配,而是简单的使用两个JSP标签即可实现;

    2) 两个标签分别是<jsp:include>和<jsp:forward>,其属性是page,指定调配到的目的页面(URL),例如:<jsp:include page="index.view">;

    3) 但是我们知道在调配的时候可以添加额外的请求参数,这里在使用这两个标签的时候不能直接在page属性中加入额外的请求参数,但是可以使用<jsp:param>标签指定额外的请求参数,其属性有两个,一个是name,另一个是value,即参数的名称和值,例如:<jsp:param name="name" value="Peter" />,该标签可以简写,无需再加结束标签,直接在>左侧加一个/即可,但是<jsp:include>和<jsp:forward>必须要有末尾标签,因为<jsp:param>使其子标签,没有这个子标签这两个标签不可能结束;

    4) 完整举例:

<jsp:include page="index.view">
	<jsp:param name="a" value="1" />
	<jsp:param name="b" value="2" />
</jsp:include>
!<jsp:forward>完全相同;

    4) 这两个标签在转译后都是调用RequestDispatcher对象再进行调配的;

    5) 转译后必然也是放在_service方法中的;


3. 对JavaBean原件的支持:

    1) 先不介绍JavaBean原件,光是谈谈对JSP的特别需求,其中一点最重要的就是范围属性了,我们都知道在Serlvet中可以不同的范围对象(request、session、servletContext等)设置对象属性,那么我们如何用JSP的标签简化Java中对象属性的操作呢?

    2) JSP规范当然有标签支持对象属性操作的简化,只不过对这些对象属性有一定要求,不能随随便便就那一个对象作为属性通过JSP标签添加到各个范围对象中,这个要求就是这些对象属性必须是JavaBean元件;

    3) JavaBean元件就是满足一下条件的纯粹Java类对象:

         i. 数据成员全部都不公开(可以是private、protected或是包范围,但不能有一个public的);

         ii. 具有无参构造器;

         iii. 有public的getter和setter;

         iv. 实现串行化接口:java.io.Serializable

    4) JSP的<jsp:useBean>、<jsp:setProperty>和<jsp:getProperty>三个标准标签可以简化对满足JavaBean标准的属性对象进行操作

    5) <jsp:useBean>:在JSP页面中寻找JavaBean原件(对象实例,相当于对象定义语句),如果找不到就创建一个

         i. 该标签有两个属性,一个是id,另一个是class;

         ii. 意义是:使用class所代表的JavaBean类创建一个属性名和对象名都为id的对象属性;

         iii. 例如:<jsp:useBean id="user" class="com.lirx.User" />的意思就是com.lix.User是一个符合JavaBean标准的类,然后用该类创建一个名为user的对象,并且当该对象作为范围属性时的名称也为user,用Java代码表示就是如下:

com.lirx.User user;
XXX.setAttribute("user", user);  // 这句并不在该标签的转译语句中
!!即该对象的范围属性名(setAttribute的第一个参数的字符串值)和其对象名称本身(setAttribute的第二个参数的名称)都是相同的;

         iv. 使用该标签后就创建了一个user对象了,但并没有将该对象作为范围属性插入到某个范围中,上例代码的第二行仅仅是一种演示,即使为了演示user对象的范围属性名称也为"user"罢了;

         v. 对象创建完之后就可以自由使用了,比如在Scriptlet中直接调用user.getName等方法,但是不推荐这样使用,因为标准标签就是为了避免使用Scriptlet的,因此接下来对范围属性的操作应该使用标准标签才行;

    6) 使用<jsp:setProperty>标签设置对象属性:

         i. 当第一步使用<jsp:useBean>标签创建JavaBean元件后,就可以使用<jsp:setProperty>将该元件作为范围属性添加到某个范围中了;

         ii. 该标签的常用属性:

             a. name:属性名,即对应userBean中定义的name属性名;

             b. property:JavaBean元件中的某个数据成员;

             c. value:将property中指定的数据成员设成value指定的值;

             d. scope:将该属性添加到哪个范围中,即page、request、session、application这4个中选,如果该属性不写,则默认为page范围;

         iii. 例如:<jsp:setProperty name="user" property="name" value="Peter" scope="application" />就会被转译为如下Java代码

user.setName("Peter");
application.setAttribute("user", user);
!!这里涉及到了JSP的自省机制,即在setProperty时,如果指定的property的名称为xxx,那么将使用元件的setXxx来设置值,因此JavaBean必须要有public的setter和getter,同样,在使用<jsp:getProperty>时会使用getXXX方法来获取指定的property;         

         iv. 也可以利用请求参数来设置JavaBean元件中的数据成员的值:即不在标签中直接设定value,而是将请求参数的值作为value来设置给元件的property,此时可以使用属性param,例如:<jsp:setProperty name="user" param="member_name" property="name" />就是将member_name请求参数的值设置给user.name,其转译后的代码是这样的:

String name = request.getParameter("member_name");
user.setName(name);
application.setAttribute("user", user);
!!如果请求参数名刚好和propety名称相同,那么这里就可以进一步利用JSP的自省机制了:如果不写param和value属性,那么就会到请求参数中去找是否有何property指定的名称相同的请求参数,如果有,就把那个请求参数的值设置给property,否则就不调用setter,即什么也没发生,保持原来的值不变,例如:

<jsp:setProperty name="user" property="name" />就会被转译成下面的Java语句:

String name = request.getParameter("name");
if (name != null) {
    user.setNmae(name);
    application.setAttribute("user", user);
}
         v. 更进一步利用JSP的自省机制:所有的property都用请求参数来设置

!!!只要将property写成"*"即可,通配符*就代表所有的属性,此时就会到元件中找所有的setter,即找出所有的setXxx,然后得到所有的set属性名xxx,然后找请求参数中是否有叫做xxx的请求参数,如果有就拿其值来设置对应的元件数据成员,例如:user中有setName、setPasswd、setHeader三个setter,而使用的标签是<jsp:setProperty name="user" property="*" />,那么转译后的Java代码描述的步骤是这样的:

            a. 先到user中找其所有的setter,发现有setName、setPasswd、setHeader三个;

            b. 然和取得setter属性名:name、passwd和header;

            c. 然后到请求参数中找是否有名称为name、passwd和header的参数;

            d. 如果有就调用相应的setter将请求参数的值设置给元件;

            e. 最后调用setAttribute方法将user作为属性设置给指定范围对象;

!这也要求请求参数的名称与setter名相同;


!!!但是要注意的是setter名可以不等于元件的数据成员的名称,比如user的一个数据成员是String member_name;,但是其setter可以是setName(String s) { member_name  = s; },但是setName的setter名是name而不是member_name;

!!!而我们的这里利用的JSP自省机制就要求请求参数名要和setter名相同,而不是元件的数据成员的名称;


    7) 使用<jsp:getProperty>标签获得指定的对象属性:

         i. 其属性有3个,分别是name、property和scope(意义和set的相同),如果scope不写,则默认为page范围的;

         ii. 目的是在指定范围内获取对象属性的某个数据域,作用刚好和set相反;

         iii. 例如:<jsp:getProperty name="user" property="name" scope="session" />就会被转译为

User user = session.getAttribute("user");
out.write(user.getName());
!!实际返回的是数据域的字符串形式,因此该数据域必须实现toString方法,才能插入到字符串序列中;

         iv. 整个标签的返回值就是一个查询结果的字符串表示,和JSP中的其它输入文本混合在一起作为画面呈现;


4. 深入理解<jsp:useBean、setProperty、getProperty>的转译源码,认识其本质:

    1) <jsp:useBean>的转译源码:以<jsp:useBean id="user" class="com.lirx.User" />为例

com.lirx.User user = null; // 由于标签中未指定scope,因此默认为page scope
synchronized(request) { // 由于一个请求只能被一个线程访问,所以用同步的方式去操作request,而不是异步asynchronized
	// useBean的意思是使用Bean,因此会先查找在指定范围内是否存在名为"user"的Java Bean
	user = (com.lirx.User)_jspx_page_context.getAttribute("user", PageContext.PAGE_SCOPE);
	if (user == null) { // 如果不存在,那既然要使用,就得创建
		user = new com.lirx.User(); // 创建完了还得将其作为属性插入到指定范围中
		_jspx_page_context.setAttribute("user", user, PageContext.PAGE_SCOPE);
	}
}

!!_jspx_page_context就是PageContext对象的引用,PageContext中保存着当前JSP页面的所有信息,其它隐式对象都需要用它来初始化,这在之前讲过;

!!其本质就是先查找,如果有就直接使用,如果没有则创建后再插入,总共有两步;

    2) <jsp:setProperty>的转译源码:以<jsp:setProperty name="user"  property="name" value="Peter" />为例

((com.lirx.User)_jspx_page_context.findAttribute("user")).setName("Peter");
!这个很简单,很好理解,是先利用findAttribute在所有范围内寻找"user"属性,找到后在通过“反射”机制调用响应property的setter进行设值;

    3) useBean和setProperty组合使用:

         i. 组合使用有两种不同的方式,但是效果却大不相同;

         ii. 第一种是useBean包裹setProperty,例如:

<pre name="code" class="java"><jsp:useBean id="user" class="com.lirx.User" scope="session">
    <jsp:setProperty name="user" property="*" />
</jsp:useBean>
 !这里也用到了自省机制,直接利用请求参数设置property,先来看一下转译后的源码: 

com.lirx.User user = null;
synchronizd (request) {
	user = (com.lirx.usr)_jspx_page_context.getAttribute("user", PageContext.SESSION_SCOPE);
	if (user == null) {
		user = new com.lirx.User;
		_jspx_page_context.setAttribute("user", user, PageContext.SESSION_SCOPE);
		JspRuntimeLibrary.introspect(_jspx_page_context.findAttribute("user"), request);
	}
}
!JspRuntimeLibrary的introspect方法就是自省的意思,第一个参数是JavaBean,第二个参数就是request,意思就是将request中的请求参数通过setter设置给JavaBean;

!!从这里可以看到这种包裹方式时必须在Bean不存在的情况下才会发生setProperty,setProperty和创建一同发生;

         iii. 第二种是useBean和setProperty平行使用(但无论如何useBean都必须出现在setProperty之前),例如:

<jsp:useBean id="user" class="com.lirx.User" scope="session" />
<jsp:setProperty name="user" property="*" />
!其转译后的源码为:

com.lirx.User user = null;
synchronizd (request) {
	user = (com.lirx.usr)_jspx_page_context.getAttribute("user", PageContext.SESSION_SCOPE);
	if (user == null) {
		user = new com.lirx.User;
		_jspx_page_context.setAttribute("user", user, PageContext.SESSION_SCOPE);
	}
}
JspRuntimeLibrary.introspect(_jspx_page_context.findAttribute("user"), request);
!!即无论如何(不管存不存在,不管是否要新创建),都会进行自省(设值);

!!通常后者(平行的写法)较常用!!注意这两者的区别,千万不能在这个问题上犯错!

    4) <jsp:getProperty>的转译源码:很简单,和set一样,只不过用的方法是getter罢了,由于get的结果是直接输出,因此会用out进行输出,以<jsp:getProperty name="user" property="name" />为例

JspRuntimeLibrary.toString(
	((com.lirx.User)_jspx_page_context.findAttribute("user")).getName()
);


Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐