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中的标签源码解析


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

你可能感兴趣的:(struts2)