自定义分页标签详解
关于自定义分页标签的使用,我想大家都见过许多人写过,我今天也来凑个热闹写下我见到的自定义标签的使用步骤既然是自定义标签那么肯定少不了类和tld文件这两大因素,因为这两个才能构成标签
首先奉献上最核心的自定义分页标签类的写法PagerTag.java,前提是要继承自TagSupport类
package com.javacrazyer.web.tag;import java.io.IOException;import java.util.Enumeration;import javax.servlet.http.HttpServletRequest;import javax.servlet.jsp.JspException;import javax.servlet.jsp.tagext.TagSupport;/** * 分页标签处理类 */public class PagerTag extends TagSupport {private static final long serialVersionUID = 5729832874890369508L;private String url; //请求URIprivate int pageSize = 10;//每页要显示的记录数private int pageNo = 1; //当前页号private int recordCount; //总记录数@SuppressWarnings("unchecked")public int doStartTag() throws JspException {int pageCount = (recordCount + pageSize - 1) / pageSize;//计算总页数//拼写要输出到页面的HTML文本StringBuilder sb = new StringBuilder();sb.append("<style type=\"text/css\">");sb.append(".pagination {padding: 5px;float:right;font-size:12px;}");sb.append(".pagination a, .pagination a:link, .pagination a:visited {padding:2px 5px;margin:2px;border:1px solid #aaaadd;text-decoration:none;color:#006699;}");sb.append(".pagination a:hover, .pagination a:active {border: 1px solid #ff0000;color: #000;text-decoration: none;}");sb.append(".pagination span.current {padding: 2px 5px;margin: 2px;border: 1px solid #ff0000;font-weight: bold;background-color: #ff0000;color: #FFF;}");sb.append(".pagination span.disabled {padding: 2px 5px;margin: 2px;border: 1px solid #eee; color: #ddd;}");sb.append("</style>\r\n");sb.append("<div class=\"pagination\">\r\n");if(recordCount == 0){sb.append("<strong>没有可显示的项目</strong>\r\n");}else{//页号越界处理if(pageNo > pageCount){pageNo = pageCount;}if(pageNo < 1){pageNo = 1;}sb.append("<form method=\"post\" action=\"").append(this.url).append("\" name=\"qPagerForm\">\r\n");//获取请求中的所有参数HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();Enumeration<String> enumeration = request.getParameterNames();String name = null;//参数名String value = null; //参数值//把请求中的所有参数当作隐藏表单域while (enumeration.hasMoreElements()) {name =enumeration.nextElement();value = request.getParameter(name);// 去除页号if (name.equals("pageNo")) {if (null != value && !"".equals(value)) {pageNo = Integer.parseInt(value);}continue;}sb.append("<input type=\"hidden\" name=\"").append(name).append("\" value=\"").append(value).append("\"/>\r\n");}// 把当前页号设置成请求参数sb.append("<input type=\"hidden\" name=\"").append("pageNo").append("\" value=\"").append(pageNo).append("\"/>\r\n");// 输出统计数据sb.append("&nbsp;共<strong>").append(recordCount).append("</strong>项").append(",<strong>").append(pageCount).append("</strong>页:&nbsp;\r\n");//上一页处理if (pageNo == 1) {sb.append("<span class=\"disabled\">&laquo;&nbsp;上一页").append("</span>\r\n");} else {sb.append("<a href=\"javascript:turnOverPage(").append((pageNo - 1)).append(")\">&laquo;&nbsp;上一页</a>\r\n");}//如果前面页数过多,显示"..."int start = 1; if(this.pageNo > 4){start = this.pageNo - 1;sb.append("<a href=\"javascript:turnOverPage(1)\">1</a>\r\n");sb.append("<a href=\"javascript:turnOverPage(2)\">2</a>\r\n");sb.append("&hellip;\r\n");}//显示当前页附近的页int end = this.pageNo + 1;if(end > pageCount){end = pageCount;}for(int i = start; i <= end; i++){if(pageNo == i){ //当前页号不需要超链接sb.append("<span class=\"current\">").append(i).append("</span>\r\n");}else{sb.append("<a href=\"javascript:turnOverPage(").append(i).append(")\">").append(i).append("</a>\r\n");}}//如果后面页数过多,显示"..."if(end < pageCount - 2){sb.append("&hellip;\r\n");}if(end < pageCount - 1){sb.append("<a href=\"javascript:turnOverPage(").append(pageCount - 1).append(")\">").append(pageCount - 1).append("</a>\r\n");}if(end < pageCount){sb.append("<a href=\"javascript:turnOverPage(").append(pageCount).append(")\">").append(pageCount).append("</a>\r\n"); }//下一页处理if (pageNo == pageCount) {sb.append("<span class=\"disabled\">下一页&nbsp;&raquo;").append("</span>\r\n");} else {sb.append("<a href=\"javascript:turnOverPage(").append((pageNo + 1)).append(")\">下一页&nbsp;&raquo;</a>\r\n");}sb.append("</form>\r\n");// 生成提交表单的JSsb.append("<script language=\"javascript\">\r\n");sb.append("function turnOverPage(no){\r\n");sb.append(" if(no>").append(pageCount).append("){");sb.append(" no=").append(pageCount).append(";}\r\n");sb.append(" if(no<1){no=1;}\r\n");sb.append(" document.qPagerForm.pageNo.value=no;\r\n");sb.append(" document.qPagerForm.submit();\r\n");sb.append("}\r\n");sb.append("</script>\r\n");}sb.append("</div>\r\n");//把生成的HTML输出到响应中try {pageContext.getOut().println(sb.toString());} catch (IOException e) {throw new JspException(e);}return SKIP_BODY;//本标签主体为空,所以直接跳过主体}public void setUrl(String url) {this.url = url;}public void setPageSize(int pageSize) {this.pageSize = pageSize;}public void setPageNo(int pageNo) {this.pageNo = pageNo;}public void setRecordCount(int recordCount) {this.recordCount = recordCount;}} WEB-INF/pager.tld的写法
<?xml version="1.0" encoding="UTF-8"?><taglib version="2.0" 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 web-jsptaglibrary_2_0.xsd"><tlib-version>0.9</tlib-version> <!--标签头--><short-name>w</short-name> <!--将来在页面用taglib引用时的uri属性,这部分的名字可以随便写,只要是符合HTTP网址形式的--><uri>http://javacrazyer.iteye.com/tags/pager</uri><!-- 自定义标签的描述信息 --><tag><!-- 标签名 --><name>pager</name><!-- 对应的标签处理类全限定名 --><tag-class>com.javacrazyer.web.tag.PagerTag</tag-class><!-- 标签主体的类型 --><body-content>empty</body-content><!-- 当前页号属性的描述信息 --><attribute><!-- 属性名 --><name>pageNo</name><!-- 该属性是否为必要的 --><required>true</required><!-- 属性值是否可以在JSP运行时期动态产生 --><rtexprvalue>true</rtexprvalue><!-- 属性的数据类型 --><type>int</type></attribute><!-- 总记录数属性的描述信息 --><attribute><name>recordCount</name><required>true</required><rtexprvalue>true</rtexprvalue><type>int</type></attribute><!-- 总页数属性的描述信息 --><attribute><name>pageSize</name><required>true</required><rtexprvalue>true</rtexprvalue><type>int</type></attribute><!-- 分页标签要跳转的URI属性的描述信息 --><attribute><name>url</name><required>true</required><rtexprvalue>true</rtexprvalue><type>java.lang.String</type></attribute></tag></taglib>
好了,就上面的两个基本要素就已经构成了完整的分页标签,下面就差在页面的使用方式了
一般的使用步骤为在JSP页面中:
先倒入标签库:<%@taglib uri="http://javacrazyer.iteye.com/tags/pager" prefix="w"%>
然后使用: <w:pager pageSize="${pageSize}" pageNo="${pageNo}" url="do.jsp" recordCount="${pm.recordCount}"/>
具体使用示例,上面两个类保持不变
这个例子可以说非常好的纯servlet项目的例子,大家今后编程如果没有用到任何框架的话,我希望这个例子能给你带来点启示
前提是导入所有需要的jar包:包括common-dbcp.jar(数据源需要用到),common-dbutils-1.2.jar(数据库CURD操作需要用到,可取代最原始的JDBC操作),junit.jar(测试用到)以及数据库驱动包
关于上面的common-dbutils非常有用,尤其是在非框架项目中,后面我提供下载
http://dl.iteye.com/upload/attachment/350626/316a8749-ed67-3993-b4f7-b3fcfdc373c1.png
首先数据库,这个数据库脚本是我用navicat导出来的
/*Navicat MySQL Data TransferSource Host : localhost:3306Source Database : jstlTarget Host : localhost:3306Target Database : jstlDate: 2010-11-18 14:30:30*/SET FOREIGN_KEY_CHECKS=0;-- ------------------------------ Table structure for news-- ----------------------------DROP TABLE IF EXISTS `news`;CREATE TABLE `news` (`detail` varchar(255) DEFAULT NULL,`name` varchar(255) DEFAULT NULL,`id` int(11) NOT NULL AUTO_INCREMENT,PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1253 DEFAULT CHARSET=utf8;
实体类News.java
package com.javacrazyer.domain;public class News {private int id;private String name;private String detail;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getDetail() {return detail;}public void setDetail(String detail) {this.detail = detail;}@Overridepublic String toString() {return "News ";}} NewsDao.java
package com.javacrazyer.dao;import com.javacrazyer.common.PageModel;import com.javacrazyer.domain.News;public interface NewsDao {PageModel<News> findByPager(int pageNo, int pageSize);void createNews(News news);void update(News news);void delete(int id);News findbyId(int id);int findTotalSize();}NewsImplDao.java
package com.javacrazyer.daoimpl;import java.sql.Connection;import java.sql.SQLException;import java.util.List;import org.apache.commons.dbutils.DbUtils;import org.apache.commons.dbutils.QueryRunner;import org.apache.commons.dbutils.handlers.BeanHandler;import org.apache.commons.dbutils.handlers.BeanListHandler;import org.apache.commons.dbutils.handlers.ScalarHandler;import com.javacrazyer.common.ConnectionFactory;import com.javacrazyer.common.DAOException;import com.javacrazyer.common.PageModel;import com.javacrazyer.dao.NewsDao;import com.javacrazyer.domain.News;public class NewsDaoImpl implements NewsDao {private QueryRunner qr = new QueryRunner();public void createNews(News news) {Connection conn = null;String sql = "insert into news(name,detail) "+ " values(?,?)";Object[] param = { news.getName(),news.getDetail() };try {conn = ConnectionFactory.getConn();qr.update(conn, sql, param);} catch (SQLException e) {e.printStackTrace();throw new DAOException("新增新闻信息时出现异常", e);} finally {DbUtils.closeQuietly(conn);}}public PageModel<News> findByPager(int pageNo, int pageSize) {PageModel<News> pm=new PageModel<News>();Connection conn=null;String sql="select *from news limit ?,?";Object[] param={(pageNo-1)*pageSize,pageSize};List<News> cates=null;int count;try {conn=ConnectionFactory.getConn();cates=(List<News>)qr.query(conn, sql, new BeanListHandler(News.class), param);pm.setDatas(cates);pm.setRecordCount(findTotalSize());} catch (SQLException e) {e.printStackTrace();throw new DAOException("分页查询出错",e);}finally{DbUtils.closeQuietly(conn);}return pm;}public void delete(int id) {Connection conn = null;String sql = "delete from news where id=?";Object[] param = { id };try {conn = ConnectionFactory.getConn();qr.update(conn, sql, param);} catch (SQLException e) {e.printStackTrace();throw new DAOException("删除新闻信息时出现异常", e);} finally {DbUtils.closeQuietly(conn);}}public News findbyId(int id) {News news=null;Connection conn=null;String sql="select * from news where id=?";Object[] param={id};try {conn=ConnectionFactory.getConn();news=(News)qr.query(conn,sql, new BeanHandler(News.class), param);} catch (SQLException e) {e.printStackTrace();throw new DAOException("根据ID查询新闻信息时出现异常",e);}finally{DbUtils.closeQuietly(conn);}return news;}public void update(News news) {Connection conn=null;String sql="update news set name=?,detail=? where id=?";Object[] param={news.getName(),news.getDetail(),news.getId()};try {conn=ConnectionFactory.getConn();qr.update(conn, sql, param);} catch (SQLException e) {e.printStackTrace();throw new DAOException("更新新闻信息出错",e);}finally{DbUtils.closeQuietly(conn);}}public int findTotalSize() {Connection conn=null;String sql="select count(id) from news";int count=0;try {conn=ConnectionFactory.getConn();count=((Long)qr.query(conn,sql,new ScalarHandler())).intValue();} catch (SQLException e) {e.printStackTrace();throw new DAOException("查询记录总数出错",e);}finally{DbUtils.closeQuietly(conn);}return count;}} PageModel.java
package com.javacrazyer.common;import java.util.List;/*** 分页组件(包含当前页结果数据列表和总记录数)* 注意,它不是持久化实体类* */public class PageModel<T> { private int recordCount; private List<T> datas; public int getRecordCount() { return recordCount; } public void setRecordCount(int recordCount) { this.recordCount = recordCount; } public List<T> getDatas() { return datas; } public void setDatas(List<T> datas) { this.datas = datas; }}
数据库连接工具类
package com.javacrazyer.common;import java.io.IOException;import java.sql.Connection;import java.sql.SQLException;import java.util.Properties;import javax.sql.DataSource;import org.apache.commons.dbcp.BasicDataSource;/** ** 数据库连接工厂类 **/public class ConnectionFactory { private static DataSource dss=null;static{Properties pr=new Properties();try {pr.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("config.properties"));} catch (IOException e) {e.printStackTrace();}BasicDataSource ds=new BasicDataSource();ds.setDriverClassName(pr.getProperty("driver_name"));ds.setUrl(pr.getProperty("url"));ds.setUsername(pr.getProperty("username"));ds.setPassword(pr.getProperty("password"));dss=ds;}private ConnectionFactory(){}public static Connection getConn() throws SQLException{return dss.getConnection();}} 上面代码用到的config.properties
#mysqldriver_name=com.mysql.jdbc.Driverurl=jdbc:mysql:///jstlusername=rootpassword=rootDAO实例工具类,类似于Spring的BeanFactory
package com.javacrazyer.common;import java.io.IOException;import java.util.Properties;/** ***自定义简单工厂 */public class DAOFactory {public static Properties pr = new Properties();static {try {pr.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("daoname.properties"));} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}public static Object newInstance(String name){ Object obj=null;String daoImplName = pr.getProperty(name);if(null!=daoImplName){try {obj=Class.forName(daoImplName).newInstance();} catch (InstantiationException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}}else{throw new RuntimeException("指定名称的DAO类未找到");}return obj;}} 上面代码用到的配置daoname.properties
newsdao=com.javacrazyer.daoimpl.NewsDaoImplcoursedao=com.javacrazyer.web.tag.CourseDAOImplteacherdao=com.javacrazyer.web.tag.TeacherDAOImplcatedao=com.javacrazyer.web.tag.CategoryDAOImplgradao=com.javacrazyer.web.tag.GraduateDAOImplaccountdao=com.javacrazyer.web.tag.AccountDAOImpl 平时开发中常用到的常量存放类
package com.javacrazyer.common;import java.util.LinkedHashMap;import java.util.Map;/** * 系统常量类 ** @author tjitcast.com */public class Constant {/** 状态:可用 */public static final int STATUS_ACTIVE = 1;/** 状态:不可用 */public static final int STATUS_INACTIVE = 0;/** 课程类型:全日制 */public static final int COURSE_FULL_TIME = 1;/** 课程类型:业务 */public static final int COURSE_SPARE_TIME = 2;/** 课程类型:免费 */public static final int COURSE_FREE_TIME = 3;/** 账号类型:超级管理员 */public static final int ACCOUNT_SUPER = 100;/** 账号类型:普通管理员 */public static final int ACCOUNT_COMMON = 50;/** 账号状态:激活 */public static final int ACCOUNT_STATUS_ACTIVE = 1;/** 账号状态:未激活 */public static final int ACCOUNT_STATUS_INACTIVE = 0;/** 账号状态:锁定 */public static final int ACCOUNT_STATUS_LOCK = -1;/** 每页要显示的记录数:10 */public static final int PAGE_SIZE =10;private static Map<Integer, String> statusMap = new LinkedHashMap<Integer, String>();static {// 对状态Map进行初始化statusMap.put(Integer.valueOf(STATUS_ACTIVE), "可用");statusMap.put(Integer.valueOf(STATUS_INACTIVE), "不可用");}public static Map<Integer, String> getStatusMap() {return statusMap;}}
开发中常用到的用来判断空值,类型转换,集合操作等等的自定义常用工具类
/** *ClassName: DataValidateUtil.java *created on Jul 10, 2009 *Copyrights 2009 www.tjicast.com All rights reserved. *site: http://www.tjitcast.com *email: tjhr@csdn.net *phone: 022-83726777,89721888 */package com.javacrazyer.common;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Collection;import java.util.Date;import java.util.Map;/** * 对一些常用数据进行操作的工具类 **/public class DataOptUtil {/** 日期长格式 */public static final String DATE_PATTERN_LONG = "yyyy-MM-dd HH:mm:ss";/** 日期格式 */public static final String DATE_PATTERN = "yyyy-MM-dd";public static boolean isNotNull(String str){if(null != str && !"".equals(str)){return true;}else{return false;}}public static int parseInt(String str){if(isNotNull(str)){return Integer.parseInt(str);}else{throw new RuntimeException("字符串为空,不能转换成数字");}}public static Date parseDate(String str, String pattern){SimpleDateFormat sdf = new SimpleDateFormat(pattern);Date date = null;if(isNotNull(str)){try {date = sdf.parse(str);} catch (ParseException e) {e.printStackTrace();}}else{throw new RuntimeException("字符串为空,不能转换成日期");}return date;}public static Date parseDate(String str){return parseDate(str, DataOptUtil.DATE_PATTERN);}public static Date parseLongDate(String str){return parseDate(str, DataOptUtil.DATE_PATTERN_LONG);}public static String date2String(Date date, String pattern){SimpleDateFormat sdf = new SimpleDateFormat(pattern);return sdf.format(date);}public static String Date2String(Date date){return date2String(date, DataOptUtil.DATE_PATTERN);}public static String Date2LongString(Date date){return date2String(date, DataOptUtil.DATE_PATTERN_LONG);}public static int getSize(Collection<?> coll){int size = coll == null ? 0 : coll.size();return size;}public static int getSize(Map<?,?> map){int size = map == null ? 0 : map.size();return size;}public static int getLength(Object[] obj){int length = 0;length = obj == null ? 0 : obj.length;return length;}}
还有一个自定义非受检异常类
package com.javacrazyer.common;/** * * 自定义的非受检异常 **/public class DAOException extends RuntimeException {private static final long serialVersionUID = 1047748781772098415L;public DAOException() {super();}public DAOException(String message, Throwable cause) {super(message, cause);}public DAOException(String message) {super(message);}public DAOException(Throwable cause) {super(cause);}}
页面方面
index.html
<html><head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta http-equiv="refresh" content="0; url=do.jsp" /></head></html>
do.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><%@page import="com.javacrazyer.common.DAOFactory"%><%@page import="com.javacrazyer.dao.NewsDao"%><%@page import="com.javacrazyer.common.PageModel"%><%@page import="com.javacrazyer.common.DataOptUtil"%><%@page import="com.javacrazyer.common.Constant"%><%@page import="com.javacrazyer.domain.News"%><%NewsDao dao=(NewsDao)DAOFactory.newInstance("newsdao");int pageNo = 1;String temp = request.getParameter("pageNo");if (DataOptUtil.isNotNull(temp)) {pageNo = Integer.parseInt(temp);}int categoryid = 1;String temp1 = request.getParameter("category_id");if (DataOptUtil.isNotNull(temp1)) {categoryid = Integer.parseInt(temp1);}PageModel<News> pm=dao.findByPager(pageNo,Constant.PAGE_SIZE);request.setAttribute("pm",pm);request.setAttribute("pageNo", Integer.valueOf(pageNo));request.setAttribute("pageSize", Constant.PAGE_SIZE);request.getRequestDispatcher("/index.jsp").forward(request,response);%> index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><%@taglib uri="http://javacrazyer.iteye.com/tags/pager" prefix="w"%><%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %><%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> <h3>新闻列表</h3><hr/> <table> <tr><td>新闻编号</td><td>新闻标题</td><td>新闻内容</td></tr> <c:forEach items="${pm.datas}" var="news"> <tr><td>${news.id}</td><td>${news.name }</td><td>${news.detail}</td></tr> </c:forEach> </table> <w:pager pageSize="${pageSize}" pageNo="${pageNo}" url="do.jsp" recordCount="${pm.recordCount}"/></body></html> 实际运行出来的效果
http://dl.iteye.com/upload/attachment/350629/6522aa48-c116-34a9-882e-f7afd281799d.png
虽然自定义标签使用成功了,但总不可能每次开发项目都写下这个类和加上TLD文件吧,比较简洁的方式就是将编译类编译好的class文件和tld一起打成jar包,以后直接导入到项目lib中就可以使用了
具体步骤:将整个目录com/javacrazyer/web/tag/PagerTag.class放到一个目录中去,同时将META-INF拷贝到与com目录相同的目录下,最后将WEB-INF下的pager.tld拷贝到MET-INF下
http://dl.iteye.com/upload/attachment/350634/0d130979-8be3-3481-91f9-597b590dcde4.png
META-INF中的文件
http://dl.iteye.com/upload/attachment/350638/173f8074-1f02-3484-ba21-65d09e602fa2.png
最后将com文件夹与META-INF文件选中右键添加到压缩文件,选中ZIP压缩格式
http://dl.iteye.com/upload/attachment/350643/22ccad9f-8d89-358a-89bc-7d35a673dceb.png
改名为jar后缀后确定,这样在同一目录就会多出一个jar来了
http://dl.iteye.com/upload/attachment/350646/b02b2fac-948d-37f4-9943-c453e4ed6fc1.png
以后只要在需要的地方导入该分页JAR包,并且在网页上
<%@taglib uri="http://javacrazyer.iteye.com/tags/pager" prefix="w"%>
<w:pager pageSize="${pageSize}" pageNo="${pageNo}" url="do.jsp"
页:
[1]