主要内容:
- 防盗链
- 开发if标签
- 开发if else标签
- 开发foreach标签
- 开发HTML转义的标签
- 打包标签库
一、防盗链
防盗链的意思是,比如一个网站有一个资源很好,希望来访者先进入首页看完广告才能看到这个资源,如果不加防盗链,则别的网站可能加如此资源的超链接,那么就没有进入首页看广告。我们需要防止除从首页访问资源的其他方式。(工程tag_example
)
相关代码:
itcast.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>itcast</short-name>
<uri>/itcast</uri>
<!-- 防盗链标签 -->
<tag>
<name>referer</name>
<tag-class>cn.itcast.web.tag.RefererTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>site</name><!-- 第一个属性 -->
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>page</name><!-- 第二个属性 -->
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>
index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<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">
</head>
<body>
<a href="${pageContext.request.contextPath}/1.jsp">内容链接</a>
</body>
</html>
1.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib uri="/itcast" prefix="itcast" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<itcast:referer site="http://localhost:8080/" page="/index.jsp"/>
<html>
<head>
<title>保护页面</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
</head>
<body>
This is my JSP page. <br>
</body>
</html>
RefererTag.java
package cn.itcast.web.tag;
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;
public class RefererTag extends SimpleTagSupport {
private String site ;
private String page ;
@Override
public void doTag() throws JspException, IOException {
//拿到来访者代表page的实例对象
PageContext pageContext = (PageContext) this.getJspContext();
HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();
String referer = request.getHeader("referer");
//判断
//如果地址为空或者地址不是我们给定的地址,就跳转到page页面
if(referer == null || !referer.startsWith(site)){
HttpServletResponse response = (HttpServletResponse) pageContext.getResponse();
String webroot = request.getContextPath();//得到/day11_example
//如果page="/day11_example/index.jsp"
if(page.startsWith(webroot)){
response.sendRedirect(page);
}else {
//如果page="/index.jsp"
response.sendRedirect(webroot + page);
}
//重定向之后我们不能让保护页面显示,要让浏览器先进入index.jsp再进入保护页面
throw new SkipPageException();
}
}
public String getSite() {
return site;
}
public void setSite(String site) {
this.site = site;
}
public String getPage() {
return page;
}
public void setPage(String page) {
this.page = page;
}
}
说明:
- 1.我们在首页中给出资源页面(保护页面)的链接,在保护页面(1.jsp)中使用自定义标签进行检测:
<itcast:referer site="http://localhost:8080/" page="/index.jsp"/>
。如果资源不是从site指定的地址进入,则跳转到index.jsp
页面。当然为了更好的匹配page,我们提供两种方式指定地址,一种是page="/index.jsp"
,一种是page="/tag_example/index.jsp"
。 - 2.需要注意的是当我们处理外来地址访问之后需要抛出
SkipPageException
异常,不让后面的页面进行显示。
二、开发 if 标签
相关代码:
itcast.tld
<!-- if标签 -->
<tag>
<name>if</name>
<tag-class>cn.itcast.web.tag.IfTag</tag-class>
<body-content>scriptless</body-content>
<attribute>
<name>test</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
2.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib uri="/itcast" prefix="itcast" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>if标签</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
</head>
<body>
<itcast:if test="${user != null }">
xxxx
</itcast:if>
</body>
</html>
IfTag.java
package cn.itcast.web.tag;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
//开发if标签
public class IfTag extends SimpleTagSupport {
private boolean test;
@Override
public void doTag() throws JspException, IOException {
//只有在满足test条件的时候才执行
if(test){
this.getJspBody().invoke(null);
}
}
public boolean isTest() {
return test;
}
public void setTest(boolean test) {
this.test = test;
}
}
说明:当满足test为ture时才执行相关页面内容。
三、开发if else 标签
相关代码:
itcast.tld
<!-- if else标签 -->
<tag>
<name>choose</name>
<tag-class>cn.itcast.web.tag.ChooseTag</tag-class>
<body-content>scriptless</body-content>
</tag>
<tag>
<name>when</name>
<tag-class>cn.itcast.web.tag.WhenTag</tag-class>
<body-content>scriptless</body-content>
<attribute>
<name>test</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<tag>
<name>otherwise</name>
<tag-class>cn.itcast.web.tag.OtherwiseTag</tag-class>
<body-content>scriptless</body-content>
</tag>
ChooseTag.java
package cn.itcast.web.tag;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class ChooseTag extends SimpleTagSupport {
private boolean isOk;
@Override
public void doTag() throws JspException, IOException {
this.getJspBody().invoke(null);
}
public boolean isOk() {
return isOk;
}
public void setOk(boolean isOk) {
this.isOk = isOk;
}
}
WhenTag.java
package cn.itcast.web.tag;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class WhenTag extends SimpleTagSupport {
private boolean test;
@Override
public void doTag() throws JspException, IOException {
ChooseTag parent = (ChooseTag) this.getParent();
if(test == true && parent.isOk() == false){
this.getJspBody().invoke(null);
parent.setOk(true);
}
}
public boolean isTest() {
return test;
}
public void setTest(boolean test) {
this.test = test;
}
}
OtherwiseTag.java
package cn.itcast.web.tag;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class OtherwiseTag extends SimpleTagSupport {
@Override
public void doTag() throws JspException, IOException {
ChooseTag parent = (ChooseTag) this.getParent();
if(parent.isOk() == false){
this.getJspBody().invoke(null);
parent.setOk(true);
}
}
}
3.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib uri="/itcast" prefix="itcast" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>if else标签</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
</head>
<body>
<itcast:choose>
<itcast:when test="${user != null }">aaa</itcast:when>
<itcast:otherwise>bbb</itcast:otherwise>
</itcast:choose>
</body>
</html>
说明:开发if else
标签需要三个标签组合起来。同时我们在父标签<itcast:choose>
中设置了一个标志位,如果标志位为true表示已经执行了。默认是false。
四、开发迭代标签ForEach
相关代码:
itcast.tld
<!-- foeach标签 -->
<tag>
<name>foreach</name>
<tag-class>cn.itcast.web.tag.ForEachTag</tag-class>
<body-content>scriptless</body-content>
<attribute>
<name>var</name>
<required>true</required>
<rtexprvalue>false</rtexprvalue><!-- 这里不能是表达式 -->
</attribute>
<attribute>
<name>items</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
ForEachTag.java
package cn.itcast.web.tag;
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;
public class ForEachTag extends SimpleTagSupport {
private List items;
private String var;
@Override
public void doTag() throws JspException, IOException {
PageContext pageContext = (PageContext) this.getJspContext();
Iterator it = items.iterator();
while(it.hasNext()){
//因为我们不知道集合中的内容是什么类型,所以一定要用Object类
Object obj = it.next();
//后一次的值会覆盖前一次的值,将得到的值存到pageContext域中
pageContext.setAttribute(var, obj);
this.getJspBody().invoke(null);
}
}
public void setItems(List items) {
this.items = items;
}
public void setVar(String var) {
this.var = var;
}
}
4.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib uri="/itcast" prefix="itcast" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>ForEach标签</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
</head>
<body>
<%
List list = new ArrayList();
list.add("aa");
list.add("bb");
request.setAttribute("list", list);
%>
<itcast:foreach items="${list}" var="str">
${str }
</itcast:foreach>
</body>
</html>
说明:显然这个标签是不完善的,因为只能处理List
集合,我们需要进行改进。
相关代码:(重点)
itcast.tld
<tag>
<name>foreach2</name>
<tag-class>cn.itcast.web.tag.ForEachTag2</tag-class>
<body-content>scriptless</body-content>
<attribute>
<name>var</name>
<required>true</required>
<rtexprvalue>false</rtexprvalue><!-- 这里不能是表达式 -->
</attribute>
<attribute>
<name>items</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
ForEachTag2.java
package cn.itcast.web.tag;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.lang.reflect.Array;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class ForEachTag2 extends SimpleTagSupport {
//不知道要迭代的类型使用Object代替
private Object items;
private String var;
private Collection collection;
@Override
public void doTag() throws JspException, IOException {
PageContext pageContext = (PageContext) this.getJspContext();
Iterator it = collection.iterator();
while(it.hasNext()){
Object obj = it.next();
pageContext.setAttribute(var, obj);
this.getJspBody().invoke(null);
}
}
public void setItems(Object items) {
this.items = items;
//如果传递进来的是一个集合
if(items instanceof Collection){
collection = (Collection) items;
}
//如果传递进来的是一个对象数组
/*if(items instanceof Object[]){
Object arg[] = (Object[]) items;
collection = Arrays.asList(arg);
}*/
//如果传递进来的是一个Map
if(items instanceof Map){
Map map = (Map) items;
collection = map.entrySet();
}
//如果传递进来的是一个基本数据类型数组
/*if(items instanceof int[]){
int temp[] = (int[]) items;
collection = new ArrayList();
for(int num : temp){
collection.add(num);
}
}*///对于基本数据类型这样写八次很麻烦,sun公司的源码也是这样判断的。。。
//对于数组类型(对象数组和基本数据数组)我们使用此种方式改进
if(items.getClass().isArray()){
collection = new ArrayList();
int len = Array.getLength(items);
for(int i = 0; i < len; i++){
Object obj = Array.get(items, i);
collection.add(obj);
}
}
}
public void setVar(String var) {
this.var = var;
}
public void setCollection(Collection collection) {
this.collection = collection;
}
}
5.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib uri="/itcast" prefix="itcast" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>ForEach标签2</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
</head>
<body>
<%
List list = new ArrayList();
list.add("aa");
list.add("bb");
request.setAttribute("list", list);
Integer arr[] = new Integer[]{1,2,3};
request.setAttribute("arr", arr);
Map map = new HashMap();
map.put("a", "aaa");
map.put("b", "bbb");
map.put("c", "ccc");
request.setAttribute("map", map);
%>
<itcast:foreach items="str" var="${list }">
${str }
</itcast:foreach><br>
<itcast:foreach2 items="num" var="${var }">
${num }
</itcast:foreach2><br>
<!-- 注意基本数据迭代 -->
<%
int arr1[] = new int[]{1,2,3};
request.setAttribute("arr1", arr1);
byte[] arr2 = new byte[]{3,2,1};
request.setAttribute("arr2", arr2);
%>
<itcast:foreach2 items="num" var="${arr1 }">
${num }
</itcast:foreach2>
<itcast:foreach2 items="num" var="${arr2 }">
${num }
</itcast:foreach2>
</body>
</html>
说明:
- 1.首先我们分析一些可能会遇到的数据类型。数据类型有集合、
Map
、数组,数组中又分对象数组和基本数据类型数组。我们可以在execute
方法中依次进行判断,但是会有很多重复代码,于是我们统一在setItems
方法中进行判断。首先对于集合、Map
、对象数组我们可以统统将其转换为Collection
对象。单数对于基本数据类型数组的处理其实sun公司是进行了八次判断,显然较为麻烦。这里其实我们可以将数组统一处理,从代码中可以看到。 - 2.判断过程。如果本来就是一个
Collection
对象,则直接转换。如果是对象数组,则先转换成Object
对象,然后再转换成Collection
对象。如果是Map对象,其实本身这个对象在迭代的时候也是通过Set
集合进行迭代的,所以这里我们可以将其先转换成Collection
对象。然后是八种基本数据类型的转换,但是之后我们对数组的处理进行了修改,我们这里统一处理。
五、开发HTML转义标签
相关代码:
itcast.tld
<!--html转义标签 -->
<tag>
<name>htmlFilter</name>
<tag-class>cn.itcast.web.tag.HtmlFilterTag</tag-class>
<body-content>scriptless</body-content>
</tag>
HtmlFilterTag.java
package cn.itcast.web.tag;
import java.io.IOException;
import java.io.StringWriter;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
//这个方法在源码中可以找到
public class HtmlFilterTag extends SimpleTagSupport {
@Override
public void doTag() throws JspException, IOException {
StringWriter sw = new StringWriter();
this.getJspBody().invoke(sw);
//得到标签体
String content = sw.getBuffer().toString();
content = filter(content);
//输出
this.getJspContext().getOut().write(content);
}
private String filter(String message) {
if (message == null)
return (null);
char content[] = new char[message.length()];
message.getChars(0, message.length(), content, 0);
StringBuilder result = new StringBuilder(content.length + 50);
for (int i = 0; i < content.length; i++) {
switch (content[i]) {
case '<':
result.append("<");
break;
case '>':
result.append(">");
break;
case '&':
result.append("&");
break;
case '"':
result.append(""");
break;
default:
result.append(content[i]);
}
}
return (result.toString());
}
}
6.jsp
<body>
<itcast:htmlFilter>
<a href="#">链接</a>
</itcast:htmlFilter>
</body>
说明:这个标签中的方法filter其实我们可以在源码中找到,地址是apache-tomcat-8.0.26-src\webapps\examples\WEB-INF\classes\util\HTMLFilter.java
。主要是为了在页面中显示HTML代码,不能让页面解析使用的。
六、打包标签库
我们在自定义了一些自己需要用到的标签之后,可能在别的地方也需要使用,这时我们最好像sun公司一样将其打包成一个jar文件。
先将cn.itcast.web.tag
中所有的自定义标签类全部拷贝到一个普通java工程中的src
目录下面,这时会发现有错误,这时因为我们没有导入相应的包,这里我们需要将tomcat中的E:\software\tomcat\lib
中的jsp-api.jar
和servlet-api.jar
两个包拷贝到新建的lib
包中,然后导入即可。最后还需要将配置文件itcast.tld
拷贝到新建目录META-INF
目录中。这里我们可以看到需要建立一个lib
包保存相关的jar
包,而META-INF
中一般保存相关的配置文件。然后选中工程右键->Export->Java->JAR file->Next->选中要打包的工程,同时将右边框中MyEclipse的配置文件取消选中,然后在JAR file选择要保存的路径和文件名,文件名后缀必须为.jar,然后点击finish即可。
最后:
这些标签其实sun公司已经帮我写好了,我们只需要拿过来用即可。这里只是让我们更好的明白其是怎样实现的,以后碰到特殊的标签我们可以自己开发。