* 自定义便签(高级)
* 作用:自定义标签主要用于移除Jsp页面中的java代码
* 自定义标签入门
* 新建一个类ViewIp implements Tag
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.Tag;
public class ViewIp implements Tag{
private PageContext pc;
@Override
public void setPageContext(PageContext pc) {
System.out.println("setPageContext");
this.pc=pc;
}
@Override
public void setParent(Tag t) {
System.out.println("setParent");
}
@Override
public Tag getParent() {
return null;
}
@Override
public int doStartTag() throws JspException {
System.out.println("doStartTag");
String ip = pc.getRequest().getRemoteAddr();
try {
pc.getOut().print("ip:"+ip);
} catch (IOException e) {
e.printStackTrace();
}
return 0;
}
@Override
public int doEndTag() throws JspException {
System.out.println("doEndTag");
return 0;
}
@Override
public void release() {
System.out.println("release");
}
}
* 添加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">
<!-- description用来添加对taglib(标签库)的描述 -->
<description>自定义标签库</description>
<!--taglib(标签库)的版本号 -->
<tlib-version>1.0</tlib-version>
<short-name>HxTagLibrary</short-name>
<!--
为自定义标签库设置一个uri,uri以/开头,/后面的内容随便写,如这里的/hx ,
在Jsp页面中引用标签库时,需要通过uri找到标签库
在Jsp页面中就要这样引入标签库:<%@taglib uri="/hx" prefix="hx"%>
-->
<uri>/hx</uri>
<!--一个taglib(标签库)中包含多个自定义标签,每一个自定义标签使用一个tag标记来描述 -->
<!-- 一个tag标记对应一个自定义标签 -->
<tag>
<description>这个标签的作用是用来输出客户端的IP地址</description>
<!--
为标签处理器类配一个标签名,在Jsp页面中使用标签时是通过标签名来找到要调用的标签处理器类的
通过viewIP就能找到对应的类com.hx.tag.ViewIp
-->
<name>viewIp</name>
<!-- 标签对应的处理器类-->
<tag-class>com.hx.bean.ViewIp</tag-class>
<body-content>empty</body-content>
</tag>
</taglib>
* 自定义标签API介绍
* Tag * The interface of a classic tag handler that does not want to manipulate its body.
DY_INCLUDE或SKIP_BODY。如果doStartTag方法返回EVAL_BODY_INCLUDE,WEB容器就会接着执行自定义标签的标签体;如果doStartTag方法返回SKIP_BODY,WEB容器就会忽略自定义标签的标签体,直接解释执行自定义标签的结束标记。
(2)WEB容器解释执行到自定义标签的结束标记时,就会调用标签处理器的doEndTag方法,doEndTag方法执行完后可以向WEB容器返回常量EVAL_PAGE或SKIP_PAGE。如果doEndTag方法返回常量EVAL_PAGE,WEB容器就会接着执行JSP页面中位于结束标记后面的JSP代码;如果doEndTag方法返回SKIP_PAGE,WEB容器就会忽略JSP页面中位于结束标记后面的所有内容。
从doStartTag和doEndTag方法的作用和返回值的作用可以看出,开发自定义标签时可以在doStartTag方法和doEndTag方法体内编写合适的Java程序代码来实现具体的功能,通过控制doStartTag方法和doEndTag方法的返回值,还可以告诉WEB容器是否执行自定义标签中的标签体内容和JSP页面中位于自定义标签的结束标记后面的内容。
(3)生命周期:setPageContext(可以获得PageContext)--->setParent--->doStartTag--->doEndTag--->release
* IterationTag
* The IterationTag interface extends Tag by defining one additional method that controls the reevaluation of its body
IterationTag接口继承了Tag接口,并在Tag接口的基础上增加了一个doAfterBody方法和一个EVAL_BODY_AGAIN常量。实现IterationTag接口的标签除了可以完成Tag接口所能完成的功能外,还能够通知WEB容器是否重复执行标签体内容。对于实现了IterationTag接口的自定义标签,WEB容器在执行完自定义标签的标签体后,将调用标签处理器的doAfterBody方法,doAfterBody方法可以向WEB容器返回常量EVAL_BODY_AGAIN或SKIP_BODY。如果doAfterBody方法返回EVAL_BODY_AGAIN,WEB容器就会把标签体内容再重复执行一次,执行完后接着再调用doAfterBody方法,如此往复,直到doAfterBody方法返回常量SKIP_BODY,WEB容器才会开始处理标签的结束标记和调用doEndTag方法。
可见,开发自定义标签时,可以通过控制doAfterBody方法的返回值来告诉WEB容器是否重复执行标签体内容,从而达到循环处理标签体内容的效果。例如,可以通过一个实现IterationTag接口的标签来迭代输出一个集合中的所有元素,在标签体部分指定元素的输出格式。
在JSP API中也提供了IterationTag接口的默认实现类TagSupport,我们在编写自定义标签的标签处理器类时,可以继承和扩展TagSupport类,这相比实现IterationTag接口将简化开发工作。
* TagSupport
* BodyTag
* The BodyTag interface extends IterationTag by defining additional methods
that let a tag handler manipulate the content of evaluating its body
BodyTag接口继承了IterationTag接口,并在IterationTag接口的基础上增加了两个方法(setBodyContent、doInitBody)和一个EVAL_BODY_BUFFERED常量。实现BodyTag接口的标签除了可以完成IterationTag接口所能完成的功能,还可以对标签体内容进行修改。对于实现了BodyTag接口的自定义标签,标签处理器的doStartTag方法不仅可以返回前面讲解的常量EVAL_BODY_INCLUDE或SKIP_BODY,还可以返回常量EVAL_BODY_BUFFERED。如果doStartTag方法返回EVAL_BODY_BUFFERED,WEB容器就会创建一个专用于捕获标签体运行结果的BodyContent对象,然后调用标签处理器的setBodyContent方法将BodyContent对象的引用传递给标签处理器,WEB容器接着将标签体的执行结果写入到BodyContent对象中。在标签处理器的后续事件方法中,可以通过先前保存的BodyContent对象的引用来获取标签体的执行结果,然后调用BodyContent对象特有的方法对BodyContent对象中的内容(即标签体的执行结果)进行修改和控制其输出。
在JSP API中也提供了BodyTag接口的实现类BodyTagSupport,我们在编写能够修改标签体内容的自定义标签的标签处理器类时,可以继承和扩展BodyTagSupport类,这相比实现BodyTag接口将简化开发工作。
* 开发案例
* 自定义标签除了可以移除jsp页面java代码外,它也可以实现以下功能
* 控制jsp页面某一部分内容是否执行
* 便签类
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;
/**
* @author xiaozhao
* 控制jsp页面某一部分内容是否执行
*/
public class Tag1 extends TagSupport{
@Override
public int doStartTag() throws JspException {
return SKIP_BODY;
// return EVAL_BODY_INCLUDE;
}
}
* 在tld添加tag
<tag>
<description>控制jsp页面某一部分内容是否执行</description>
<name>tag1</name>
<tag-class>com.hx.tag.Tag1</tag-class>
<body-content>JSP</body-content>
</tag>
* 编写tag.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="hx" uri="/hx" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<hx:tag1>
小黑
</hx:tag1>
</body>
</html>
* 控制整个jsp页面是否执行
@Override
public int doEndTag() throws JspException {
return EVAL_PAGE;
// return SKIP_PAGE;
}
<hx:tag1>
小黑
</hx:tag1>
<h1>我是否还可以被看见</h1>
* 控制jsp页面内容重复执行
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;
/**
* @author xiaozhao 控制jsp页面某一部分内容是否执行
*/
public class Tag2 extends TagSupport {
int count=3;
@Override
public int doStartTag() throws JspException {
return EVAL_BODY_INCLUDE;
}
/* 控制doAfterBody()方法的返回值,
* 如果这个方法返回EVAL_BODY_AGAIN, 则web服务器又执行一次标签体,
* 依次类推,一直执行到doAfterBody方法返回SKIP_BODY,则标签体才不会重复执行。
* @see javax.servlet.jsp.tagext.TagSupport#doAfterBody()
*/
@Override
public int doAfterBody() throws JspException {
count--;
if(count>0) {
return EVAL_BODY_AGAIN;
}
return SKIP_BODY;
}
}
<tag>
<description>控制jsp页面内容重复执行</description>
<name>tag2</name>
<tag-class>com.hx.tag.Tag2</tag-class>
<body-content>JSP</body-content>
</tag>
* 修改jsp页面内容输出
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyContent;
import javax.servlet.jsp.tagext.BodyTagSupport;
public class Tag3 extends BodyTagSupport{
@Override
public int doStartTag() throws JspException {
return EVAL_BODY_BUFFERED;
}
@Override
public int doEndTag() throws JspException {
//1 获得bodyContent对象
BodyContent content = getBodyContent();
//2 获得标签体内容
String value = content.getString();
//3 转换成小写
value =value.toLowerCase();
//4 写出去
try {
pageContext.getOut().write(value);
} catch (IOException e) {
e.printStackTrace();
}
return super.doEndTag();
}
}
<tag>
<description>修改jsp页面内容输出</description>
<name>tag3</name>
<tag-class>com.hx.tag.Tag3</tag-class>
<body-content>JSP</body-content>
</tag>
<hx:tag3>HelloWolrd</hx:tag3>
* 在现在的jsp标签开发中,很少直接使用传统标签来开发了,目前用得较多的都是简单标签,所以Jsp的传统标签开发了解一下即可。
SimpleTag接口
SimpleTag接口是JSP2.0中新增的一个标签接口。由于传统标签使用三个标签接口来完成不同的功能,显得过于繁琐,不利于标签技术的推广,因此,SUN公司为降低标签技术的学习难度,在JSP 2.0中定义了一个更为简单、便于编写和调用的SimpleTag接口。SimpleTag接口与传统标签接口最大的区别在于,SimpleTag接口只定义了一个用于处理标签逻辑的doTag方法,该方法在WEB容器执行自定义标签时调用,并且只被调用一次。那些使用传统标签接口所完成的功能,例如是否执行标签体、迭代标签体、对标签体内容进行修改等功能都可以在doTag方法中完成。
在JSP API中也提供了SimpleTag接口的实现类SimpleTagSupport,我们在编写简单标签时,可以继承和扩展SimpleTagSupport类,这相比实现SimpleTag接口将简化开发工作。
* invoke方法
* Executes the fragment and directs all output to the given Writer,
or the JspWriter returned by the getOut() method of the JspContext
associated with the fragment if out is null.
<!--
tld文件中有四种标签体类型 :empty JSP scriptless tagdepentend
在简单标签(SampleTag)中标签体body-content的值只允许是empty和scriptless,不允许设置成JSP,如果设置成JSP就会出现异常
在传统标签中标签体body-content的值只允许是empty和JSP
如果标签体body-content的值设置成tagdepentend,那么就表示标签体里面的内容是给标签处理器类使用的,
例如:开发一个查询用户的sql标签,此时标签体重的SQL语句就是给SQL标签的标签处理器来使用的
<hx:sql>select * from user</hx:sql>
在这种情况下,sql标签的<body-content>就要设置成tagdepentend,tagdepentend用得比较少,了解一下即可
-->
* 开发案例(
* 控制jsp页面某一部分内容是否执行
* 写个类继承SimpleTagSupport,重写doTag方法
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;
/**
* @author xiaozhao
* 控制jsp页面某一部分内容是否执行
*/
public class SimpleTag1 extends SimpleTagSupport{
@Override
public void doTag() throws JspException, IOException {
JspFragment jspFragment = getJspBody();
jspFragment.invoke(null);// 参数为null,代表拿到JspWriter.getOut(会输出到浏览器)
}
}
<tag>
<description>控制jsp页面某一部分内容是否执行</description>
<name>stag1</name>
<tag-class>com.hx.tag.SimpleTag1</tag-class>
<body-content>scriptless</body-content>
</tag>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="hx" uri="/hx" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<hx:stag1>
你好
</hx:stag1>
</body>
</html>
/**
* @author xiaozhao
* 控制jsp页面某一部分内容是否执行
*/
public class SimpleTag2 extends SimpleTagSupport{
@Override
public void doTag() throws JspException, IOException {
throw new SkipPageException();
}
}
* 控制jsp页面内容重复执行
* 写个类继承SimpleTagSupport,重写doTag方法
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.SkipPageException;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;
/**
* @author xiaozhao
*
*/
public class SimpleTag3 extends SimpleTagSupport{
@Override
public void doTag() throws JspException, IOException {
int count=3;
JspFragment jspFragment=getJspBody();
for (int i = 0; i < count; i++) {
jspFragment.invoke(null);// 有多少次循环就调用多少次invoke方法
}
}
}
</tag>
<tag>
<description>控制jsp页面内容重复执行 </description>
<name>stag3</name>
<tag-class>com.hx.tag.SimpleTag3</tag-class>
<body-content>scriptless</body-content>
</tag>
<h1>大家好才是真的好</h1>
<hx:stag2></hx:stag2>
<hx:stag1>
<h1>你好</h1>
</hx:stag1>
<hx:stag3>
<h2 style="color: red">小黑</h2>
</hx:stag3>
* 修改jsp页面内容输出
* 写个类继承SimpleTagSupport,重写doTag方法
import java.io.IOException;
import java.io.StringWriter;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.SkipPageException;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;
/**
* @author xiaozhao
*
*/
public class SimpleTag4 extends SimpleTagSupport{
@Override
public void doTag() throws JspException, IOException {
JspFragment jspFragment = getJspBody();
StringWriter sw=new StringWriter();
jspFragment.invoke(sw);
String value = sw.getBuffer().toString();
value = value.toLowerCase();
jspFragment.getJspContext().getOut().write(value);
}
}
<tag>
<description>修改jsp页面内容输出 </description>
<name>stag4</name>
<tag-class>com.hx.tag.SimpleTag4</tag-class>
<body-content>scriptless</body-content>
</tag>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="hx" uri="/hx" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>大家好才是真的好</h1>
<hx:stag2></hx:stag2>
<hx:stag1>
<h1>你好</h1>
</hx:stag1>
<hx:stag3>
<h2 style="color: red">小黑</h2>
</hx:stag3>
<hx:stag4><h2 style="color: blue">HelloWorld</h2></hx:stag4>
</body>
</html>
* 开发案例一
* 写个类继承SimpleTagSupport,重写doTag方法
/**
* @author xiaozhao
* 控制jsp页面某一部分内容是否执行
*/
public class SimpleTag5 extends SimpleTagSupport{
private boolean show;
public void setShow(boolean show) {
this.show=show;
}
@Override
public void doTag() throws JspException, IOException {
JspFragment jspFragment = getJspBody();
if(show)
jspFragment.invoke(null);// 参数为null,代表拿到JspWriter.getOut(会输出到浏览器)
}
}
<tag>
<description>控制jsp页面某一部分内容是否执行(升级)</description>
<name>stag5</name>
<tag-class>com.hx.tag.SimpleTag5</tag-class>
<body-content>scriptless</body-content>
<!-- 标签的属性描述 -->
<attribute>
<description>描述标签的show属性</description>
<!-- 标签的show属性 -->
<name>show</name>
<required>true</required>
<!-- rtexprvalue用来指示标签的属性值是否可以是一个表达式,
一般设置为true,true就表示允许标签的属性值可以是一个表达式-->
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</hx:stag5>
<hx:stag5 show="false">
<h2 style="color: blue">不显示</h2>
</hx:stag5>
* 标签的属性值是8种基本数据类型,那么在JSP页面在传递字符串时,JSP引擎会自动转换成相应的类型,但如果标签的属性值是复合数据类型,那么JSP引擎是无法自动转换的
/**
* @author xiaozhao
*
*/
public class SimpleTag6 extends SimpleTagSupport{
private Date date;
public void setDate(Date date) {
this.date=date;
}
@Override
public void doTag() throws JspException, IOException {
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
String format = sdf.format(date);
getJspContext().getOut().write(format);
}
}
<tag>
<description>测试属性复合类型</description>
<name>stag6</name>
<tag-class>com.hx.tag.SimpleTag6</tag-class>
<body-content>scriptless</body-content>
<!-- 标签的属性描述 -->
<attribute>
<description>描述标签的date属性</description>
<!-- 标签的show属性 -->
<name>date</name>
<required>true</required>
<!-- rtexprvalue用来指示标签的属性值是否可以是一个表达式,
一般设置为true,true就表示允许标签的属性值可以是一个表达式-->
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<hx:stag6 date="2018-08-08"></hx:stag6>
* 改
<hx:stag6 date="<%=new Date() %>"></hx:stag6>
<%
request.setAttribute("date", new Date());
%>
<hx:stag6 date="${date }"></hx:stag6>
* 自定义ForTag
/**
* @author xiaozhao
*
*/
public class ForTag 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 {
JspFragment jspFragment = getJspBody();
if (items != null) {
for (Object item : items) {
getJspContext().setAttribute(var, item);
jspFragment.invoke(null);
}
}
}
}
<tag>
<description>forTag</description>
<name>for</name>
<tag-class>com.hx.tag.ForTag</tag-class>
<body-content>scriptless</body-content>
<!-- 标签的属性描述 -->
<attribute>
<description>描述标签的items属性</description>
<!-- 标签的show属性 -->
<name>items</name>
<required>true</required>
<!-- rtexprvalue用来指示标签的属性值是否可以是一个表达式,
一般设置为true,true就表示允许标签的属性值可以是一个表达式-->
<rtexprvalue>true</rtexprvalue>
</attribute>
<!-- 标签的属性描述 -->
<attribute>
<description>描述标签的var属性</description>
<!-- 标签的show属性 -->
<name>var</name>
<required>true</required>
<!-- rtexprvalue用来指示标签的属性值是否可以是一个表达式,
一般设置为true,true就表示允许标签的属性值可以是一个表达式-->
<rtexprvalue>false</rtexprvalue>
</attribute>
</tag>
<%
List<User> users=new ArrayList<User>();
User user1=new User();
user1.setId(1);
user1.setName("xiaohei");
user1.setPsw("123");
User user2=new User();
user2.setId(2);
user2.setName("xiaobai");
user2.setPsw("456");
users.add(user1);
users.add(user2);
request.setAttribute("users", users);
%>
<table border="1px">
<hx:for items="${users }" var="user">
<tr>
<td>${user.id }</td>
<td>${user.name }</td>
<td>${user.psw }</td>
</tr>
</hx:for>
</table>
* 开发防盗链
盗链的定义
此内容不在自己服务器上,而通过技术手段,绕过别人放广告有利益的最终页,直接在自己的有广告有利益的页面上向最终用户提供此内容。 常常是一些名不见经传的小网站来盗取一些有实力的大网站的地址(比如一些音乐、图片、软件的下载地址)然后放置在自己的网站中,通过这种方法盗取大网站的空间和流量。
private String page;
public void setSite(String site) {
this.site = site;
}
public void setPage(String page) {
this.page = 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();
}
}
}
<tag>
<description>refererTag</description>
<name>referer</name>
<tag-class>com.hx.tag.RefererTag</tag-class>
<body-content>scriptless</body-content>
<!-- 标签的属性描述 -->
<attribute>
<description>描述标签的site属性</description>
<!-- 标签的show属性 -->
<name>site</name>
<required>true</required>
<!-- rtexprvalue用来指示标签的属性值是否可以是一个表达式,
一般设置为true,true就表示允许标签的属性值可以是一个表达式-->
<rtexprvalue>true</rtexprvalue>
</attribute>
<!-- 标签的属性描述 -->
<attribute>
<description>描述标签的page属性</description>
<!-- 标签的show属性 -->
<name>page</name>
<required>true</required>
<!-- rtexprvalue用来指示标签的属性值是否可以是一个表达式,
一般设置为true,true就表示允许标签的属性值可以是一个表达式-->
<rtexprvalue>false</rtexprvalue>
</attribute>
</tag>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="/hx" prefix="hx" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<hx:referer site="http://192.168.10.107:8080" page="/index.jsp"></hx:referer>
<img alt="hehe" src="${pageContext.request.contextPath }/images/mv.jpg">
</body>
</html>
* 不同服务器:访问show.jsp会跳转到主页里面
<a href="http://192.168.10.107:8080/RefererDemo/show.jsp">测试</a>