JavaWeb开发与代码的编写(十一)
简单标签库
javax.servlet.jsp.tagext.JspFragment类是在JSP2.0中定义的,它的实例对象代表JSP页面中的一段符合JSP语法规范的JSP片段,这段JSP片段中不能包含JSP脚本元素。 WEB容器在处理简单标签的标签体时,会把标签体内容用一个JspFragment对象表示,并调用标签处理器对象的setJspBody方法把JspFragment对象传递给标签处理器对象。JspFragment类中只定义了两个方法,如下所示:
getJspContext方法
用于返回代表调用页面的JspContext对象. public abstract void invoke(java.io.Writer out)
用于执行JspFragment对象所代表的JSP代码片段,参数out用于指定将JspFragment对象的执行结果写入到哪个输出流对象中,如果 传递给参数out的值为null,则将执行结果写入到JspContext.getOut()方法返回的输出流对象中。(简而言之,可以理解为写给浏览器)
invoke方法详解
JspFragment.invoke方法是JspFragment最重要的方法,利用这个方法可以控制是否执行和输出标签体的内容、是否迭代执行标签体的内容或对标签体的执行结果进行修改后再输出。例如: 在标签处理器中如果没有调用JspFragment.invoke方法,其结果就相当于忽略标签体内容; 在标签处理器中重复调用JspFragment.invoke方法,则标签体内容将会被重复执行; 若想在标签处理器中修改标签体内容,只需在调用invoke方法时指定一个可取出结果数据的输出流对象(例如StringWriter),让标签体的执行结果输出到该输出流对象中,然后从该输出流对象中取出数据进行修改后再输出到目标设备,即可达到修改标签体的目的。
开发带属性的标签
自定义标签可以定义一个或多个属性,这样,在JSP页面中应用自定义标签时就可以设置这些属性的值,通过这些属性为标签处理器传递参数信息,从而提高标签的灵活性和复用性。 要想让一个自定义标签具有属性,通常需要完成两个任务:
- 在标签处理器中编写每个属性对应的setter方法
- 在TLD文件中描术标签的属性
为自定义标签定义属性时,每个属性都必须按照JavaBean的属性命名方式,在标签处理器中定义属性名对应的setter方法,用来接收 JSP页面调用自定义标签时传递进来的属性值。 例如属性url,在标签处理器类中就要定义相应的setUrl(String url)方法。 在标签处理器中定义相应的set方法后,JSP引擎在解析执行开始标签前,也就是调用doStartTag方法前,会调用set属性方法,为标签设置属性。
开发带属性的标签范例
范例1:通过标签的属性控制标签体的执行次数
示例代码如下:
SimpleTagDemo5.java
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
/**
* @author gacl
* SimpleTagSupport类实现了SimpleTag接口,
* SampleTagDemo5类继承SimpleTagSupport
* 通过标签的属性控制标签体的执行次数
*/
public class SimpleTagDemo5 extends SimpleTagSupport {
/**
* 定义标签的属性
*/
private int count;
/**count属性对应的set方法
* @param count
*/
public void setCount(int count) {
this.count = count;
}
/* 简单标签使用这个方法就可以完成所有的业务逻辑
* @see javax.servlet.jsp.tagext.SimpleTagSupport#doTag()
* 重写doTag方法,通过标签的属性控制标签体的执行次数
*/
@Override
public void doTag() throws JspException, IOException {
for (int i = 0; i < count; i++) {
this.getJspBody().invoke(null);
}
}
}
在WEB-INF目录下的tld文件中添加对该标签的描述,如下所示:
demo5
me.gacl.web.simpletag.SimpleTagDemo5
scriptless
描述标签的count属性
count
true
true
在jsp页面引入标签库并使用自定义标签
通过标签的属性控制标签体的执行次数
孤傲苍狼
运行效果如下:
如果标签的属性值是8种基本数据类型,那么在JSP页面在传递字符串时,JSP引擎会自动转换成相应的类型,但如果标签的属性值是复合数据类型,那么JSP引擎是无法自动转换的
范例2:标签接收的属性值是一个复合数据类型,该如何给标签的属性赋值
示例代码如下:
SimpleTagDemo6.java
import java.io.IOException;
import java.util.Date;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
/**
* @author gacl
* SimpleTagSupport类实现了SimpleTag接口,
* SampleTagDemo6类继承SimpleTagSupport
* 标签的属性说明
*/
public class SimpleTagDemo6 extends SimpleTagSupport {
/**
* 定义标签的属性
*/
private Date date;
/**date属性对应的set方法
* @param date
*/
public void setDate(Date date) {
this.date = date;
}
/* 简单标签使用这个方法就可以完成所有的业务逻辑
* @see javax.servlet.jsp.tagext.SimpleTagSupport#doTag()
* 重写doTag方法,输出date属性值
*/
@Override
public void doTag() throws JspException, IOException {
this.getJspContext().getOut().write(date.toLocaleString());
}
在WEB-INF目录下的tld文件中添加对该标签的描述,如下所示:
demo6
me.gacl.web.simpletag.SimpleTagDemo6
empty
描述标签的date属性
date
true
true
在jsp页面引入标签库并使用自定义标签
如果标签接收的属性值是一个复合数据类型,该如何给标签的属性赋值
运行效果如下:
tld文件中用于描述标签属性的元素说明
元素的子元素用于描述自定义标签的一个属性,自定义标签所具有的每个属性都要对应一个元素
例如:
demo5
me.gacl.web.simpletag.SimpleTagDemo5
scriptless
描述标签的count属性
count
true
true
元素的子元素说明:
到此,简单标签的开发技术就算是全部讲完了,在下一篇博文中会编写一些自定义标签的案例来加深自定标签技术的学习和理解。
开发简单标签库与打包
开发防盗链标签
编写标签处理器类:RefererTag.java
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.SkipPageException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
/**
* @author gacl
* 防盗链标签RefererTag
*/
public class RefererTag extends SimpleTagSupport {
/**
* 网站域名
*/
private String site;
/**
* 要跳转的页面
*/
private String page;
@Override
public void doTag() throws JspException, IOException {
//获取jsp页面的PageContext对象
PageContext pageContext = (PageContext) this.getJspContext();
//通过PageContext对象来获取HttpServletRequest对象
HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();
//获取请求的来路(Referer)
String referer = request.getHeader("referer");
//如果来路是null或者来路不是来自我们自己的site,那么就将请求重定向到page页面
if (referer == null || !referer.startsWith(site)) {
//获取HttpServletResponse对象
HttpServletResponse response = (HttpServletResponse)pageContext.getResponse();
String webRoot = request.getContextPath();
if (page.startsWith(webRoot)) {
//重定向到page页面
response.sendRedirect(page);
} else {
//重定向到page页面
response.sendRedirect(webRoot+page);
}
//重定向后,控制保护的页面不要执行
throw new SkipPageException();
}
}
public void setSite(String site) {
this.site = site;
}
public void setPage(String page) {
this.page = page;
}
}
在WEB-INF目录下tld文件中添加对该标签的描述,如下:
孤傲苍狼开发的简单标签库
1.0
TagLib
/gaclTagLib
referer
me.gacl.web.simpletag.RefererTag
empty
描述标签的site属性
site
true
true
描述标签的page属性
page
true
true
测试:在jsp页面中导入标签库并使用防盗链标签
防盗链标签测试
网站内部资料
运行效果如下:
开发标签
编写标签处理器类:IFTag.java
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
/**
* @author gacl
* 开发if标签
*/
public class IFTag extends SimpleTagSupport {
/**
* if标签的test属性
*/
private boolean test;
@Override
public void doTag() throws JspException, IOException {
if (test) {
this.getJspBody().invoke(null);
}
}
public void setTest(boolean test) {
this.test = test;
}
}
在WEB-INF目录下tld文件中添加对该标签的描述,如下:
if标签
if
me.gacl.web.simpletag.IFTag
scriptless
if标签的test属性
test
true
true
测试:在jsp页面中导入标签库并使用if标签
if链标签测试
网站内部资料
这里的内部不输出
运行效果如下:
开发标签
这个标签的开发稍微有一点难度,因为这里面涉及到两个标签处理器类共享同一个变量的问题,如下:
用户不为空
用户为空
标签和标签对应着两个不同的标签处理器类,我们希望做到的效果是,如果标签执行了,那么就标签就不要再执行,那么这里面就涉及到一个问题:标签执行的时候该如何通知标签不要执行了呢?这个问题就涉及到了两个标签处理器类如何做到相互通讯的问题,如果标签执行了,就要通过某种方式告诉标签不要执行,那么该如何做到这样的效果呢?让标签处理器类和标签处理器类共享同一个变量就可以做到了,那么又该怎么做才能够让两个标签处理器类共享同一个变量呢,标准的做法是这样的:让两个标签拥有同一个父标签。
开发父标签:ChooseTag.java
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
/**
* @author gacl
* when标签和otherwise标签的父标签
*/
public class ChooseTag extends SimpleTagSupport {
/**
* 定义一个boolean类型的属性,该属性用于标识该标签下的某一个子标签是否已经执行过了,
* 如果该标签下的某一个子标签已经执行过了,就将该属性设置为true
*/
private boolean isExecute;
@Override
public void doTag() throws JspException, IOException {
//输出标签体中的内容
this.getJspBody().invoke(null);
}
public boolean isExecute() {
return isExecute;
}
public void setExecute(boolean isExecute) {
this.isExecute = isExecute;
}
}
开发when标签和otherwise标签
WhenTag.java
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
/**
* @author gacl
* when标签
*/
public class WhenTag extends SimpleTagSupport {
/**
* test属性,该属性值为true时,输出标签体中的内容
*/
private boolean test;
@Override
public void doTag() throws JspException, IOException {
//获取标签的父标签
ChooseTag parentTag = (ChooseTag) this.getParent();
if (test == true && parentTag.isExecute() == false) {
//输出标签体中的内容
this.getJspBody().invoke(null);
//将父标签的isExecute属性设置为true,告诉父标签,我(when标签)已经执行过了
parentTag.setExecute(true);
}
}
public void setTest(boolean test) {
this.test = test;
}
}
OtherWiseTag.java
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
/**
* @author gacl
* otherwise标签
*/
public class OtherWiseTag extends SimpleTagSupport {
@Override
public void doTag() throws JspException, IOException {
//获取标签的父标签
ChooseTag parentTag = (ChooseTag) this.getParent();
//如果父标签下的when标签没有执行过
if (parentTag.isExecute() == false) {
//输出标签体中的内容
this.getJspBody().invoke(null);
//设置父标签的isExecute属性为true,告诉父标签,我(otherwise标签)已经执行过了
parentTag.setExecute(true);
}
}
}
在WEB-INF目录下tld文件中添加对ChooseTag、WhenTag、OtherWiseTag这三对标签的描述,如下:
choose标签
choose
me.gacl.web.simpletag.ChooseTag
scriptless
when标签
when
me.gacl.web.simpletag.WhenTag
scriptless
when标签的test属性
test
true
true
otherwise标签
otherwise
me.gacl.web.simpletag.OtherWiseTag
scriptless
测试:在jsp页面中导入标签库并测试when和otherwise标签
when和otherwise标签测试
when标签标签体输出的内容:
用户为空
用户不为空
用户不为空
otherwise标签标签体输出的内容:
用户为空
运行效果如下:
开发foreach迭代标签
编写标签处理器类:ForEachTag.java
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.SimpleTagSupport;
/**
* @author gacl
* ForEach迭代标签
*/
public class ForEachTag extends SimpleTagSupport {
/**
* 存储集合
*/
private List items;
/**
* 迭代集合时使用的变量
*/
private String var;
public void setItems(List items) {
this.items = items;
}
public void setVar(String var) {
this.var = var;
}
@Override
public void doTag() throws JspException, IOException {
PageContext pageContext = (PageContext) this.getJspContext();
Iterator it = items.iterator();
while (it.hasNext()) {
//得到一个迭代出来的对象
Object object = (Object) it.next();
//将迭代出来的对象存放到pageContext对象中
pageContext.setAttribute(var, object);
//输出标签体中的内容
this.getJspBody().invoke(null);
}
}
}
在WEB-INF目录下tld文件中添加对该标签的描述,如下:
foreach标签
foreach
me.gacl.web.simpletag.ForEachTag
scriptless
foreach标签的items属性
items
true
true
foreach标签的var属性
var
false
true
测试:在jsp页面中导入标签库并使用foreach标签
foreach标签测试
${str}
运行效果如下:
目前这个foreach标签的功能较弱,只能遍历list集合,下面我们改造一下,使我们的foreach标签可以遍历所有集合类型,修改后foreach标签的代码如下:
3 import java.io.IOException;
4 import java.lang.reflect.Array;
5 import java.util.ArrayList;
6 import java.util.Collection;
7 import java.util.Iterator;
8 import java.util.Map;
9
10 import javax.servlet.jsp.JspException;
11 import javax.servlet.jsp.PageContext;
12 import javax.servlet.jsp.tagext.SimpleTagSupport;
13
14 /**
15 * @author gacl
16 * ForEach迭代标签
17 */
18 public class ForEachTag extends SimpleTagSupport {
19
20 /**
21 * 存储数据
22 */
23 private Object items;
24
25 /**
26 * 迭代集合时使用的变量
27 */
28 private String var;
29
30 /**
31 * 集合,用于存储items中的数据
32 */
33 private Collection collection;
34
35 @Override
36 public void doTag() throws JspException, IOException {
37 PageContext pageContext = (PageContext) this.getJspContext();
38 //迭代collection集合
39 Iterator it = collection.iterator();
40 while (it.hasNext()) {
41 //得到一个迭代出来的对象
42 Object object = (Object) it.next();
43 //将迭代出来的对象存放到pageContext对象中
44 pageContext.setAttribute(var, object);
45 //输出标签体中的内容
46 this.getJspBody().invoke(null);
47 }
48 }
49
50 public void setVar(String var) {
51 this.var = var;
52 }
53
54 public void setItems(Object items) {
55 if (items instanceof Collection) {
56 collection = (Collection) items;//list
57 }else if (items instanceof Map) {
58 Map map = (Map) items;
59 collection = map.entrySet();//map
60 }else if (items.getClass().isArray()) {
61 collection = new ArrayList();
62 //获取数组的长度
63 int len = Array.getLength(items);
64 for (int i = 0; i < len; i++) {
65 //获取数组元素
66 Object object = Array.get(items, i);
67 collection.add(object);
68 }
69 }
70 this.items = items;
71 }
72 }
测试功能增强后的foreach标签,如下:
1
2
3
4
5
6
7
8
9
10
13
14
15
16 foreach标签测试
17
18
19
42
43
44
45
46 ${str}
47
48
49
50
51 ${num}
52
53
54
55
56 ${num}
57
58
59
60
61 ${me}
62
63
64
测试结果:
开发html转义标签
编写标签处理器类:HtmlEscapeTag.java
3 import java.io.IOException;
4 import java.io.StringWriter;
5 import javax.servlet.jsp.JspException;
6 import javax.servlet.jsp.tagext.SimpleTagSupport;
7
8 /**
9 * @author gacl
10 * html转义标签
11 */
12 public class HtmlEscapeTag extends SimpleTagSupport {
13
14 /**
15 * @param message
16 * @return 转义html标签
17 */
18 private String filter(String message) {
19
20 if (message == null){
21 return (null);
22 }
23 char content[] = new char[message.length()];
24 message.getChars(0, message.length(), content, 0);
25 StringBuilder result = new StringBuilder(content.length + 50);
26 for (int i = 0; i < content.length; i++) {
27 switch (content[i]) {
28 case '':
32 result.append(">");
33 break;
34 case '&':
35 result.append("&");
36 break;
37 case '"':
38 result.append(""");
39 break;
40 default:
41 result.append(content[i]);
42 }
43 }
44 return (result.toString());
45 }
46
47 @Override
48 public void doTag() throws JspException, IOException {
49 StringWriter sw = new StringWriter();
50 //将标签体中的内容先输出到StringWriter流
51 this.getJspBody().invoke(sw);
52 //得到标签体中的内容
53 String content = sw.getBuffer().toString();
54 //转义标签体中的html代码
55 content = filter(content);
56 //输出转义后的content
57 this.getJspContext().getOut().write(content);
58 }
59 }
在WEB-INF目录下tld文件中添加对该标签的描述,如下:
HtmlEscape标签
htmlEscape
me.gacl.web.simpletag.HtmlEscapeTag
scriptless
测试:在jsp页面中导入标签库并使用htmlEscape标签
html转义标签测试
访问博客园
运行结果:
开发out输出标签
编写标签处理器类:OutTag.java
3 import java.io.IOException;
4
5 import javax.servlet.jsp.JspException;
6 import javax.servlet.jsp.tagext.SimpleTagSupport;
7
8 /**
9 * @author gacl
10 * 开发输出标签
11 */
12 public class OutTag extends SimpleTagSupport {
13
14 /**
15 * 要输出的内容
16 */
17 private String content;
18
19 /**
20 * 是否将内容中的html进行转义后输出
21 */
22 private boolean escapeHtml;
23
24 public void setContent(String content) {
25 this.content = content;
26 }
27
28 public void setEscapeHtml(boolean escapeHtml) {
29 this.escapeHtml = escapeHtml;
30 }
31
32 @Override
33 public void doTag() throws JspException, IOException {
34 if (escapeHtml == true) {
35 //转义内容中的html代码
36 content = filter(content);
37 //输出转义后的content
38 this.getJspContext().getOut().write(content);
39 }else {
40 this.getJspContext().getOut().write(content);
41 }
42 }
43
44 /**
45 * @param message
46 * @return 转义html标签
47 */
48 private String filter(String message) {
49
50 if (message == null){
51 return (null);
52 }
53 char content[] = new char[message.length()];
54 message.getChars(0, message.length(), content, 0);
55 StringBuilder result = new StringBuilder(content.length + 50);
56 for (int i = 0; i < content.length; i++) {
57 switch (content[i]) {
58 case '':
62 result.append(">");
63 break;
64 case '&':
65 result.append("&");
66 break;
67 case '"':
68 result.append(""");
69 break;
70 default:
71 result.append(content[i]);
72 }
73 }
74 return (result.toString());
75 }
76 }
在WEB-INF目录下tld文件中添加对该标签的描述,如下:
out标签
out
me.gacl.web.simpletag.OutTag
empty
out标签的content属性,表示要输出的内容
content
true
true
out标签的escapeHtml属性,表示是否将内容中的html进行转义后输出
escapeHtml
true
false
测试:在jsp页面中导入标签库并使用out标签
out标签测试
运行效果如下:
打包开发好的标签库
我们的标签库开发好之后,为了方便别人使用,可以将开发好
我们的标签库开发好之后,为了方便别人使用,可以将开发好的标签库打包成一个jar包,具体的打包步骤如下:
1、新建一个普通的java工程,例如:taglib
2、将在JavaWeb_JspTag_study_20140816这个web工程中开发好标签库的java代码拷贝到普通java工程taglib项目中,如下:
此时,我们可以看到,拷贝到taglib项目的标签代码都有错误,这是因为taglib项目中缺少了javaEE的jar包,而标签类是是基于javaEE API进行开发的,所以还需要将javaEE的jar包添加到taglib项目中。
在taglib项目中创建一个【lib】文件夹,用于存放标签类依赖的javaEE的jar包。找到tomcat服务器目录下的lib文件夹,如下图所示:
将【jsp-api.jar】和【servlet-api.jar】这两个jar包拷贝到tagib项目中的lib文件夹中,然后添加【jsp-api.jar】和【servlet-api.jar】这两个jar包的引用,如下所示:
在taglib项目中引用了【jsp-api.jar】和【servlet-api.jar】这两个jar包后,标签类中的代码就不再报错了
3、在taglib项目中添加一个【META-INF】文件夹,如下所示:
将位于WEB-INF目录下的标签库对应的tld文件拷贝到taglib项目的【META-INF】文件夹中
4、将taglib项目打包成jar包
此时就可以看到我们打包好的jar包了,如下所示:
将标签库打包成jar包之后,以后哪个web项目要使用标签库,那么就将打包好的标签库jar包添加到web项目中就可以使用标签库中的标签了。