Struts2自定义标签的流程概念:
(1)需要两个类:标签类(继承相应的tag类),基本类(继承Component)。标签类专门负责从客户端取得用户输入的一些属性,这个普通的jsp自定义标签一样,取出来以后,需要把取出的属性值赋给基本类。从而可以看到基本类里面的属性也跟tag里面差不多。
(2)
tag类里面,需要定义你客户端传来的属性。私有化。并相应的set get。
必须的两个方法是public Component getBean(ValueStack stack,HttpServletRequest req, HttpServletResponse res);
protected void populateParams();
第一个方法就是获得一个基本类的对象。在基本类里面需要有传入ValueStack的构造函数,如果基本类逻辑里面需要request或者response,那么需要有传入ValueStack stack, HttpServletRequest req, HttpServletResponse res的构造函数;
第二个方法是用来将客户端传来的值赋给基本类。这里是继承来的,所以首先调用super的相应方法。我理解上面第一个方法返回的对象就是全局的一个Component对象,也就是第二个方法里面使用的对象。上面这些就是在准备参数。
(3)所有的逻辑都放到了基本类里面了,基本类里面有两个方法是需要覆盖的,当然这是常用的两个方法,还有很多其它的方法,那些继承自jsp里面的 BodyContentTag(好像是)里面的一些方法。简单说说这两个方法是一个是public boolean start(Writer writer);public boolean end(Writer writer);顾名思义,start是标签开始时的输出,end是标签结束时的输出。这里都必须执行父类里面的方法,因为很多的输出Struts2都做 好了,所以要继承过来。然后再将自己需要输出的逻辑通过writer输出字符串就可以了。
这里面需要注意start的返回值,如果是true表示将标签内的body内容显示出来,否则不显示。end好像无所谓。
第一步:建立tld文件,str.tld在项目根目录webroot/web-inf下(类似web.xml项目启动时,读取tld文件,利用反射,实例化标签对应的java类)
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN" "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd"> 3 <taglib> <!-- 自定义库标签的根 --> 4 <tlibversion>1.2</tlibversion> <!-- 版本号 --> 5 <jspversion>1.1</jspversion> <!-- JSP版本号 --> 6 <shortname>stu</shortname> <!-- prefix="stu"标签名称--> 7 <uri>StudentTags</uri> <!-- uri="StudentTags" 外界导入标签时,认识的名字,很重要。--> 8 9 <tag> 10 <name>selectAll</name> <!-- 标签名称 --> 11 <tagclass>com.bjsxt.sxf.tag.SeclectAllSt</tagclass> <!-- 对应的java类的全路径 --> 12 </tag> 13 14 <!-- 有属性的标签,可以有多个属性 <attribute>并列 --> 15 <tag> 16 <name>selectBySex</name> <!-- 标签名称 --> 17 <tagclass>com.bjsxt.sxf.tag.SeclectStBySex</tagclass> 18 <attribute> <!-- 对应标签的属性。 --> 19 <name>sex</name> 20 <required>true</required> <!-- 是必需写的属性,即没有写属性标签不能被正常使用 --> 21 </attribute> 22 </tag> 23 <!-- 按班级学生id查询出班级集合,存放到属性var指定的变量中,然后利用s标签的迭代标签,将var指定的变量存放的学生集合遍历出来 --> 24 <tag> 25 <name>selectByClassId</name> <!-- 标签名称 --> 26 <tagclass>com.bjsxt.sxf.tag.MyTag</tagclass> <!-- 对应的java类的全路径 --> 27 <body-content>JSP</body-content><!-- 如果不需要标签体则设置empty,反之设定jsp,内部可以运行jsp所有的语法 --> 28 <attribute> 29 <name>classId</name><!--指定属性名 和标签java类一致--> 30 <required>true</required><!--该属性是否是必须,如果非必须没设置则为空。 --> 31 <rtexprvalue>true</rtexprvalue><!-- 该属性能不能动态使用表达式为该属性赋值 true可以 false不可以 使用脚本和EL表达式来获取动态的值 --> 32 </attribute> 33 <attribute> 34 <name>var</name> 35 <required>true</required> 36 <rtexprvalue>false</rtexprvalue> 37 </attribute> 38 <attribute> 39 <name>num</name> 40 <required>false</required> 41 <rtexprvalue>false</rtexprvalue> 42 </attribute> 43 </tag> 44 45 </taglib>
第二步:建立标签类MyTag.java继承ComponentTagSupport类
1 package com.bjsxt.sxf.tag; 2 3 import javax.servlet.http.HttpServletRequest; 4 import javax.servlet.http.HttpServletResponse; 5 6 import org.apache.struts2.components.Component; 7 import org.apache.struts2.views.jsp.ComponentTagSupport; 8 9 import com.opensymphony.xwork2.util.ValueStack; 10 /** 11 * 标签体,传递标签的参数 12 * @ClassName: MyTag 13 * @Description: TODO(这里用一句话描述这个类的作用) 14 * @author 尚晓飞 15 * @date 2014-10-21 下午5:07:16 16 * 17 */ 18 public class MyTag extends ComponentTagSupport { 19 private String classId;//标签属性 20 private String num;//标签属性 21 private String var;//标签属性 22 23 24 //继承ComponentTagSupport类是为了获得标签中的属性值,并包装成Component对象。继承Component类是为了从Struts2中的ValueStack中获得相对应的值。 25 @Override 26 public Component getBean(ValueStack valueStack, HttpServletRequest req, 27 HttpServletResponse resp) { 28 System.out.println("MyTag.getBean()");//标签运行第一站 29 return new StuComponent(valueStack, req, resp);//返回标签业务逻辑类的对象 30 } 31 32 //将标签传递过来的值,赋值给标签业务逻辑类中的属性,业务处理逻辑会用到 33 @Override 34 protected void populateParams() { 35 super.populateParams(); 36 System.out.println("MyTag.populateParams()");//标签运行第三站 37 StuComponent stuComponent=(StuComponent) getComponent(); 38 stuComponent.setVar(var);//将前台传来的存放结果集合的变量名,赋值给逻辑类的父类中一个属性。个人理解,值栈中的一个存放结果集的集合引用。 39 stuComponent.setClassId(classId); 40 stuComponent.setNum(num); 41 //stuComponent.setVard(var);将储存数据的变量传递到标签逻辑类中 42 43 } 44 45 public String getClassId() { 46 return classId; 47 } 48 49 public void setClassId(String classId) { 50 this.classId = classId; 51 } 52 53 54 public String getNum() { 55 return num; 56 } 57 58 public void setNum(String num) { 59 this.num = num; 60 } 61 62 public String getVar() { 63 return var; 64 } 65 66 public void setVar(String var) { 67 this.var = var; 68 } 69 70 71 72 }
第三步:建立标签业务逻辑类StuComponent.java类继承ContextBean类
1 package com.bjsxt.sxf.tag; 2 3 import java.io.Writer; 4 import java.util.List; 5 import java.util.Map; 6 7 import javax.servlet.http.HttpServletRequest; 8 import javax.servlet.http.HttpServletResponse; 9 10 11 import org.apache.struts2.ServletActionContext; 12 13 import org.apache.struts2.components.ContextBean; 14 import org.apache.struts2.views.annotations.StrutsTag; 15 import org.apache.struts2.views.annotations.StrutsTagAttribute; 16 17 import org.springframework.context.ApplicationContext; 18 import org.springframework.web.context.support.WebApplicationContextUtils; 19 20 21 22 import com.bjsxt.sxf.dao.StudentDao; 23 import com.bjsxt.sxf.po.Student; 24 import com.opensymphony.xwork2.util.ValueStack; 25 26 /** 27 * MyTag标签的业务逻辑 28 * @ClassName: StuComponent 29 * @Description: TODO(这里用一句话描述这个类的作用) 30 * @author 尚晓飞 31 * @date 2014-10-21 下午5:12:31 32 * 33 */ 34 //注解现在还不清楚什么意思,就当时描述作用,没有也行。 35 @StrutsTag(name="selectByClassId", tldBodyContent="JSP", tldTagClass="com.bjsxt.sxf.tag.MyTag", description="sxf zdy") 36 public class StuComponent extends ContextBean { 37 protected String classId; 38 protected String callBack="data"; 39 protected String num; 40 protected String vard; 41 //标签进来,先运行该方法 42 public StuComponent(ValueStack stack,HttpServletRequest req, HttpServletResponse res) { 43 super(stack); 44 System.out.println("StuComponent.StuComponent()");//标签运行第二站 45 //下面可以直接获取,也可以通过标签体中的populateParams()方法,给该对象中的属性赋值set方法 46 // classId=req.getParameter("classId"); 47 // num=req.getParameter("num"); 48 49 } 50 51 52 53 54 55 56 57 //业务逻辑开始 58 @Override 59 public boolean start(Writer writer) { 60 //班级id 61 System.out.println("StuComponent.start()");//标签运行第四站 62 Integer classIds=Integer.valueOf(classId); 63 //查询出该班级的集合 64 //ssh框架获取spring的ioc容器,从而获取与数据库交互的dao对象 65 ApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(ServletActionContext.getServletContext()); 66 //获取与数据库交互的dao 67 StudentDao dao=(StudentDao) app.getBean("StudentDao"); 68 List<Student> students=dao.queryByClassId(classIds); 69 //获取标签的储存属性名 70 String varString=getVar();//此方法是继承ContextBean才有的,返回的是存储数据的变量名 71 if(varString!=null){ 72 callBack=varString; 73 } 74 //将得到的数据库数据集合,存放入集合。 75 //应该是struts2中的值栈,用来存放显示层要去的从数据库查询出来的值 76 ValueStack stack=getStack(); 77 Map<String, Object> listMap=stack.getContext(); 78 79 //标签逻辑类继承ContextBean类时,该类里有getVar()方法返回引用数据的变量 80 listMap.put(callBack, students); 81 82 //标签类继承Component类时,该类里没有getVar()方法,但可以从标签类中,将引用数据的变量通过 populateParams() 83 //传递到逻辑类里,直接存放到值栈的map中 84 //说白了,标签中取出的数据,都是从值栈的map中取,只要值栈的map<key:vlaue>中有key,就能得到value 85 //listMap.put(vard, students); 86 87 return true;//retrun true则执行标签体内的jsp页面内容,return false则跳过标签体内容,执行标签后面的内容 88 } 89 90 @Override 91 public boolean end(Writer writer, String body) { 92 System.out.println("StuComponent.end()");//标签运行第五站 93 //清空引用,可以使垃圾回收机制,回收没用的对象,减轻内存压力 94 getStack().getContext().remove(vard); 95 96 return false;//return false 或true意思暂时不明确 97 } 98 99 100 101 public String getClassId() { 102 return classId; 103 } 104 @StrutsTagAttribute(description="classId",required=true)//必须有 105 public void setClassId(String classId) { 106 this.classId = classId; 107 } 108 public String getCallBack() { 109 return callBack; 110 } 111 public void setCallBack(String callBack) { 112 this.callBack = callBack; 113 } 114 public String getNum() { 115 return num; 116 } 117 @StrutsTagAttribute(description="num",required=false)//非必须 118 public void setNum(String num) { 119 this.num = num; 120 } 121 122 123 124 125 126 127 128 public String getVard() { 129 return vard; 130 } 131 132 133 134 135 136 137 138 public void setVard(String vard) { 139 this.vard = vard; 140 } 141 142 143 144 145 146 147 }
第四步:页面测试代码
1 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> 2 <%@ taglib uri="StudentTags" prefix="stu" %> 3 <%@ taglib uri="/struts-tags" prefix="s"%> 4 <% 5 String path = request.getContextPath(); 6 String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; 7 %> 8 9 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 10 <html> 11 <head> 12 </head> 13 14 <body> 15 This is my JSP page. <br> 16 <a href="<%=request.getContextPath()%>/StudentAction!reportStudent.action">班级表单下载</a> 17 <!-- 查询出所有学生 --> 18 <stu:selectAll></stu:selectAll> 19 <!-- 查询出指定性别的学生 --> 20 <stu:selectBySex sex="男"></stu:selectBySex> 21 <!-- 查询出指定班级的学生 --> 22 <stu:selectByClassId var="students" classId="1" > 23 <table border="2"> 24 <tr> 25 <td>id</td> 26 <td>姓名</td> 27 <td>性别</td> 28 <td>班级</td> 29 </tr> 30 <s:iterator value="#students" var="stu"> 31 <tr> 32 <td><s:property value="#stu.id"/></td> 33 <td><s:property value="#stu.name"/></td> 34 <td><s:property value="#stu.sex"/></td> 35 <td><s:property value="#stu.classRoom.name"/></td> 36 </tr> 37 </s:iterator> 38 </table> 39 </stu:selectByClassId> 40 41 <h1>我是中国人</h1> 42 </body> 43
第五步:效果图(只截图了最后一个自定义标签的效果图)
struts2自定义标签编写tld文件时。<body-content>的意思
body-content的值有下面4种:
<xsd:enumeration value="tagdependent"/> <xsd:enumeration value="JSP"/> <xsd:enumeration value="empty"/> <xsd:enumeration value="scriptless"/> |
tagdependent:标签体内容直接被写入BodyContent,由自定义标签类来进行处理,而不被JSP容器解释,
如下:
<test:myList>
select name,age from users
</test:myList>
JSP:接受所有JSP语法,如定制的或内部的tag、scripts、静态HTML、脚本元素、JSP指令和动作。如:
<my:test>
<%=request.getProtocol()%> // ②
</my:test>
具体可参考后面附源码。
empty:空标记,即起始标记和结束标记之间没有内容。
下面几种写法都是有效的,
<test:mytag />
<test:mytag uname="Tom" />
<test:mytag></test:mytag>
scriptless:接受文本、EL和JSP动作。如上述②使用<body-content> scriptless </body-content>则报错,具体可参考后面附源码。