您的位置:首页 > 其它

对Guava Cache的封装和使用(包括一个管理页面实现了查看统计信息、情况、查看记录等)

2017-08-04 00:18 921 查看
非常非常感谢http://blog.csdn.net/clementad/article/details/46491701

由于项目的实际情况,需要缓存一些比较不经常改动的数据在本地服务器中,以提高接口处理的速度。决定采用Guava Cache之后,整理了一些具体需求:

由于要缓存的key-value对比较多,需要一个封装好的类被继承,子类可以简单的实现把key-value缓存到Guava Cache中;
需要定义一个接口,简单的定义一个get(K key)方法,方便使用;
需要有一个管理界面,统计缓存的命中率、记录数,以便以后做出相应的调整;
需要有一个管理界面,重设、清空缓存中的数据,或使缓存中的数据失效,以强行让服务器重新从数据库获取数据,并记录重置的时间;
需要有一个管理界面,分页查看缓存中的具体内容。

现在,该系统已经实现,并已经在正式环境中运行了一段时间,日均总命中次数超过一百万,大部分缓存的命中率在98%以上,为某些接口的请求节省了一半的时间。
Guava Cache简介:

Guava Cache提供了一种把数据(key-value对)缓存到本地(JVM)内存中的机制,适用于很少会改动的数据,比如地区信息、系统配置、字典数据,等。Guava Cache与ConcurrentMap很相似,但也不完全一样。最基本的区别是ConcurrentMap会一直保存所有添加的元素,直到显式地移除。相对地,Guava
Cache为了限制内存占用,通常都设定为自动回收元素。
本文介绍了一种对Guava LoadingCache的封装使用,并提供管理页面的实现。
首先,介绍一些Guava Cache的基本概念:

Guava提供两种不同的方法来加载数据:

CacheLoader:在build cache的时候定义一个CacheLoader来获取数据,适用的情况:有固定的方式可以根据key来加载或计算value的值,比如从数据库中获取数据
Callable:在get的时候传入一个Callable对象,适用的情况:如果从缓存中获取不到数据,则另外计算一个出来,并把计算结果加入到缓存中

另外,还可以使用cache.put(key, value)方法直接向缓存中插入值,但不推荐使用,因为这样会多了一步操作。

缓存回收方式:

1、基于容量的回收(size-based eviction),有两种方式,接近最大的size或weight时回收:

基于maximumSize(long):一个数据项占用一个size单位,适用于value是固定大小的情况
基于maximumWeight(long):对不同的数据项计算weight,适用于value不定大小的情况,比如value为Map类型时,可以把map.size()作为weight

2、定时回收(Timed Eviction)

expireAfterAccess(long, TimeUnit):缓存项在给定时间内没有被读/写,则回收。
expireAfterWrite(long, TimeUnit):缓存项在给定时间内没有被写访问(创建或覆盖),则回收。

3、基于引用的回收(Reference-based Eviction),通过使用弱引用的键或值、或软引用的值,把缓存设置为允许垃圾回收器回收:

CacheBuilder.weakKeys():使用弱引用存储键。当键没有其它(强或软)引用时,缓存项可以被GC回收
CacheBuilder.weakValues():使用弱引用存储值。当值没有其它(强或软)引用时,缓存项可以被GC回收
CacheBuilder.softValues():使用软引用存储值。软引用只有在响应内存需要时,才按照全局最近最少使用的顺序回收。影响性能,不推荐使用。

4、显式清除(invalidate)

个别清除:Cache.invalidate(key)
批量清除:Cache.invalidateAll(keys)
清除所有缓存项:Cache.invalidateAll()

什么时候发生缓存清理:

使用CacheBuilder构建的缓存不会"自动"执行清理和回收工作,也不会在某个缓存项过期后马上清理,也没有诸如此类的清理机制。它是在写操作时顺带做少量的维护工作(清理);如果写操作太少,读操作的时候也会进行少量维护工作。

基于容量的回收原则:

基本原则是LRU,但是按照每个Segment来清除的。比如:

一个maximumSize为100的Cache,concurrencyLevel=4,则如果开始清除缓存时,那些segment中size>25的会被优先清除掉只剩下25个。

移除监听器(Removal Listener):

通过CacheBuilder.removalListener(RemovalListener),可以声明一个监听器,以便缓存项被移除时做一些额外操作,RemovalListener会获取移除通知[RemovalNotification],里面包含移除原因[RemovalCause]、键和值。

注:耗性能。可以使用RemovalListeners.asynchronous(RemovalListener, Executor)定义监听器为异步操作。

统计功能:

CacheBuilder.recordStats()用来开启Guava Cache的统计功能。统计打开后,Cache.stats()方法会返回CacheStats对象以提供一些统计信息。具体信息可以查看CacheStats类的定义。

asMap视图:

asMap视图提供了缓存的ConcurrentMap形式,但它的get方法不会往缓存中加入数据,实质上等同于cache.getIfPresent(key)。可以用它来遍历查看缓存中的所有数据。

下面介绍对Guava Cache进行封装使用的具体方法:

使用的设计模式:策略模式(Strategy)

关于这种设计模式,具体请参考:http://zz563143188.iteye.com/blog/1847029 (第13种)
这种设计模式的关系图如下:



总策略:利用Guava Cache来存放我们自己的数据。

图中3+1种角色和代码的对应关系如下:

Context:Service.java,(策略的使用者)
Strategy:ILocalCache.java,(定义策略的接口)
ConcreteStrategy:LCAreaIdToArea.java,其他ILocalCache实现类 ……,(实现策略的类)
辅助类:GuavaAbstractLoadingCache.java,(实现策略的辅助类,就是封装了Guava Cache的一个类)

各角色对应的具体功能如下:

Service:cache的使用者
Cache接口:定义一个get()方法,通过key获取value

Cache实现类:利用辅助类,实现Cache接口的get()方法

Guava Cache实现辅助类:封装了对Guava Cache的利用,包括cache的创建、从数据源获取数据、定义过时策略、等

除了上面的核心功能模块,其他辅助的功能模块如下:

Cache管理类:封装了对所有实现类进行管理的一些方法,包括清空Cache中的数据、查看数据、查看统计信息、等

Cache Controller:调用Cache管理类,为管理页面提供Web HTTP接口

Cache管理页面:Web页面,用于查看Cache列表、统计信息、数据,清空缓存,等

各模块的具体代码,代码中已经包括了比较详尽的注解:
主要的依赖包:

[html] view
plain copy

<dependency>

<groupId>com.google.guava</groupId>

<artifactId>guava</artifactId>

<version>18.0</version>

</dependency>

Cache接口:定义一个get()方法,通过key获取value

[java] view
plain copy

/**

* 本地缓存接口

* @author XuJijun

*

* @param <K> Key的类型

* @param <V> Value的类型

*/

public interface ILocalCache <K, V> {

/**

* 从缓存中获取数据

* @param key

* @return value

*/

public V get(K key);

}

Guava Cache实现辅助类:封装了对Guava Cache的利用,包括cache的创建、从数据源获取数据、定义过时策略、等

[java] view
plain copy

package com.xjj.cache.guava;

/**

* 抽象Guava缓存类、缓存模板。

* 子类需要实现fetchData(key),从数据库或其他数据源(如Redis)中获取数据。

* 子类调用getValue(key)方法,从缓存中获取数据,并处理不同的异常,比如value为null时的InvalidCacheLoadException异常。

*

* @author XuJijun

* @Date 2015-05-18

*

* @param <K> key 类型

* @param <V> value 类型

*/

public abstract class GuavaAbstractLoadingCache <K, V> {

protected final Logger logger = LoggerFactory.getLogger(this.getClass());

//用于初始化cache的参数及其缺省值

private int maximumSize = 1000; //最大缓存条数,子类在构造方法中调用setMaximumSize(int size)来更改

private int expireAfterWriteDuration = 60; //数据存在时长,子类在构造方法中调用setExpireAfterWriteDuration(int duration)来更改

private TimeUnit timeUnit = TimeUnit.MINUTES; //时间单位(分钟)

private Date resetTime; //Cache初始化或被重置的时间

private long highestSize=0; //历史最高记录数

private Date highestTime; //创造历史记录的时间

private LoadingCache<K, V> cache;

/**

* 通过调用getCache().get(key)来获取数据

* @return cache

*/

public LoadingCache<K, V> getCache() {

if(cache == null){ //使用双重校验锁保证只有一个cache实例

synchronized (this) {

if(cache == null){

cache = CacheBuilder.newBuilder().maximumSize(maximumSize) //缓存数据的最大条目,也可以使用.maximumWeight(weight)代替

.expireAfterWrite(expireAfterWriteDuration, timeUnit) //数据被创建多久后被移除

.recordStats() //启用统计

.build(new CacheLoader<K, V>() {

@Override

public V load(K key) throws Exception {

return fetchData(key);

}

});

this.resetTime = new Date();

this.highestTime = new Date();

logger.debug("本地缓存{}初始化成功", this.getClass().getSimpleName());

}

}

}

return cache;

}

/**

* 根据key从数据库或其他数据源中获取一个value,并被自动保存到缓存中。

* @param key

* @return value,连同key一起被加载到缓存中的。

*/

protected abstract V fetchData(K key);

/**

* 从缓存中获取数据(第一次自动调用fetchData从外部获取数据),并处理异常

* @param key

* @return Value

* @throws ExecutionException

*/

protected V getValue(K key) throws ExecutionException {

V result = getCache().get(key);

if(getCache().size() > highestSize){

highestSize = getCache().size();

highestTime = new Date();

}

return result;

}

public long getHighestSize() {

return highestSize;

}

public Date getHighestTime() {

return highestTime;

}

public Date getResetTime() {

return resetTime;

}

public void setResetTime(Date resetTime) {

this.resetTime = resetTime;

}

public int getMaximumSize() {

return maximumSize;

}

public int getExpireAfterWriteDuration() {

return expireAfterWriteDuration;

}

/**

* 设置最大缓存条数

* @param maximumSize

*/

public void setMaximumSize(int maximumSize) {

this.maximumSize = maximumSize;

}

/**

* 设置数据存在时长(分钟)

* @param expireAfterWriteDuration

*/

public void setExpireAfterWriteDuration(int expireAfterWriteDuration) {

this.expireAfterWriteDuration = expireAfterWriteDuration;

}

}

Cache实现类:利用辅助类,实现Cache接口的get()方法,(以一个areaId -> Area为例子)

[java] view
plain copy

package com.xjj.entity;

public class Area {

private int id;

private int parentCode;

private String name;

private int code;

private String pinyin;

private int type;

public char getFirstLetter(){

return pinyin.charAt(0);

}

//省略其他getter和setter

}

package com.xjj.cache.local.impl;

/**

* 本地缓存:areaId -> Area

* @author XuJijun

*

*/

@Component

public class LCAreaIdToArea extends GuavaAbstractLoadingCache<Integer, Area> implements ILocalCache<Integer, Area> {

//@Autowired

//private AreasDAO areasDAO;

//由Spring来维持单例模式

private LCAreaIdToArea(){

setMaximumSize(3000); //最大缓存条数

}

@Override

public Area get(Integer key) {

try {

return getValue(key);

} catch (Exception e) {

logger.error("无法根据areaId={}获取Area,可能是数据库中无该记录。", key ,e);

return null;

}

}

/**

* 从数据库中获取数据

*/

@Override

protected Area fetchData(Integer key) {

logger.debug("测试:正在从数据库中获取area,area id={}", key);

//return areasDAO.getAreaById(key);

//测试专用,实际项目使用areaDao从数据库中获取数据

Area a = new Area();

a.setCode(key);

a.setId(key);

a.setName("地区:"+key);

a.setParentCode(Integer.valueOf(key.toString().substring(0, key.toString().length()-3)));

a.setPinyin("pinyin:"+key);

a.setType(AreaType.CITY.getValue());

return a;

}

}

Service:cache的使用者

[java] view
plain copy

/**

* Area相关方法,使用缓存

* @author XuJijun

*

*/

@Service

public class AreaService implements IAreaService {

@Resource(name="LCAreaIdToArea")

ILocalCache<Integer, Area> lCAreaIdToArea;

/**

* 根据areaId获取Area

* @param areaId

* @return Area

*/

@Override

public Area getAreaById(int areaId) {

return lCAreaIdToArea.get(areaId);

}

}

Cache管理类:封装了对所有实现类进行管理的一些方法,包括清空Cache中的数据、查看数据、查看统计信息、等

代码中所涉及到的其他类(SpringContextUtil、PageParams、PageResult)请参考源代码:https://github.com/xujijun/MyJavaStudio

[java] view
plain copy

package com.xjj.cache.guava;

/**

* Guava缓存监视和管理工具

* @author XuJijun

*

*/

public class GuavaCacheManager {

//保存一个Map: cacheName -> cache Object,以便根据cacheName获取Guava cache对象

private static Map<String, ? extends GuavaAbstractLoadingCache<Object, Object>> cacheNameToObjectMap = null;

/**

* 获取所有GuavaAbstractLoadingCache子类的实例,即所有的Guava Cache对象

* @return

*/

@SuppressWarnings("unchecked")

private static Map<String, ? extends GuavaAbstractLoadingCache<Object, Object>> getCacheMap(){

if(cacheNameToObjectMap==null){

cacheNameToObjectMap = (Map<String, ? extends GuavaAbstractLoadingCache<Object, Object>>) SpringContextUtil.getBeanOfType(GuavaAbstractLoadingCache.class);

}

return cacheNameToObjectMap;

}

/**

* 根据cacheName获取cache对象

* @param cacheName

* @return

*/

private static GuavaAbstractLoadingCache<Object, Object> getCacheByName(String cacheName){

return (GuavaAbstractLoadingCache<Object, Object>) getCacheMap().get(cacheName);

}

/**

* 获取所有缓存的名字(即缓存实现类的名称)

* @return

*/

public static Set<String> getCacheNames() {

return getCacheMap().keySet();

}

/**

* 返回所有缓存的统计数据

* @return List<Map<统计指标,统计数据>>

*/

public static ArrayList<Map<String, Object>> getAllCacheStats() {

Map<String, ? extends Object> cacheMap = getCacheMap();

List<String> cacheNameList = new ArrayList<>(cacheMap.keySet());

Collections.sort(cacheNameList);//按照字母排序

//遍历所有缓存,获取统计数据

ArrayList<Map<String, Object>> list = new ArrayList<>();

for(String cacheName : cacheNameList){

list.add(getCacheStatsToMap(cacheName));

}

return list;

}

/**

* 返回一个缓存的统计数据

* @param cacheName

* @return Map<统计指标,统计数据>

*/

private static Map<String, Object> getCacheStatsToMap(String cacheName) {

Map<String, Object> map = new LinkedHashMap<>();

GuavaAbstractLoadingCache<Object, Object> cache = getCacheByName(cacheName);

CacheStats cs = cache.getCache().stats();

NumberFormat percent = NumberFormat.getPercentInstance(); // 建立百分比格式化用

percent.setMaximumFractionDigits(1); // 百分比小数点后的位数

map.put("cacheName", cacheName);

map.put("size", cache.getCache().size());

map.put("maximumSize", cache.getMaximumSize());

map.put("survivalDuration", cache.getExpireAfterWriteDuration());

map.put("hitCount", cs.hitCount());

map.put("hitRate", percent.format(cs.hitRate()));

map.put("missRate", percent.format(cs.missRate()));

map.put("loadSuccessCount", cs.loadSuccessCount());

map.put("loadExceptionCount", cs.loadExceptionCount());

map.put("totalLoadTime", cs.totalLoadTime()/1000000); //ms

SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

if(cache.getResetTime()!=null){

map.put("resetTime", df.format(cache.getResetTime()));

}

map.put("highestSize", cache.getHighestSize());

if(cache.getHighestTime()!=null){

map.put("highestTime", df.format(cache.getHighestTime()));

}

return map;

}

/**

* 根据cacheName清空缓存数据

* @param cacheName

*/

public static void resetCache(String cacheName){

GuavaAbstractLoadingCache<Object, Object> cache = getCacheByName(cacheName);

cache.getCache().invalidateAll();

cache.setResetTime(new Date());

}

/**

* 分页获得缓存中的数据

* @param pageParams

* @return

*/

public static PageResult<Object> queryDataByPage(PageParams<Object> pageParams) {

PageResult<Object> data = new PageResult<>(pageParams);

GuavaAbstractLoadingCache<Object, Object> cache = getCacheByName((String) pageParams.getParams().get("cacheName"));

ConcurrentMap<Object, Object> cacheMap = cache.getCache().asMap();

data.setTotalRecord(cacheMap.size());

data.setTotalPage((cacheMap.size()-1)/pageParams.getPageSize()+1);

//遍历

Iterator<Entry<Object, Object>> entries = cacheMap.entrySet().iterator();

int startPos = pageParams.getStartPos()-1;

int endPos = pageParams.getEndPos()-1;

int i=0;

Map<Object, Object> resultMap = new LinkedHashMap<>();

while (entries.hasNext()) {

Map.Entry<Object, Object> entry = entries.next();

if(i>endPos){

break;

}

if(i>=startPos){

resultMap.put(entry.getKey(), entry.getValue());

}

i++;

}

List<Object> resultList = new ArrayList<>();

resultList.add(resultMap);

data.setResults(resultList);

return data;

}

}

Cache Controller:调用Cache管理类,为管理页面提供Web HTTP接口

[java] view
plain copy

package com.xjj.web.controller;

/**

* 本地缓存管理接口:统计信息查询、重置数据……等

* @author XuJijun

*

*/

@RestController

@RequestMapping("/cache/admin")

public class CacheAdminController {

/**

* 查询cache统计信息

* @param cacheName

* @return cache统计信息

*/

@RequestMapping(value = "/stats", method = RequestMethod.POST)

public JsonResult cacheStats(String cacheName) {

JsonResult jsonResult = new JsonResult();

//暂时只支持获取全部

switch (cacheName) {

case "*":

jsonResult.setData(GuavaCacheManager.getAllCacheStats());

jsonResult.setMessage("成功获取了所有的cache!");

break;

default:

break;

}

return jsonResult;

}

/**

* 清空缓存数据、并返回清空后的统计信息

* @param cacheName

* @return

*/

@RequestMapping(value = "/reset", method = RequestMethod.POST)

public JsonResult cacheReset(String cacheName) {

JsonResult jsonResult = new JsonResult();

GuavaCacheManager.resetCache(cacheName);

jsonResult.setMessage("已经成功重置了" + cacheName + "!");

return jsonResult;

}

/**

* 返回所有的本地缓存统计信息

* @return

*/

@RequestMapping(value = "/stats/all", method = RequestMethod.POST)

public JsonResult cacheStatsAll() {

return cacheStats("*");

}

/**

* 分页查询数据详情

* @param pageSize

* @param pageNo

* @param cacheName

* @return

*/

@RequestMapping(value = "/queryDataByPage", method = RequestMethod.POST)

public PageResult<Object> queryDataByPage(@RequestParam Map<String, String> params){

int pageSize = Integer.valueOf(params.get("pageSize"));

int pageNo = Integer.valueOf(params.get("pageNo"));

String cacheName = params.get("cacheName");

PageParams<Object> page = new PageParams<>();

page.setPageSize(pageSize);

page.setPageNo(pageNo);

Map<String, Object> param = new HashMap<>();

param.put("cacheName", cacheName);

page.setParams(param);

return GuavaCacheManager.queryDataByPage(page);

}

}

Cache管理页面:Web页面,用于查看Cache列表、统计信息、数据,清空缓存,等(文件名:cache-admin.html)

[html] view
plain copy

<!DOCTYPE html>

<html>

<head>

<title>Cache Admin</title>

<meta charset="UTF-8">

<script src="./resources/js/jquery-2.1.4.js" charset="UTF-8" type="text/javascript"></script>

<style>

.important {color : red;}

.attention {color : orange;}

.perfect {color : green;}

.highlight {color : blue;}

table{border: 1px solid #8968CD; border-collapse: collapse;}

th,td{border: 1px solid #8968CD; padding:6px;}

td{color: green;}

</style>

</head>

<body>

<div>

<div id="operations">

Cache列表:

<input type="button" value="刷新" onClick="refreshStatsAll();">

<input type="checkbox" id="autoRefresh" onClick="toggleAutoRefreshStats();"><label for="autoRefresh">自动刷新(3s)</label>

</div>

<div><pre id="response" class="attention"></pre></div>

<div><br><pre id="responseRawData" ></pre></div>

</div>

</body>

<script>

var autoRefreshInterval = 3000;

var autoRefershObject;

var requestStatsAll = {url : "/cache/admin/stats/all", params : "*", callback: requestStatsAllCallback};

$(function() {

refreshStatsAll();

});

function refreshStatsAll(){

ajaxRequest(requestStatsAll.url, requestStatsAll.params, requestStatsAll.callback);

}

function sizeStatistics(obj){

var c = "当前数据量/上限:" + obj.size + "/" + obj.maximumSize;

c += "\n历史最高数据量:" + obj.highestSize;

c += "\n最高数据量时间:" + obj.highestTime;

return c;

}

function hitStatistics(obj){

var c = "命中数量:" + obj.hitCount;

c += "\n命中比例:" + obj.hitRate;

c += "\n读库比例:" + obj.missRate;

return c;

}

function loadStatistics(obj){

var c = "成功加载数:" + obj.loadSuccessCount;

c += "\n失败加载数:" + obj.loadExceptionCount;

c += "\n总加载毫秒:" + obj.totalLoadTime;

return c;

}

function requestStatsAllCallback(jsonResult){

var html = "<table><tr><th>Cache名称</th> <th>数据量统计</th> <th>命中统计</th> <th>加载统计</th> <th>开始/重置时间</th> <th>操作</th> </tr>";

$.each(jsonResult.data, function(idx, obj){

html += "<tr><th>" + obj.cacheName + "</th>"

+ "<td>" + sizeStatistics(obj) + "</td>"

+ "<td>" + hitStatistics(obj) + "</td>"

+ "<td>" + loadStatistics(obj) + "</td>"

+ "<td>" + obj.resetTime +"\n\n失效时长:" + obj.survivalDuration + "(分钟)</td>"

+ "<td>"

+ "<a href='javascript:void(0)' onclick='resetCache(\""+obj.cacheName+"\");'>清空缓存</a>"

+ "\t<a href='javascript:void(0)' onclick='queryDataByPage(\""+obj.cacheName+"\");'>显示详情</a>"

+ "</td>"

+ "</tr>";

});

html += "</table>";

$("#response").html(html);

}

function resetCache(cacheName){

$.ajax({

type : "POST",

url : getRootPath()+"/cache/admin/reset",

dataType : "json", //表示返回值类型

data : {"cacheName":cacheName},

success : function(jsonResult){alert(jsonResult.message);refreshStatsAll();}

});

}

//定时刷新开关

function toggleAutoRefreshStats(){

if($("#autoRefresh").prop("checked")==true){

autoRefershObject = setInterval(refreshStatsAll, autoRefreshInterval);

}else{

clearInterval(autoRefershObject);

}

}

var pageParam = {pageNo : 1, pageSize : 10, cacheName : null};

function resetpageParam(){

pageParam.pageNo = 1;

pageParam.totalPage = 0;

}

function queryDataByPage(cacheName){

resetpageParam();

pageParam.cacheName = cacheName;

ajaxRequest("/cache/admin/queryDataByPage", pageParam, pageQueryCallback);

}

function pageQueryCallback(jsonResult){

pageParam.totalPage = jsonResult.totalPage;

var html = "<label class='highlight'>Cache名称:" + pageParam.cacheName + "</label><br/><br/>";

html += "<a href='javascript:void(0)' onclick='firstPage();'>首页 </a>\t";

html += "<a href='javascript:void(0)' onclick='previousPage();'>上一页 </a>\t";

html += "第<input type='number' id='pageNo' min='1' max='" + jsonResult.totalPage + "' value='" + jsonResult.pageNo + "' size='" + lengthOfNum(jsonResult.totalPage) + "' />页(共" + jsonResult.totalPage + "页)\t";

html += "<a href='javascript:void(0)' onclick='nextPage();'>下一页 </a>\t";

html += "<a href='javascript:void(0)' onclick='lastPage();'>末页</a>\t";

html += "<br/><br/>";

html += JSON.stringify(jsonResult.results[0], null, "\t");

$("#responseRawData").html(html);

$("#pageNo").blur(function(){

pn = $("#pageNo").val();

if(pn < 1){

pn = 1;

$("#pageNo").val(pn);

}else if(pn > pageParam.totalPage){

pn = pageParam.totalPage;

$("#pageNo").val(pn);

}

pageParam.pageNo=pn;

ajaxRequest("/cache/admin/queryDataByPage", pageParam, pageQueryCallback);

});

//回车

$("#pageNo").keyup(function(event){

if(event.which != 13){

return;

}

pn = $("#pageNo").val();

if(pn < 1){

pn = 1;

$("#pageNo").val(pn);

}else if(pn > pageParam.totalPage){

pn = pageParam.totalPage;

$("#pageNo").val(pn);

}

pageParam.pageNo=pn;

ajaxRequest("/cache/admin/queryDataByPage", pageParam, pageQueryCallback);

});

}

function firstPage(){

pageParam.pageNo=1;

ajaxRequest("/cache/admin/queryDataByPage", pageParam, pageQueryCallback);

}

function lastPage(){

pageParam.pageNo=pageParam.totalPage;

ajaxRequest("/cache/admin/queryDataByPage", pageParam, pageQueryCallback);

}

function nextPage(){

if(pageParam.pageNo==pageParam.totalPage){

alert("已经是最后一页了!");

return;

}

pageParam.pageNo++;

ajaxRequest("/cache/admin/queryDataByPage", pageParam, pageQueryCallback);

}

function previousPage(){

if(pageParam.pageNo==1){

alert("已经是第一页了!");

return;

}

pageParam.pageNo--;

ajaxRequest("/cache/admin/queryDataByPage", pageParam, pageQueryCallback);

}

//js获取项目根路径,如: http://localhost:8083/uimcardprj
function getRootPath() {

//获取当前网址,如: http://localhost:8083/uimcardprj/share/meun.jsp
var curWwwPath = window.document.location.href;

//获取主机地址之后的目录,如: uimcardprj/share/meun.jsp

var pathName = window.document.location.pathname;

var pos = curWwwPath.indexOf(pathName);

//获取主机地址,如: http://localhost:8083
var localhostPath = curWwwPath.substring(0, pos);

//获取带"/"的项目名,如:/uimcardprj

var projectName = pathName.substring(0, pathName.substr(1).indexOf('/') + 1);

return (localhostPath + projectName);

}

//发送ajax请求

function ajaxRequest(url, params, successCallback, contentType, errorCallback, async) {

var _async = async || true;

$.ajax({

type : "POST",

url : getRootPath() + url,

async : _async,

contentType : contentType,

dataType : "json", //表示返回值类型

data : params,

success : successCallback,

error : errorCallback

});

}

function lengthOfNum(num){

var length = 1;

var _num = num;

while((_num=_num/10) >= 1){

length++;

}

return length;

}

</script>

</html>

其他代码,包括测试页面和测试Controller请参考源代码:https://github.com/xujijun/MyJavaStudio,有问题请留言。^_^
测试页面:



管理页面:



(原创文章,转载请注明转自Clement-Xu的博客:http://blog.csdn.net/clementad/article/details/46491701,源码地址:https://github.com/xujijun/MyJavaStudio
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐