struts2标签底层实现过程(Struts2)

  1. 说明:阅读此文章需要有:strut2, ognl, freemarker, 自定义标签的基础。采用自然语言来描述struts2标签的底层实现过程。
  2. 视图层页面:
页面: movies-edit.jsp >>>>>>
<%@ page contentType="text/html; charset=UTF-8" language="java" errorPage="" %> 
<%@taglib prefix="s" uri="/struts-tags" %> 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
   
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
<!-- 简单样式表 -->
	<style type="text/css">
			div{
			 	background-color: #FFEBCD;
				margin-left: 5px;
				margin-bottom: 20px;
			}
			
			div table {
				margin-left: 20px
			}
			
			div a {
				margin-left: 20px
			}
			
	</style>
	
   <title> 编辑 ID 为 <s:property value="id"/> 的影片 </title> 
  
</head> 
<body> 
	<div>
	<s:form method="post" action="%{#request.contextPath}/movies/%{id}"> 
	
		<!-- 添加 _method 请求参数,参数值为 put 用于模拟 PUT 操作 -->
		<s:hidden name="_method" value="put" /> 
		<table> 
		   <!-- s:textfield标签是如何从值栈中获取到数据的?
		   		  此过程在struts2标签解析过程中进行:
		   		  struts.tag.altSyntax=true(是否开启表达式语法),数据类型转换器:XWorkConverter
		   		  Object value = ognlUtil.getValue(expr, context, root, asType);
		   		  expr="name",CompoundRootAccessor和Ognl从CompoundRoot栈中匹配到元素Movie的成员属性name取到值getName();
		   		(2).如何把取到的数据,输出到浏览器?参见struts2标签底层实现_概要
		    -->
		   <s:textfield name="id" label="影片 ID" disabled="true"/> 
		   <s:textfield name="name" label="片名"/> 
		   <s:textfield name="price" label="价格" /> 
		   <tr>
		       <td colspan="2"> 
		           <s:submit value="修改"/> 
		       </td> 
		   </tr>
		</table> 
	</s:form> 
	
	<a href="<%=request.getContextPath() %>/movies"> 返回首页 </a>
	
	</div>
</body> 
</html>

3.标签底层实现过程简述

	>>>>>>>>>>> Struts2 标签(属于自定义标签范畴)底层实现原理或过程简述
		
		前言:首先来说下自定义标签的两种方式(打开javaEE1.6文档或Tomcat服务器安装目录下的doc文档找到包:javax.servlet.jsp.tagext)
			接口层级,标签顶层接口为JspTag,直接子接口有两个:SimpleTag(JSP2.0后出现的)和Tag(原来老式的)
			1.javax.servlet.jsp.tagext.JspTag
				1.1 javax.servlet.jsp.tagext.SimpleTag
				1.2 javax.servlet.jsp.tagext.Tag
					 1.2.1 javax.servlet.jsp.tagext.IterationTag
					 1.2.2 javax.servlet.jsp.tagext.BodyTag
			
			1.实现SimpleTag接口
				这种方式感觉简便一些
				
			2.实现Tag接口
				这种方式感觉繁琐一些
				
		说明:(1) struts-tags.tld这种标签文件定义自己去看,这里就不简述!
			这里主要是讲struts2标签底层原理或流程是如何进行。
			
			  (2) 下面的例子属于第两种方式(实现Tag接口,这个例子中Struts2作了适当扩展)
		
		环境:tomcat + eclipse + struts2(Ognl,Freemarker等)	,视图层文件:movies_edit.jsp
			此外还有一个字符流分析文件:struts2标签底层实现过程_jspWriterImple字符流中的数据.txt
			
		
		一、Tomcat中的jasper引擎将movies_edit.jsp文件解析成:movies_002dedit_jsp.java
			准备工作>>>>>>
			1.值栈中存储Movie数据(name="拈花为何不一笑"),读者自己完成(相当于一个helloword级别的action)。
			public class Movie{
				private String name; 
				public void setName(String name){this.name=name;}
				public String getName(){return this.name;}
			}
			
			自定义标签实现流程讲解>>>>>
			2.以movies_edit.jsp中如下标签进行演示,后面称此标签为"演示标签"
				<s:textfield name="name" label="片名"/>
		
		二、Tomcate会调用movies_002dedit_jsp.java(当然会生成字节码文件,在这里我们不需要关注)进行以下业务处理:
		
		1.TextFieldTag textFieldTag = TagHandlerPool.get(org.apache.struts2.views.jsp.ui.TextFieldTag.class);
		
		2.ComponentTagSupport.java >>>>>>
			//此方法进行一些准备工作
			textFieldTag.doStartTag();
			
		3.TagSupport.java>>>>>>
			//设置页面上下文和演示标签的父标签(Form)
			TextFieldTag.setPageContext(_jspx_page_context);
			TextFieldTag.setParent((javax.servlet.jsp.tagext.Tag) _jspx_th_s_005fform_005f0);
		
		4.AbstractUITag.java>>>>>>
			//给演示标签设置属性attribute(name,lable,disabled)值
			TextFieldTag.setName("id");
			TextFieldTag.setLabel("影片 ID");
			TextFieldTag.setDisabled("true");
		
		5.ComponentTagSupport.java >>>>>>
			//主要业务处理(通过Ognl从值栈中取数据,参数填充,将参数parameters中的数据插值到freemarker模板中,JspWriterImpl字符流将处理过的freemarker模板数据输出到浏览器)
			TextFieldTag.doEndTag();
			
			5.1 UIBean.java>>>>>>
				填充参数:evaluateParams();
					addParameter("name", findString(name));
					addParameter("label", findString(label));
					addParameter("required", findValue(required, Boolean.class));
					addParameter("onclick", findString(onclick));
					addParameter("cssClass", findString(cssClass));
					addParameter("cssStyle", findString(cssStyle));
					addParameter("nameValue", findValue(expr, valueClazz));	//重点
					......
					 
							
			5.2 调用Freemarker:将参数中的数据插值到freemarker模板中然后输出 >>>>>>
				Freemarker准备工作 >>>>>>>>>>>>
				找到Freemarker模板: /template/xhtml/text.ftl
				(5.2.1) TemplateEngineManager:
						//Struts2 IOC容器创建org.apache.struts2.components.template.FreemarkerTemplateEngine
						TemplateEngine create();
						
				(5.2.2) TemplateRenderingContext:
						TemplateRenderingContext构造造器初始化
						{
							this.template = template;
							this.writer = writer;
							this.stack = stack;
							this.parameters = params; //此参数params就是evaluateParams()方法填充的,查看5.1 具体填充了哪些数据至Component中的Map parameters
							this.tag = tag;			  //UIBean tag = TextField;  class TextField extends UIBean 而 class UIBean extends Component(拥有protected Map parameters;)
						}
						//初始化时几个重要参数说明,如下:
						(1)writer:
							jspWriterImpl
						(2)parameters:
						{templateDir=template, theme=xhtml, dynamicAttributes={}, name=name, label=片名, nameValue=拈花为何不一笑, 
							id=1_name, escapedId=1_name,
							form={templateDir=template, theme=xhtml, dynamicAttributes={}, action=/struts2_restful/movies/1, 
									namespace=/struts2_restful/movies, id=1, method=post, tagNames=[_method, id, name]}
						}
					   (3)tag:
							TextField
							
				Freemarker核心工作 >>>>>>>>>>>>
				(5.2.3)FreemarkerTemplateEngine.renderTemplate(templateRenderingContext);
						(i).创建config: freemarker.template.Configuration config = freemarkerManager.getConfiguration(servletContext);
						(ii)获取模板: freemarker.template.Template  template = config.getTemplate("/template/xhtml/text.ftl");
						(iii)建立模板模型:freemarkerManager.buildTemplateModel(stack, action, servletContext, request, response, wrapper) 
							populateContext(model, stack, action, request, response);
							Map<String,TagLibrary> tagLibraries: {s=org.apache.struts2.views.DefaultTagLibrary@1a325f0}
							
						(iv)将Map数据插值到模板中并通过Writer字符流输出:template.process(model, writer);
							参数说明:
							model(ScopesHashModel)此对象包含以下属性:
								A): map= {Parameters=freemarker.ext.servlet.HttpRequestParametersHashModel@abb9ce,
									tag=org.apache.struts2.components.TextField@1730d54, //从此对象中获取parameters
									 Request=freemarker.ext.servlet.HttpRequestHashModel@674bf6, JspTaglibs=null,
									 struts=org.apache.struts2.util.StrutsUtil@17f3921, 
									response=org.apache.catalina.connector.ResponseFacade@969e08,
									s=org.apache.struts2.views.freemarker.tags.StrutsModels@195b5ad, 
									themeProperties={parent=simple}, 
									ognl=org.apache.struts2.views.jsp.ui.OgnlTool@182e8c2,
									session=null,
									Application=null, 
									request=org.apache.struts2.dispatcher.StrutsRequestWrapper@8f46a6, action=com.bs.action.MoviesController@4e3801,
									base=/struts2_restful, 
									stack=com.opensymphony.xwork2.ognl.OgnlValueStack@dd9e27}
									
								B):objectWrapper
									org.apache.struts2.views.freemarker.StrutsBeanWrapper@69f31d
								C):ServletContext
								D):stack=OgnlValueStack 
								E):request
							
								F):此过程中用到的Freemarker模板:	
									(f1) /template/xhtml/txt.ftl
										<#include "/${parameters.templateDir}/${parameters.theme}/controlheader.ftl" />
										<#include "/${parameters.templateDir}/simple/text.ftl" />
										<#include "/${parameters.templateDir}/xhtml/controlfooter.ftl" />
									(f2) controlheader.ftl
										<#include "/${parameters.templateDir}/xhtml/controlheader-core.ftl" />
											<td
										<#if parameters.align??>
											align="${parameters.align?html}"<#t/>
										</#if>
									(f3)/template/simple/text.ftl(这个是主要的模板,用于构建
											<s:textfield name="name" label="片名"/> 对应的html标签,最后输出到浏览器)
											
										构建成这样:================此为演示标签被解析后构建的样貌============================
										   <input type="text" name= name="${parameters.name?default("")?html}"<#rt/>
											<#if parameters.nameValue??>
												<!--ftl使用struts2标签<s:property value="xxx" /> -->
												 value="<@s.property value="parameters.nameValue"/>"<#rt/>
											</#if>
											<#if parameters.readonly?default(false)>
												readonly="readonly"<#rt/>
											</#if>
											....
											....
											/> <!--input 标签结束-->
										其中还有javascript event等...
										
									===>重点:这个参数parameters(Map数组结构)是从哪来的?它里面放的是什么数据?
										来源于:tag=org.apache.struts2.components.TextField@1730d54
										分析:UIBean tag = textField; 
											 类: class TextField extends UIBean{...} 而 类class UIBean extends Component{protected Map parameters; ...}
										//从这些继承关系可以得出TextField拥有parameters属性。
										//===>而此属性parameters数据是哪里来的?是UIBean(TextField)的 evaluateParams()方法填充的
										//查看5.1 具体填充了哪些数据
										//===>Freemarker插值过程简述,例如:${parameters.name}会进行以下取值过程
											1).首先会在TextField(继承父辈Component的方法)中调用下面方法拿到parameters
											    public Map getParameters() {
													return parameters;
												}
											2).然后调用:parameters.get("name")取值
										//tips: 这个取值过程跟Ognl很相似...
						

猜你喜欢

转载自blog.csdn.net/Netaa001/article/details/82830147