- 说明:展示jsp中el表达式取值底层过程。说是底层原理有点过,不过底层原理就蕴含其中。
- 自行甄别。
- 主代码如下:
- 原理分析,showFindUser.jsp中展示el取值的底层实现过程或原理。
=================================3.主代码===============================
一、视图层:
(1).findUser.jsp >>>>>>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
String basePath = request.getScheme()+"://"
+ request.getServerName() +":"
+request.getServerPort() +"/"
+ request.getContextPath()+"/";
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<base href="<%=basePath%>">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>findUser.jsp</title>
</head>
<body>
<form action="findUser.action" method="get">
<input type="text" name="user.id" /><br/>
<input type="submit" value="查看用户" /><br/>
</form>
</body>
</html>
(2).showFindUser.jsp >>>>>>>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"
import="java.util.*"
import="com.opensymphony.xwork2.ognl.OgnlValueStack"
import="com.opensymphony.xwork2.util.CompoundRoot"
import="org.yl.action.UserAction"
import="org.yl.domain.User"
%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<%
String basePath = request.getScheme()+"://"
+ request.getServerName() +":"
+request.getServerPort() +"/"
+ request.getContextPath()+"/";
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<base href="<%=basePath%>">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>视图层:JSP,从ValueStack中的CompoundRoot中取值</h1>
<div>
<span>用户信息:</span><br/>
<%
OgnlValueStack vs = (OgnlValueStack)request.getAttribute("struts.valueStack");
//值栈的上下文
Map<String,Object> vsContext = vs.getContext();
for(Map.Entry<String, Object> entry: vsContext.entrySet()){
//System.out.println("key:" + entry.getKey() + "--value:" + entry.getValue() + "\n");
}
//值栈的根(CompoundRoot数据结构为栈,使用ArrayList实现的)
CompoundRoot root = vs.getRoot();
Object obj = root.peek();//从root栈中取出一个数据
UserAction userAction = null;
User user = null;
String name = null;
if(obj instanceof UserAction){
userAction = (UserAction)obj;
user = userAction.getUser();
System.out.println("Action中的成员变量信息:" +"\n"
+ "1.user信息 ======"
+ "id:" + user.getId() + "\n"
+ "name:" + user.getName() + "\n"
+ "sex:" + user.getSex() + "\n"
+ "deposit:" + user.getDeposit() + "\n"
+ "telephone:" + user.getTelephone() + "\n"
+"");
name = user.getName();//供页面使用,与html标签进行整合
}
%>
java直接取值:<input type="text" value="<%=name %>" readonly="readonly" /><br/>
el表达式取值:<input type="text" value="${user.name }" readonly="readonly" /><br/>
struts2标签取值:<input type="text" value="<s:property value="user.telephone"/>" readonly="readonly" /><br/>
</div>
</body>
</html>
二、控制层
/**
* 演示:显示层从ValueStack中取值
* (1)el+jstl
* (2)struts2标签库+Ognl
* @author 拈花为何不一笑
*
*/
public class UserAction {//何时何地创建Action?服务Action时创建ActionProxy,
//而在创建代理时ObjectFactory创建Action并且把Action放入至值栈中(实际是放入CompoundRoot中)
//何地?DefaultActionInvocation作为场地
//初始化
private User user = new User();
//业务方法(struts.xml的UserAction配置中不指定具体方法时,excute或doExecute作为默认方法)
public String doExecute(){//执行action阶段调用
System.out.println("findUser...");
/**
* 下面逻辑发生在拦截器阶段
* 1.xwork2的InstantiatingNullHandler类调用对象工厂objectFactory.buildBean(clazz, context);
* 来创建了UserAction中为空的成员属性User:user。
* 2.当UserAction中的成员属性是类对象时且开发人员没有初始化时,
* 由Ognl+CompoundRootAccessor+InstantiatingNullHandler经过一系列逻辑创建了该成员属性
* 创建User实例后,再通过Ognl把前端传递过来的参数值:user.id="m-p6"赋给User对象中的成员变量id。
* 3.由于步骤2开销不少,建议开发中先初始化Action中的成员变量(类对象作为Action的成员变量),避免程序执行步骤2
*/
if(this.user != null){//UserAction中的User实例也被框架创建了。(谁创建的?在哪创建的?)
System.out.println("user.id:" + this.user.getId());
//模拟从DB或企业信息系统中获取到的数据
if(user.getId().equals("1688")){
this.user.setName("张天见");
this.user.setSex("男");
this.user.setTelephone("13638885606");
this.user.setDeposit(73000);
}
}
//struts2中提供与Web容器直接交互的类:ServletActionContext
//获取服务器路径
/*HttpServletRequest request =ServletActionContext.getRequest();
String serverPath = request.getSession().getServletContext().getRealPath("/");
System.out.println("serverPath: " + serverPath);*/
return "showFindUser";
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
三、模型层
public class User {
private String id; //身份证
private String name;
private String address;
private String telephone;
private String sex;
private long deposit; //存款
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getTelephone() {
return telephone;
}
public void setTelephone(String telephone) {
this.telephone = telephone;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public long getDeposit() {
return deposit;
}
public void setDeposit(long deposit) {
this.deposit = deposit;
}
}
配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.devMode" value="true" />
<package name="default" namespace="/" extends="struts-default">
<action name="reg" class="org.yl.action.Register">
<result name="success">/success.jsp</result>
</action>
<action name="findUser" class="org.yl.action.UserAction">
<result name="showFindUser">/showFindUser.jsp</result>
</action>
</package>
</struts>
===========================4.jsp中el取值底层实现或原理=========================
jsp视图层中几种取值方式的底层原理(Struts2)
环境:tomcat,win7
1.tomcat中的jasper作为引擎解析jsp文件
(1)jsp文件被解析成java文件,这个java文件解析jsp分为两部分:jsp中的html和jsp中的java代码
(2)html代码通过JspWtriter直接输出到浏览器,java代码被编译执行作为结果嵌入html中。
(3)输出到浏览器中的html文件,被浏览器解析,呈现出UI界面。
tips:说标签库或许你感受不深,那换一种说法你肯定会熟悉:视图层组件。(放个点在此)
2.一探,jasper解析jsp后生成的java文件,若jsp中有el表达式取值。则反应到这个java文件中是如何进行的?
此showFindUser_jsp.java(解析showFindUser.jsp得到的java文件)文件片段:
//通过jsp内置对象获取输出字符流JspWriter
JspWrite out = pageContext.getOut();
out.write("\t\t\r\n");
out.write("\t\t\r\n");
out.write("\t\tel表达式取值:<input type=\"text\" value=\"");
out.write((java.lang.String) org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate("${user.name }", java.lang.String.class, (PageContext)_jspx_page_context, null, false));
out.write("\" readonly=\"readonly\" /><br/>\r\n");
out.write("\t\t\r\n");
3.el取值流程,PageContextImpl.proprietaryEvaluate("${user.name }", java.lang.String.class, (PageContext)_jspx_page_context, null, false));
如下:
类:PageContextImpl中的进行以下逻辑:
调用proprietaryEvaluate(final String expression,
final Class expectedType,
final PageContext pageContext,
final ProtectedFunctionMapper functionMap,
final boolean escape))方法
类:ExpressionEvaluatorImpl中进行了以下逻辑:
解析表达式:Object parsedValue = parseExpressionString (pExpressionString);
表达式取值: Object value = ((Expression) parsedValue).evaluate (pResolver, functions, pLogger);
(类:ComplexValue extends Expression,表示一个动态值,它由一个前缀和一组可选的ValueSuffix元素组成。
前缀类似于标识符,后缀类似于“属性”或“索引元素”运算符。)
类:VariableResolverImpl 基于jstl实现的变量解析器
1.这里有大家熟悉的取值顺序,从隐式对象中取值:pageContext/pageScope/requestScope/sessionScope/applicationScope/
param/paramValues/header/headerValues/initParam/cookie
2.若不在其中,就在页面上下文中查找即可PageContext.findAttribute (pName);
类:又回到PageContextImpl中的进行以下逻辑:
1.在doFindAttribute(name)方法中查看值
2.它是先从request中取值request.getAttribute(name);而这个request是被装饰了的StrutsRequestWrapper
从这里开始,el表达式取值就与struts2联系上了。
类: StrutsRequestWrapper extends HttpServletRequestWrapper进行了以下逻辑:
//从request(ServletRequestWrapper)中取值(User)
Object attribute = super.getAttribute(s);
//若request中没值,则从值栈OgnlValueStack中取值(结合Ongl)
//有了值栈和Ognl,从struts2中取数据简单是易如反掌,屡试不爽。(不细说,前面文章有写)
ValueStack stack = ctx.getValueStack();
if (stack != null) {
attribute = stack.findValue(s);
}
类:JspContextWrapper进行了以下逻辑:
1.evaluate方法中,通过上面获取到的User实例对象,
与BeanInfoManager获取User的bean信息(如getName()),结合反射取出user.name的值
2.取出的值放置到showFindUser_jsp.java(japser解析jsp后生成的文件)文件中
**tips:里面包含了不少东西,比如struts2中的实现具体细节描述。**