我们经常会遇到这种场景: 假设项目中有一个分类对象,类似数据字典,一个code对应一个name。这个在页面作为一个过滤条件:选择分类。一般做法是在jsp页面直接写select标签,用option在页面
做判断。比如:
<label>选择分类:
<select id="levelClassSelect" name="classCode"
class="form-control input-small input-sm input-inline"
>
<option value="-1" <c:if test="${searchParam.classCode==-1}"> selected="selected" </c:if>>全部
</option>
<c:forEach items="${levelClassDtoList}" var="v_levelClass">
<option value="${v_levelClass.code}"
<c:if test="${v_levelClass.id==searchParam.classCode}">selected="selected" </c:if>>${v_levelClass.name}</option>
</c:forEach>
</select>
</label>
这样操作我们需要在后台Controller中查询放置levelClassDtoList对象,页面写这样一个select标签。如果这个标签是在多个页面使用,那么我们每个页面都需要复制一份,每个页面对应的后台Controller中也要查询放置一份levelClassDtoList对象。这样就重复做事情,而且一旦有变动,需要改很多地方,费时,还容易出错。所有我们就想到可不可把这个自定义成一个标签,多处使用?方便我们使用和后期维护。可以的。结合自己的项目,需要以下4个步骤:
- 定义java的tag类:LevelCodeTag
采用继承org.springframework.web.servlet.tags.RequestContextAwareTag
RequestContextAwareTag类是一个abstract修饰的抽象类,有一个protected级别的抽象方法,子类需要实现这个方法,将自己需要的数据放入pageContext对象中即可。
public abstract class RequestContextAwareTag extends TagSupport implements TryCatchFinally { ...//省略部分代码 /** * Called by doStartTag to perform the actual work. * @return same as TagSupport.doStartTag * @throws Exception any exception, any checked one other than * a JspException gets wrapped in a JspException by doStartTag * @see javax.servlet.jsp.tagext.TagSupport#doStartTag */ protected abstract int doStartTagInternal() throws Exception; ...//省略部分代码 } LevelCodeTag: public class LevelCodeTag extends RequestContextAwareTag { @Override protected int doStartTagInternal() throws Exception { //通过注入spring的levelClassService.findAll()方法得到数据库的所有levelCode对象,并放置在pageContext对象中。 ILevelClassService levelClassService=getBean("levelClassService"); pageContext.setAttribute("levelCodeList", levelClassService.findAll()); return 0; } private WebApplicationContext wac; /** * 从web应用上下文中,根据注册的beanName获取对象 * @param beanName spring 管理的beanName * @param <T> * @return */ @SuppressWarnings("unchecked") protected <T> T getBean(String beanName) { if (wac == null) { wac = getRequestContext().getWebApplicationContext(); } return (T) wac.getBean(beanName); } }
- 定义tld文件
这个文件是放置在WEB-INF目录下yq_fn.tld,最简配置如下:
<?xml version="1.0" encoding="UTF-8" ?> <taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" version="2.0"> <tlib-version>1.1</tlib-version><!--版本号--> <short-name>yq_fn</short-name><!--后面引用的短名称--> <uri>http://www.wangan.com/media/tag</uri><!--定义uri,便于后面引用定位--> <tag> <name>listLevelCode</name><!--名字--> <tag-class>com.wangan.media.tag.LevelCodeTag</tag-class><!--指向步骤1中定义的java的tag类--> <body-content>empty</body-content><!--使用empty即可--> </tag> </taglib>
- 定义tag标签文件
定义了tld文件,我们就可以通过tld文件得到javatag中的levelCodeList对象,定义levelCode.tag文件,这个文件和步骤2中的yq_fn.tld文件在同一目录下。
<%@tag pageEncoding="utf-8" %><!--定义编码,防止乱码 --> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><!--引入c标签--> <%@taglib prefix="yq" uri="http://www.wangan.com/media/tag" %><!-- 引入步骤2中我们自定义的tld文件,定义前缀为yq(这个可以根据自己项目任意定义),uri值是tld文件中的uri值--> <!-- 下面定义这个select标签需要的属性字段--> <%@attribute name="levelName" required="true" type="java.lang.String" rtexprvalue="true"%><!--name:属性的名字,required:使用这个标签时候,本属性是否必须的;type:这个属性值的类型;rtexprvalue:这个标签是否支持el表达式,我们设置为true,支持表达式,这样就可以在引用时候动态设置值--> <%@attribute name="levelCode" required="true" rtexprvalue="true"%> <%@attribute name="selectId" required="true" rtexprvalue="true" %> <yq:listLevelCode/><!--引入文件2中订阅的listLevelCode,就相当于引入了步骤一中的java代码,这样我们就能访问步骤一中定义的levelCodeList对象。yq就是第三行定义的前缀,liestLevelCode就是步骤2中定义的name值--> <!--下面就是动态定义我们的select标签了,class值是我项目中使用到的,请忽略--> <select class="form-control input-small input-sm input-inline" id="${selectId}" name="${levelName}"> <option value="0" <c:if test="${levelCode ==0}"> selected="selected" </c:if>>全部</option> <c:forEach items="${levelCodeList}" var="v_level" > <option value="${v_level.code}" <c:if test="${v_level.code== levelCode}"> selected="selected" </c:if>>${v_level.name}</option> </c:forEach> </select>
- web.xml中配置tld文件
将步骤2中定义的tld文件交给项目管理
<jsp-config> <taglib> <taglib-uri>http://www.wangan.com/media/tag</taglib-uri><!--这个地址就是步骤2中的uri值--> <taglib-location>/WEB-INF/tags/yq_fn.tld</taglib-location><!--这个值就是步骤2中定义的tld文件的路径--> </taglib> </jsp-config>
- jsp使用自定义标签
<%@ taglib prefix="yq" tagdir="/WEB-INF/tags" %><!--引入步骤3中自定义的标签-->
<label>选择分类: <yq:levelCode selectId="levelClassCode" levelName="levelClassCode" levelCode="${levelClassCode}"/> <!--yq就是引入的时定义的前缀,levelCode就是引入是tagdir目录下的*.tag文件的名字。我们要引入步骤3中定义的levelCode.tag标签。levelCode属性是页面用el表达式动态获取--> </label>
- 页面效果
总结:几个简单步骤后,我们就实现了一次定义,随意使用的效果。以后每次使用就按照步骤5引用就可以了。这样我们关于levelCode对象的值都没有在页面写死,以后需要修改这个对象数据,我们直接修改库的字典就可。最开始,由于项目时间要求,本人是直接在页面对select标签写死value和name的,后面项目需要扩展,增加分类,此时就需要修改多出页面和数据库,由于后面也有多余的时间,则抽取出来成标签,以后再需要修改,就直接修改数据库字典即可。