struts2中的标签源码解析

struts2中的标签源码解析
2012-02-05 23:15

struts2标签库的源代码主要在三个包内,org.apache.struts2.components,org.apache.struts2.views.jsp.ui
和org.apache.struts2.views.jsp。
org.apache.struts2.views.jsp.ui包主要包括struts2的所有的标签类(如TextFieldTag)及其公共的抽象基类
AbstractUITag。
org.apache.struts2.views.jsp包与struts标签相关的类主要是ComponentTagSupport和StrutsBodyTagSupport。
org.apache.struts2.components包主要包括所有标签类所对应的组件类(如TextFieldTag对应TextField)以及他
们的公共抽象基类UIBean和UIBean的父类Component。
他们的继承关系如下图所示。


struts2的标签之所以能够利用ognl与action类属性相关联起来,是因为标签组件类的基类Component存储了值栈
成员变量stack,在其构造函数中进行赋值,如下
/**
* Constructor.
*
* @param stack OGNL value stack.
*/
public Component(ValueStack stack) {
this.stack = stack;
this.parameters = new LinkedHashMap();
getComponentStack().push(this);
}
何时构造这个Component呢,来看其他两个包中的类了
StrutsBodyTagSupport类继承自javax.servlet.jsp.tagext.BodyTagSupport,BodyTagSupport或
SimpleTagSupport都是jsp自定义标签必须继承的类。StrutsBodyTagSupport类主要提供获取值栈对象,查找值栈
属性,获取页面body内容的api,我们主要看下获取值栈对象的方法:

protected ValueStack getStack() {
return TagUtils.getStack(pageContext);
}

TagUtils中getStack方法代码:

public static ValueStack getStack(PageContext pageContext) {
HttpServletRequest req = (HttpServletRequest) pageContext.getRequest();
ValueStack stack = (ValueStack) req.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);

if (stack == null) {

HttpServletResponse res = (HttpServletResponse) pageContext.getResponse();
Dispatcher du = Dispatcher.getInstance();
if (du == null) {
throw new ConfigurationException("The Struts dispatcher cannot be found. This is usually caused by "+
"using Struts tags without the associated filter. Struts tags are only usable when the request "+
"has passed through its servlet filter, which initializes the Struts dispatcher needed for this tag.");
}
stack = du.getContainer().getInstance(ValueStackFactory.class).createValueStack();
Map<String, Object> extraContext = du.createContextMap(new RequestMap(req),
req.getParameterMap(),
new SessionMap(req),
new ApplicationMap(pageContext.getServletContext()),
req,
res,
pageContext.getServletContext());
extraContext.put(ServletActionContext.PAGE_CONTEXT, pageContext);
stack.getContext().putAll(extraContext);
req.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);

// also tie this stack/context to the ThreadLocal
ActionContext.setContext(new ActionContext(stack.getContext()));
} else {
// let's make sure that the current page context is in the action context
Map<String, Object> context = stack.getContext();
context.put(ServletActionContext.PAGE_CONTEXT, pageContext);

AttributeMap attrMap = new AttributeMap(context);
context.put("attr", attrMap);
}

return stack;
}

利用TagUtils的静态方法,在PageContext对象(pageContext是TagSupport类中的成员,BodyTagSupport继承自TagSupport)中获取HttpServletRequest实例,然后由request实例取得值栈属性对象,如果没有取到则在

HttpServletResponse实例中获取。

抽象类ComponentTagSupport类继承自StrutsBodyTagSupport,来看这个类的全部代码:

/**
*/
public abstract class ComponentTagSupport extends StrutsBodyTagSupport {
protected Component component;

public abstract Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res);

public int doEndTag() throws JspException {
component.end(pageContext.getOut(), getBody());
component = null;
return EVAL_PAGE;
}

public int doStartTag() throws JspException {
component = getBean(getStack(), (HttpServletRequest) pageContext.getRequest(), (HttpServletResponse) pageContext.getResponse());
Container container = Dispatcher.getInstance().getContainer();
container.inject(component);

populateParams();
boolean evalBody = component.start(pageContext.getOut());

if (evalBody) {
return component.usesBody() ? EVAL_BODY_BUFFERED : EVAL_BODY_INCLUDE;
} else {
return SKIP_BODY;
}
}

protected void populateParams() {
}

public Component getComponent() {
return component;
}
}

该类重写了TagSupport类(StrutsBodyTagSupport继承自BodyTagSupport,BodyTagSupport继承自TagSupport)的几个重要方法doStartTag(),doEndTag()。而且自己定义了一个抽象方法getBean,getBean将在其具体的标签类中有各自的实现,比如TextFieldTag的getBean实现为:

public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
return new TextField(stack, req, res);
}

它将获得一个标签类所对应的组件类的一个实例。

当用户发出请求时,doStartTag()开始执行(具体是怎样执行的还有待学习),首先就调用getBean获取对应的标签组件类实例,构造函数参数

值栈stack由基类StrutsBodyTagSupport的getStack()获得,request和response对象在PageContext实例中获取。然后调用populateParams();进行初始参数值的填充,populateParams()也将调用具体类中的populateParams()对自己的属性成员进行初始化,看TextFieldTag类中的populateParams():

/**
* @see TextField
*/
public class TextFieldTag extends AbstractUITag {

private static final long serialVersionUID = 5811285953670562288L;

protected String maxlength;
protected String readonly;
protected String size;

public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
return new TextField(stack, req, res);
}

protected void populateParams() {
super.populateParams();

TextField textField = ((TextField) component);
textField.setMaxlength(maxlength);
textField.setReadonly(readonly);
textField.setSize(size);
}
......//set属性成员方法,省略
}

先调用父类AbstractUITag中的populateParams()对所有标签共有的属性进行初始化,然后给具体自己的个性成员赋值,我们可以看到初始化是将对应的组件类成员初始化,TextFieldTag标签类有的属性成员,其对应的组件类也都有,这些初始化的值其实是初始化到对应的组件类中的对应属性,包括super.populateParams()调用AbstractUITag中的populateParams进行初始化。可以看到,AbstractUITag类抽象了所有标签的公共属性,它对应的UIBean组件基类也抽象了这些公共属性。

初始化完成之后,回到doStartTag()中,就调用Component中的start进行输出了

boolean evalBody = component.start(pageContext.getOut());

完成之后就应该调用doEndTag(),将调用component的end方法,完成html标签的输出,最后将此组件对象销毁。

从宏观上看,当用户请求页面时,将会构造页面中的标签元素,即同时也会构造出对应的组件类,当页面构造好回显到浏览器后,服务器中的该页面关联的对象都应该予以销毁。

本文转自: http://hi.baidu.com/%B7%DC%B6%B7%B2%BB%CD%A3%D0%AA/blog/item/a0911e1504ecb61e4a90a799.html
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值