JSP custom tags the first analysis of Struts2 tags

1. The role of custom labels

During the development of the back-end program, some frequently used codes will be encapsulated and reused to speed up development and reduce errors. When there are bugs, other reference locations can be modified once and automatically repaired. On the front-end Web page, if I often use some fixed-format display functions and involve server-side logic operations, I need to use custom tags. Custom tags make it easy for us to call a common code . Frameworks such as Spring MVC and Struts2 have defined their own JSP page tags, so that some fixed functions can be realized through the reuse of tags, such as struts2's <s:property value="message"/> tag, you can use tags After reading the properties in the action and the property values ​​in the fields such as session and request, we will start by analyzing the property tag and understand the definition process of custom tags in java.

2. Class hierarchy analysis of the property tag in struts2

Class hierarchy of property tags in struts2
It can be seen from the above class hierarchy diagram that the top-level parent class of the property tag is TagSupport. The package of this class is javax.servlet.jsp.tagext. By observing the package name of the class in the entire hierarchy, you can see that the TagSupport class and BodyTagSupport are two One class is defined in the Java JDK, and the other three classes are defined in the struts2 framework, so if you want to implement a custom tag, you need to inherit from the TagSupport class or the BodyTagSupport class. Of course, you can also directly inherit from the defined in the framework TagSupport class and BodyTagSupport extended subclass, but this will cause dependence on specific frameworks such as struts2, if not necessary, it is best to extend from the above TagSupport class and BodyTagSupport class.

3. Analysis of TagSupport and BodyTagSupport

The main difference between TagSupport and BodyTagSupport is whether the tag processing class needs to interact with the tag body. If there is no need for interaction, use TagSupport, otherwise use BodyTagSupport. The interaction is whether the tag processing class should read the content of the tag body and change the content returned by the tag body. All tags implemented with TagSupport can be implemented with BodyTagSupport, because BodyTagSupport inherits TagSupport.
Tag processing class: refers to the subclass derived from the TagSupport class or its subclasses. For example, the PropertyTag class in the figure is a label processing class.
Tag body: It is the tag we said at the beginning, such as <s:property value="message"/> tag, its specific definition needs to be established in WEB-INF tld file tag.tld. For the definition format of tag.tld, please refer to the tag tld definition file of struts2. I am using the struts21.3.37 version. The specific tld file is named struts2-tags.tld in the META-INF in the struts2-core-2.3.37.jar package. The specific code is as follows:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd">
  <description><![CDATA['To make it easier to access dynamic data the Apache Struts framework includes a library of custom tags. The tags interact with the framework validation and internationalization features to ensure that input is correct and output is localized. The Struts Tags can be used with JSP FreeMarker or Velocity.']]></description>
  <display-name>Struts Tags</display-name>
  <tlib-version>2.3</tlib-version>
  <short-name>s</short-name>
  <uri>/struts-tags</uri>
  <tag>
    <description><![CDATA[Print out expression which evaluates against the stack]]></description>
    <name>property</name>
    <tag-class>org.apache.struts2.views.jsp.PropertyTag</tag-class>
    <body-content>empty</body-content>
    <attribute>
      <description><![CDATA[The default value to be used if <u>value</u> attribute is null]]></description>
      <name>default</name>
      <required>false</required>
      <rtexprvalue>false</rtexprvalue>
    </attribute>
    <attribute>
      <description><![CDATA[Deprecated. Use 'escapeHtml'. Whether to escape HTML]]></description>
      <name>escape</name>
      <required>false</required>
      <rtexprvalue>false</rtexprvalue>
    </attribute>
    <attribute>
      <description><![CDATA[Whether to escape CSV (useful to escape a value for a column)]]></description>
      <name>escapeCsv</name>
      <required>false</required>
      <rtexprvalue>false</rtexprvalue>
    </attribute>
    <attribute>
      <description><![CDATA[Whether to escape HTML]]></description>
      <name>escapeHtml</name>
      <required>false</required>
      <rtexprvalue>false</rtexprvalue>
    </attribute>
    <attribute>
      <description><![CDATA[Whether to escape Javascript]]></description>
      <name>escapeJavaScript</name>
      <required>false</required>
      <rtexprvalue>false</rtexprvalue>
    </attribute>
    <attribute>
      <description><![CDATA[Whether to escape XML]]></description>
      <name>escapeXml</name>
      <required>false</required>
      <rtexprvalue>false</rtexprvalue>
    </attribute>
    <attribute>
      <description><![CDATA[Value to be displayed]]></description>
      <name>value</name>
      <required>false</required>
      <rtexprvalue>false</rtexprvalue>
    </attribute>
    <dynamic-attributes>false</dynamic-attributes>
  </tag>
  </taglib>

A lot of tags are defined in struts2-tags.tld, which are defined in the <tag></tag>tags. Due to the length of the space, I only posted the definition of the tags with the property. A tld file (tag library file) can define any number of tags. Use one <tag></tag>definition for each tag .

3.1. Definition of tag library (tld file)

The following describes the format definition of the tld file (tag library file) ( note that a tld file corresponds to a tag library, and the specific code definition can refer to the definition format of the struts2-tags.tld file above ):

1. Introduce the namespace (xmlns) in the taglib tag
<taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd">
2. The description tag defines the description of the tag library, pay attention to the description in the <![CDATA[Content]]> tag;
3. The display-name tag defines the current tag library Display name, defined in the struts2-tags.tld file is Struts Tags;
4. The tlib-version tag defines the version of the current tag library, and the definition in the struts2-tags.tld file is 2.3;
5. Short-name tag definition The prefix that the current tag library can use when quoting is defined in the struts2-tags.tld file as s;
6. The uri tag defines the uri attribute that the current tag library needs to make when referencing the jsp page, in the struts2-tags.tld file The definition in is /struts-tags; after the
above 6 parameters are defined, especially the last two parameters when referencing the tag library in the JSP page, the specific configuration is as follows, the prefix and uri tags must be consistent with those defined in the tld file:

<%@ taglib prefix="s" uri="/struts-tags" %>

After introducing the <%@ taglib prefix="s" uri="/struts-tags" %>tag library in the header of the jsp page , you can use <s:property value="message"/>tags in the page

3.2. Definition of tag

The <taglib>tags are defined above , but they cannot be used yet. You need <taglib>to define specific tags in the <tag>tags. Each <tag>tag corresponds to a tag. The <taglib>tag contains four elements:

  1. <description>The description of the tag, pay attention to the description in the <![CDATA[Content]]> tag;

  2. <name> The name of the label, such as the property defined in the above code;

  3. <tag-class>The processing class of the tag, pay attention to write the full class name including the package path, the above code is written as org.apache.struts2.views.jsp.PropertyTag. The tag processing class mentioned at the beginning of this chapter is introduced here, which is the subclass derived from the TagSupport class or its subclass ;

  4. <body-content>The value of body-content has the following 4 types:
    tagdependent : The content of the tag body is directly written into BodyContent, processed by the custom tag class, and not interpreted by the JSP container, as follows:

           <test:myList>
                 select name,age from users
           </test:myList>
    

    JSP : Accept all JSP syntax, such as custom or internal tags, scripts, static HTML, script elements, JSP instructions and actions. Such as:

          <my:test>
    
               <%=request.getProtocol()%>    // ②
        
          </my:test>
    

    empty : empty tag, that is, there is no content between the start tag and the end tag. The following writing methods are all valid,

           <test:mytag />
    
           <test:mytag uname="Tom" />
    
          <test:mytag></test:mytag>
    

    scriptless : Accept text, EL and JSP actions. If the above ② is used <body-content> scriptless </body-content>, an error will be reported.
    Through the description of the above four values, you can clearly understand the meaning of the body-content element, which refers to the acceptable value between the start and end symbols of the tag, such as <s:property value="message">这个位置不可以写值,因为是定义的empty</s:property>.

  5. <attribute>For the characteristics of the label, let’s look at an example of the <s:property value="message"> tag:

<attribute>
      <description><![CDATA[Value to be displayed]]></description>
      <name>value</name>
      <required>false</required>
      <rtexprvalue>false</rtexprvalue>
</attribute>

The above code is a feature of the <s:property/> tag. The <description>element is the same as the one mentioned above, so the name suggests to describe this feature. <name>Element defines the name of the property, who will not write the name, but to the tag handler class consistent property values, which means that <s:property/>there is a value tag handler class field, it must have a setValue () method, That is, a setter, this is a mandatory specification, or conversely, if there are several setters in a tag processing class that require the tag body to pass values ​​to the processing class, several setters must be <tag>defined in the tag <attribute>. <required>The element specifies whether the current attribute is required. If it is specified as true, then this attribute must be set in the label. If <s:property/>the value attribute of <required>false</required>the <s:property/>label is specified as then, the label must be in this form when used <s:property value="具体的值"/>. About <rtexprvalue>element explanation: The full name of rtexprvalue is Run-time Expression Value, which is used to indicate whether JSP expressions can be used. When true is specified in the tag, it means that the value of an attribute of the custom tag can be specified directly or dynamically calculated Specify, such as <myTag:cupSize cupSize="1" cupSizes="${result}"></myTag:cupSize>.

Through the above introduction, you can completely define a tag library (tld) file. Although the tld file is defined, the tag cannot be used because the tag processing class has not yet been defined. Specifically, the tag is derived from the TagSupport class or its subclasses. The method of processing the class is described in the next section.

3.3 Definition of label processing class

3.3.1 Class diagram of the TagSupport class

Class diagram description of the TagSupport class
It can be seen that TagSupport implements the interface IterationTag , this interface inherits the Tag interface, and the Tag interface inherits the JspTag interface. The JspTag interface is an empty interface and the Serializable interface is a tag interface. Let’s take a look at the Tag interface first , and I will write it directly in the code with comments:

package javax.servlet.jsp.tagext;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;

public interface Tag extends JspTag {
   //四个静态常量,等同于修饰符public static final(默认不写)
   //表示不显示标签间的文字
    int SKIP_BODY = 0;
    //表示将显示标签间的文字
    int EVAL_BODY_INCLUDE = 1;
    //表示不处理接下来的JSP网页
    int SKIP_PAGE = 5;
    //表示处理完标签后继续执行以下的JSP网页
    int EVAL_PAGE = 6;
   //页面上下文
    void setPageContext(PageContext var1);
    //设置父Tag
    void setParent(Tag var1);
   //获取父Tag
    Tag getParent();
   //遇到标签开始时会呼叫的方法,其合法的返回值是
   //EVAL_BODY_INCLUDE与SKIP_BODY,前者表示将显示标签间的文字,后者表示不显示标签间的文字
    int doStartTag() throws JspException;
   //在遇到标签结束时呼叫的方法,其合法的返回值是EVAL_PAGE与SKIP_PAGE
   //前者表示处理完标签后继续执行以下的JSP网页,后者是表示不处理接下来的JSP网页
    int doEndTag() throws JspException;
   //释放资源
    void release();
}

package javax.servlet.jsp.tagext;

import javax.servlet.jsp.JspException;

public interface IterationTag extends Tag {
    //会再显示一次标签间的文字
    int EVAL_BODY_AGAIN = 2;
    //这个方法是在显示完标签间文字之后呼叫的,其返回值有EVAL_BODY_AGAIN与SKIP_BODY
    //前者会再显示一次标签间的文字,后者则继续执行标签处理的下一步
    int doAfterBody() throws JspException;
}

The IterationTag interface can be seen from the definition and naming of the method to provide a repeated action.
Below we look at the TagSupport class

package javax.servlet.jsp.tagext;

import java.io.Serializable;
import java.util.Enumeration;
import java.util.Hashtable;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;

public class TagSupport implements IterationTag, Serializable {
    private Tag parent;
    private Hashtable values;
    protected String id;
    protected PageContext pageContext;
public static final Tag findAncestorWithClass(Tag from, Class klass) {
    boolean isInterface = false;
    if (from != null && klass != null && ((class$javax$servlet$jsp$tagext$Tag == null ? (class$javax$servlet$jsp$tagext$Tag = class$("javax.servlet.jsp.tagext.Tag")) : class$javax$servlet$jsp$tagext$Tag).isAssignableFrom(klass) || (isInterface = klass.isInterface()))) {
        while(true) {
            Tag tag = from.getParent();
            if (tag == null) {
                return null;
            }

            if (isInterface && klass.isInstance(tag) || klass.isAssignableFrom(tag.getClass())) {
                return tag;
            }

            from = tag;
        }
    } else {
        return null;
    }
}

public TagSupport() {
}

public int doStartTag() throws JspException {
    return 0;表示不显示标签间的文字
}

public int doEndTag() throws JspException {
    return 6;//表示处理完标签后继续执行以下的JSP网页
}

public int doAfterBody() throws JspException {
    return 0;表示不显示标签间的文字
}

public void release() {
    this.parent = null;
    this.id = null;
    if (this.values != null) {
        this.values.clear();
    }

    this.values = null;
}

public void setParent(Tag t) {
    this.parent = t;
}

public Tag getParent() {
    return this.parent;
}

public void setId(String id) {
    this.id = id;
}

public String getId() {
    return this.id;
}

public void setPageContext(PageContext pageContext) {
    this.pageContext = pageContext;
}

public void setValue(String k, Object o) {
    if (this.values == null) {
        this.values = new Hashtable();
    }

    this.values.put(k, o);
}

public Object getValue(String k) {
    return this.values == null ? null : this.values.get(k);
}

public void removeValue(String k) {
    if (this.values != null) {
        this.values.remove(k);
    }

}

public Enumeration getValues() {
    return this.values == null ? null : this.values.keys();
}

}

The doStartTag() and doAfterBody() methods return by default without displaying the text between the tags; the doEndTag() method returns by default to continue to execute the following JSP pages after the tags are processed. These three methods are the most useful methods when customizing tags. The pageContext is injected through the setter, and the pageContext can manipulate the context of the current page, including painting, request, out output stream, etc.
Let’s look at the class diagram of the
BodyTagSupport class diagram
BodyTagSupport class. The BodyTagSupport class implements the BodyTag interface and inherits the TagSupport class. The newly added protected BodyContent bodyContent;getter and setter of a field in the BodyTagSupport class directly hold a JspWriter, the character output stream of the jsp page, which can be sent to Page output characters. So far we can see that the BodyTagSupport class interacts with the tag through bodyContent.
In addition, the return value of doStartTag() is 2, and EVAL_BODY_AGAIN will display the text between tags again.
Okay, I wrote so much for the first article on custom labels. The next article will record how to write a custom label.

Guess you like

Origin blog.csdn.net/u011930054/article/details/88199420