目录
环境
集成环境:MyEclipse Enterprise Workbench CI 2018.8.0
服务器:apache-tomcat-8.5.71
Java EE version: JavaEE 6 - Web 3.0
Java version: 1.8(jdk1.8.0_221)
JSTL Version: 1.2.1
系统:Windows 10 家庭中文版
编码:UTF-8
写在前面
因为只是一个技巧(?),所以就没有打包源码了,在这篇文章里都写清楚。
在上一篇的时候使用IDEA,因为没有JSTL的依赖包,为了使用c标签我们是手动添加了依赖包并且定位了一个c.tld
的文件的。而这个步骤也正好说明了我们自定义标签的话也是需要一个.tld
的文件,其余我们感受不到的,应该就是帮我们封装好了。
自定义标签库
什么是tld
t:tag, l:lib, d:description,所以.tld
文件简单来说就是一个标签(库)的描述文件。
tld文件怎么写
参考看t.tld
文件,开头部分目前我稍微能看懂的就两段。
<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">
<description>JSTL 1.1 core library</description>
<display-name>JSTL core</display-name>
<tlib-version>1.1</tlib-version>
<short-name>c</short-name>
<uri>http://java.sun.com/jsp/jstl/core</uri>
第一段应该是.tld
文件编写的规范。
第二段这五个单词还是都能理解一下:描述,展示名,tlib的版本,短名字(简称)和uri(统一资源标识)。
描述应该就是这个标签库的一个简要说明。
展示名就是这个标签库的全称。
tlib版本我不太清楚是什么的版本。
简称就是由展示名缩减成一个方便使用的名称。
uri我不知道放的是什么的链接,那就照葫芦画瓢就好了。
结合这个部分,我们大概就明白为什么我们使用的时候,在jsp文件的开头要写这么一段:<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
了。
然后往下翻到熟悉的if。
<tag>
<description>
Simple conditional tag, which evalutes its body if the
supplied condition is true and optionally exposes a Boolean
scripting variable representing the evaluation of this condition
</description>
<name>if</name>
<tag-class>org.apache.taglibs.standard.tag.rt.core.IfTag</tag-class>
<body-content>JSP</body-content>
<attribute>
<description>
The test condition that determines whether or
not the body content should be processed.
</description>
<name>test</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<type>boolean</type>
</attribute>
<attribute>
<description>
Name of the exported scoped variable for the
resulting value of the test condition. The type
of the scoped variable is Boolean.
</description>
<name>var</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
<attribute>
<description>
Scope for var.
</description>
<name>scope</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
</tag>
description描述不用说了。
name是标签名。
tag-class是标签对应的类(那就意味着我们要写个类,然后你点进去看源码的话,是可以知道我们要继承哪个接口的。)
body-content是这个标签的标签体(即两个尖括号的中间那个部分)支持填写什么:empty、JSP。
然后就是attribute属性里,required是指是不是必须要写上的属性(属性必须有,值可以没有),rtexprvalue=runtime expression value运行时表达式的值,我简单理解就是支不支持动态的值,比如${ userName }
这样,type是写这个属性接收的值的类型,如果不是基本类型的话,是要写上包名+类名的全称的。
此时结合c.tld文件中的if标签和if标签对应的IfTag类来看,就还可以知道,attribute定义的属性在类中都要定义与之对应的变量,并且要有set方法。
我这里就省去看IfTag类了,我们自己自定义标签对应的类里,一般重写三个方法即可,后面在代码的注释里去描述。
编写一个myif标签
因为我test里的值是要取自定义函数的值的,就是类似于<cs:myif test="${my:score(95)}"></cs:myif>
这个效果,所以代码看起来蛮突兀的,其实是在铺垫hh
创建MyIfTag类
package com.csdn.selma003.web.tag;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyTagSupport;
/**
* 我的自定义if标签
* @author Selma
*/
public class MyIfTag extends BodyTagSupport {
/**
* 继承BodyTagSupport和继承TagSupport的区别:
* <>Body</>
* 对于想有Body部分的标签就继承BodyTagSupport
* 反之可以直接继承TagSupport
*/
private String test;
public String getTest() {
return test;
}
public void setTest(String test) {
this.test = test;
}
/**
* 七个常量(状态):
* EVAL_BODY_AGAIN:再次计算身体(c:for就是这个原理
* EVAL_BODY_BUFFERED:计算身体缓冲(大概就是允许去读身体部分的内容
* EVAL_BODY_INCLUDE:计算包含身体
* EVAL_BODY_TAG:计算身体标签(大概就是允许去读身体部分的内容
* EVAL_PAGE:计算页面(后续的代码)
* SKIP_BODY:跳过身体(大概就是不读身体部分的内容
* SKIP_PAGE:跳过页面(后续的代码)(可以粗略理解为return
*/
/**
* 进入标签头部分就会调用这个方法
*/
public int doStartTag() throws JspException {
System.out.println("进入自定义标签头");
// return BodyTagSupport.SKIP_BODY;
return BodyTagSupport.EVAL_BODY_BUFFERED;
// return BodyTagSupport.EVAL_BODY_TAG;
}
/**
* 进入Body部分就会调用这个方法
*/
public int doAfterBody() throws JspException {
System.out.println("进入自定义标签体");
System.out.println("内容是:" + getBodyContent().getString());
JspWriter out = getBodyContent().getEnclosingWriter();
try {
//这里有个弊端,就是你必须body里要写东西,不然进不来这个方法......
out.print(getTest());
} catch (IOException e) {
e.printStackTrace();
}
//↓进入body之后再跳过body进入标签尾
return BodyTagSupport.SKIP_BODY;
}
/**
* 进入标签尾部分就会调用这个方法
*/
public int doEndTag() throws JspException {
System.out.println("进入自定义标签尾");
return BodyTagSupport.EVAL_PAGE;
}
}
创建tld文件
创建在WEB-INF文件夹下。可以创建File也可以直接选择TLD,选择TLD的话就会自动生成开头的一部分代码。
<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.1" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee web-jsptaglibrary_2_1.xsd">
<tlib-version>1.0</tlib-version>
<short-name>cs</short-name>
<uri>https://www.csdn.net/</uri>
<tag>
<name>myif</name>
<tag-class>com.csdn.selma003.web.tag.MyIfTag</tag-class>
<!-- 填empty时,如果body填了值就会报错。 -->
<!-- 填JSP时,所有JSP支持的这里都能填。 -->
<body-content>JSP</body-content>
<attribute>
<name>test</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<type>java.lang.String</type>
</attribute>
</tag>
</taglib>
自定义函数
编写一个自定义函数cal
创建MyScoreFunction类
写在类里的方法一定要是静态方法,其余没什么要求。
package com.csdn.selma003.web.function;
public class MyScoreFunction {
public static String score(double s) {
if(s >= 90) {
return "优秀";
}else if(s >= 80) {
return "良好";
}else if(s >= 70) {
return "中等";
}else if(s >= 60){
return "及格";
}else {
return "不及格";
}
}
}
创建tld文件
准确在这里是我继续在我创建的csdn.tld文件里加配置。
这里直接贴我写的配置,可能不全,但是注释里写了各个的含义。
<function>
<!-- 方法名 -->
<name>score</name>
<!-- 这个函数对应的类 -->
<function-class>com.csdn.selma003.web.function.MyScoreFunction</function-class>
<!--
signature:签名
返回值方法名与参数,参数有多个就加逗号继续写
注意非基本类型要写清包名+类名
-->
<function-signature>java.lang.String score(double)</function-signature>
</function>
index.jsp
因为自定义标签的那个uri没有在全局被注册(我是这么理解的),所以我们在开头的taglib里写当前工程里自定义的tld文件的路径,就不写那个tld文件里定义的uri了。
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="cs" uri="WEB-INF/csdn.tld"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
This is my JSP page. <br>
<cs:myif test="${ cs:score(90) }">
</cs:myif>
</body>
</html>