<jsp:setProperty>是用来设置bean的属性,设置的方法有两种:
1. 直接赋值
2. 以request中的参数进行赋值。
这样就对应了两个setProperty属性:
1. value
2. param
1. value
value很容易理解,可以直接提供一个字面值/常量,也可以用Request-time Attribute的形式进行赋值。- <jsp:setProperty name="helloBean" property="vx" value="1" />
复制代码 这句JSP语句会转译成:- 68 org.apache.jasper.runtime.JspRuntimeLibrary.introspecthelper(_jspx_page_context.findAttribute("helloBean"), "v ", "1", null, null, false);
复制代码 这个转译代码要做的是下面几个步骤:
1. 使用pageContext的findAttribute方法,找到指定id的bean
2. 利用JspRuntimeLibrary.introspecthelper()进行赋值
对于第一点,显然会引来两个推论:
1. 如果我在当前网页没在指定<jsp:useBean>,照样可以用<jsp:setProperty>进行处理
2. 如果一个不小心,我们通过各种方式,比如scriptlet,设置了某个级别的scope内的一个attribute,
如果我们尝试去用<jsp:setProperty>来设置一个更高级别scope的一个JavaBean,将被低级别的
JavaBean所屏蔽。原因是findAttribute()会从page开始查找,先找低级别的scope,再找高级别的scope。
当然,如果你还要考虑性能之类,想到的问题可能会更多。
这是Tomcat 6.0的实现,我不知道其他版本或JSP container的转译结果。
下面转译结果会让你多少有点吃惊:- <%
- String value="1";
- String hello="hello";
- String prop = "prop";
- %>
- <jsp:setProperty name="<%=hello%>" property="<%=prop%>" value="<%=value%>" />
复制代码 转译出来的结果是:- 54 String value="1";
- 55 String hello="hello";
- 56 String prop = "prop";
- 57
- 58 out.write('\n');
- 59 org.apache.jasper.runtime.JspRuntimeLibrary.handleSetProperty(_jspx_page_context.findAttribute("<%=hello%>"),
- "<%=prop%>",
- 60 value);
复制代码 显然,除了value能顺利转译外,其他两个属性都没有转择,而是采用字面值。
我估计原因是JSP Specificiation中指明name和property两个值是不能用Request-time Attribute来设定的。
另外,如果试用EL的${name}方式来设置value也是不成功的。
org.apache.jasper.runtime.JspRuntimeLibrary.handleSetPropertyExpression(_jspx_page_context.findAttribute("hello"),
"prop", "${value}", _jspx_page_context, null);
情况真的是这样吗?
我们来看一下JspRuntimeLibrary.handleSetPropertyExpression()的实现:
被注释掉的一段实现- 617 // handles <jsp:setProperty> with EL expression for 'value' attribute
- 618 /** Use proprietaryEvaluate
- 619 public static void handleSetPropertyExpression(Object bean,
- 620 String prop, String expression, PageContext pageContext,
- 621 VariableResolver variableResolver, FunctionMapper functionMapper )
- 622 throws JasperException
- 623 {
- 624 try {
- 625 Method method = getWriteMethod(bean.getClass(), prop);
- 626 method.invoke(bean, new Object[] {
- 627 pageContext.getExpressionEvaluator().evaluate(
- 628 expression,
- 629 method.getParameterTypes()[0],
- 630 variableResolver,
- 631 functionMapper,
- 632 null )
- 633 });
- 634 } catch (Exception ex) {
- 635 throw new JasperException(ex);
- 636 }
- 637 }
- 638 **/
复制代码 当前使用的实现版本:- 639 public static void handleSetPropertyExpression(Object bean,
- 640 String prop, String expression, PageContext pageContext,
- 641 ProtectedFunctionMapper functionMapper )
- 642 throws JasperException
- 643 {
- 644 try {
- 645 Method method = getWriteMethod(bean.getClass(), prop);
- 646 method.invoke(bean, new Object[] {
- 647 PageContextImpl.proprietaryEvaluate(
- 648 expression,
- 649 method.getParameterTypes()[0],
- 650 pageContext,
- 651 functionMapper,
- 652 false )
- 653 });
- 654 } catch (Exception ex) {
- 655 throw new JasperException(ex);
- 656 }
- 657 }
复制代码 其实line-647 ~ 651就是做EL翻译的。但prop则没有做任何转换。
从这个地方可以看出一个bug:
如果你使用的值本身就是一个字面值,比如${value},那就很可能中招了。
这个问题主要是因为Tomcat的开发者没有在转译器阶段来实现EL转换,
而是把这个转换延迟到运行时,这是很不智的做法,估计下一个版本就会
改进吧。 |