我们先看看运行效果图:

上图中,我们启动了两个java进程,分别运行在 8080 和 8081 端口。当我们在 8080 端口添加 name 和 version 缓存后,立马在 8081 端口所在的 Java 进程中显示出来。同样,从 8081 端口删除一个缓存,立马在 8080 中体现出来。如果你仔细看,你会发现我们在 8080 端口中新添加的 name 和 version 缓存信息后,在 8081 中首次显示出来的 level 为 2(表示来自二级缓存),然后变为 level = 1(一级缓存)。
我们先看看项目结构,如下图:

下面将配置 Freemarker 模板,如下:
## Freemarker # 指定 HttpServletRequest 的属性是否可以覆盖 controller 中与 model 同名项 spring.freemarker.allow-request-override=false # 是否开启模版缓存 spring.freemarker.cache=true # 是否检查模版路径是否存在 spring.freemarker.check-template-location=true # 设定模版编码 spring.freemarker.charset=utf-8 # 设定Content-Type spring.freemarker.content-type=text/html # 设定所有request的属性在merge到模板的时候,是否要都添加到model中 spring.freemarker.expose-request-attributes=false # 设定所有HttpSession的属性在merge到模板的时候,是否要都添加到model中 spring.freemarker.expose-session-attributes=false # 设定是否以springMacroRequestContext的形式暴露RequestContext给Spring’s macrolibrary使用 spring.freemarker.expose-spring-macro-helpers=false # 设定freemarker模板的前缀 spring.freemarker.prefix= # 指定RequestContext属性的名 spring.freemarker.request-context-attribute=freemarker # 设定模板的后缀 spring.freemarker.suffix=.ftl # 设定模板的加载路径,多个以逗号分隔,默认: ["classpath:/templates/"] spring.freemarker.template-loader-path=classpath:/templates/
#J2Cache configuration ######################################### # Cache Broadcast Method # values: # jgroups -> use jgroups's multicast # redis -> use redis publish/subscribe mechanism (using jedis) # lettuce -> use redis publish/subscribe mechanism (using lettuce, Recommend) # rabbitmq -> use RabbitMQ publisher/consumer mechanism # rocketmq -> use RocketMQ publisher/consumer mechanism # none -> don't notify the other nodes in cluster # xx.xxxx.xxxx.Xxxxx your own cache broadcast policy classname that implement net.oschina.j2cache.cluster.ClusterPolicy ######################################### j2cache.broadcast = redis # jgroups properties jgroups.channel.name = j2cache jgroups.configXml = /network.xml # RabbitMQ properties rabbitmq.exchange = j2cache rabbitmq.host = localhost rabbitmq.port = 5672 rabbitmq.username = guest rabbitmq.password = guest # RocketMQ properties rocketmq.name = j2cache rocketmq.topic = j2cache # use ; to split multi hosts rocketmq.hosts = 127.0.0.1:9876 ######################################### # Level 1&2 provider # values: # none -> disable this level cache # ehcache -> use ehcache2 as level 1 cache # ehcache3 -> use ehcache3 as level 1 cache # caffeine -> use caffeine as level 1 cache(only in memory) # redis -> use redis as level 2 cache (using jedis) # lettuce -> use redis as level 2 cache (using lettuce) # readonly-redis -> use redis as level 2 cache ,but never write data to it. if use this provider, you must uncomment `j2cache.L2.config_section` to make the redis configurations available. # memcached -> use memcached as level 2 cache (xmemcached), # [classname] -> use custom provider ######################################### j2cache.L1.provider_class = ehcache3 j2cache.L2.provider_class = redis # When L2 provider isn't `redis`, using `L2.config_section = redis` to read redis configurations # j2cache.L2.config_section = redis # Enable/Disable ttl in redis cache data (if disabled, the object in redis will never expire, default:true) # NOTICE: redis hash mode (redis.storage = hash) do not support this feature) j2cache.sync_ttl_to_redis = true # Whether to cache null objects by default (default false) j2cache.default_cache_null_object = true ######################################### # Cache Serialization Provider # values: # fst -> using fast-serialization (recommend) # kryo -> using kryo serialization # json -> using fst's json serialization (testing) # fastjson -> using fastjson serialization (embed non-static class not support) # java -> java standard # fse -> using fse serialization # [classname implements Serializer] ######################################### j2cache.serialization = json #json.map.person = net.oschina.j2cache.demo.Person ######################################### # Ehcache configuration ######################################### # ehcache.configXml = /ehcache.xml ehcache3.configXml = /ehcache3.xml ehcache3.defaultHeapSize = 10000 ######################################### # Caffeine configuration # caffeine.region.[name] = size, xxxx[s|m|h|d] # ######################################### caffeine.properties = /caffeine.properties ######################################### # Redis connection configuration ######################################### ######################################### # Redis Cluster Mode # # single -> single redis server # sentinel -> master-slaves servers # cluster -> cluster servers (\u6570\u636e\u5e93\u914d\u7f6e\u65e0\u6548\uff0c\u4f7f\u7528 database = 0\uff09 # sharded -> sharded servers (\u5bc6\u7801\u3001\u6570\u636e\u5e93\u5fc5\u987b\u5728 hosts \u4e2d\u6307\u5b9a\uff0c\u4e14\u8fde\u63a5\u6c60\u914d\u7f6e\u65e0\u6548 ; redis://user:password@127.0.0.1:6379/0\uff09 # ######################################### redis.mode = single #redis storage mode (generic|hash) redis.storage = generic ## redis pub/sub channel name redis.channel = j2cache ## redis pub/sub server (using redis.hosts when empty) redis.channel.host = #cluster name just for sharded redis.cluster_name = j2cache ## redis cache namespace optional, default[empty] redis.namespace = ## redis command scan parameter count, default[1000] #redis.scanCount = 1000 ## connection # Separate multiple redis nodes with commas, such as 192.168.0.10:6379,192.168.0.11:6379,192.168.0.12:6379 redis.hosts = 127.0.0.1:6379 redis.timeout = 2000 redis.password = redis.database = 0 redis.ssl = false ## redis pool properties redis.maxTotal = 100 redis.maxIdle = 10 redis.maxWaitMillis = 5000 redis.minEvictableIdleTimeMillis = 60000 redis.minIdle = 1 redis.numTestsPerEvictionRun = 10 redis.lifo = false redis.softMinEvictableIdleTimeMillis = 10 redis.testOnBorrow = true redis.testOnReturn = false redis.testWhileIdle = true redis.timeBetweenEvictionRunsMillis = 300000 redis.blockWhenExhausted = false redis.jmxEnabled = false ######################################### # Lettuce scheme # # redis -> single redis server # rediss -> single redis server with ssl # redis-sentinel -> redis sentinel # redis-cluster -> cluster servers # ######################################### ## redis command scan parameter count, default[1000] #lettuce.scanCount = 1000 lettuce.namespace = lettuce.storage = hash lettuce.channel = j2cache lettuce.scheme = redis lettuce.hosts = 127.0.0.1:6379 lettuce.password = lettuce.database = 0 lettuce.sentinelMasterId = lettuce.maxTotal = 100 lettuce.maxIdle = 10 lettuce.minIdle = 10 # timeout in milliseconds lettuce.timeout = 10000 # redis cluster topology refresh interval in milliseconds lettuce.clusterTopologyRefresh = 3000 ######################################### # memcached server configurations # refer to https://gitee.com/mirrors/XMemcached ######################################### memcached.servers = 127.0.0.1:11211 memcached.username = memcached.password = memcached.connectionPoolSize = 10 memcached.connectTimeout = 1000 memcached.failureMode = false memcached.healSessionInterval = 1000 memcached.maxQueuedNoReplyOperations = 100 memcached.opTimeout = 100 memcached.sanitizeKeys = false
<!-- for ehcache 3.x --> <config xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns='http://www.ehcache.org/v3' xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core.xsd"> <!-- Don't remote default cache configuration --> <cache-template name="default"> <key-type>java.lang.String</key-type> <value-type>java.io.Serializable</value-type> <expiry> <ttl unit="seconds">1800</ttl> </expiry> <resources> <heap>1000</heap> <offheap unit="MB">100</offheap> </resources> </cache-template> <cache alias="default" uses-template="default"/> </config>
本文件提供 UI 简单界面,代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>J2Cache</title>
<script type="text/javascript" src="js/jquery-v1.8.1.js"></script>
</head>
<body>
<h3>新增/修改缓存</h3>
<div>
键: <input id="key" type="text"/>
值: <input id="value" type="text"/>
<button id="btnSubmit">提交</button>
</div>
<h3>缓存列表
<label style="font-size:14px;font-weight:bold;">
<input type="checkbox" id="autoRefresh" value="1" checked>自动刷新
</label>
</h3>
<table width="100%" border="1">
<thead>
<tr>
<th>key</th>
<th>value</th>
<th>region</th>
<th>level</th>
<th></th>
</tr>
</thead>
<tbody id="keyList"></tbody>
</table>
<script type="text/javascript">
var autoRefresh = false;
var intervalObj = null;
$(function(){
autoRefresh = $("#autoRefresh:checked").val();
function getCacheList(){
$.getJSON("/getCacheList", {}, function(ret){
var htmlCode = [];
for(var attr in ret) {
var row = ret[attr] || {};
htmlCode.push("<tr>")
htmlCode.push("<td>" + attr + "</td>");
htmlCode.push("<td>" + row.value + "</td>");
htmlCode.push("<td>" + row.region + "</td>");
htmlCode.push("<td>" + row.level + "</td>");
htmlCode.push("<td><a href=\"javascript:void(0);\" key=\"" + attr + "\"" +
" onclick=\"J2CacheDemo.deleteCache(event)\">删除</a></td>");
htmlCode.push("</tr>")
}
$("#keyList").html(htmlCode.join(""));
});
}
function startInterval() {
if(!!intervalObj) {
clearInterval(intervalObj);
}
intervalObj = setInterval(function(){
if(!!autoRefresh) {
getCacheList();
}
}, 2000);
}
$("#autoRefresh").change(function(){
autoRefresh = $("#autoRefresh:checked").val();
startInterval();
});
// 添加缓存
$("#btnSubmit").click(function(){
var key = $("#key").val();
var value = $("#value").val();
$.getJSON("/addCache", {
key: key,
value: value
}, function(ret){
if(ret.statu == "1") {
$("#key").val("");
$("#value").val("");
}
alert(ret.message);
});
});
function deleteCache(event) {
var key = $(event.target).attr("key");
$.getJSON("/deleteCache", {
key: key
}, function(ret){
if(ret.statu == "1") {
getCacheList();
}
alert(ret.message);
})
}
getCacheList();
startInterval();
window.J2CacheDemo = {
deleteCache: deleteCache
};
});
</script>
</body>
</html>package com.huangx.j2cache.demo2;
import net.oschina.j2cache.CacheChannel;
import net.oschina.j2cache.CacheObject;
import net.oschina.j2cache.J2Cache;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
import java.util.Map;
/**
* 可以通过运行时动态指定端口,如下:
* --server.port=8081
*/
@Controller
@SpringBootApplication
public class J2cacheDemo2Application {
private static final String REGION_NAME = "default";
private CacheChannel cacheChannel;
public J2cacheDemo2Application() {
this.cacheChannel = J2Cache.getChannel();
}
public static void main(String[] args) {
SpringApplication.run(J2cacheDemo2Application.class, args);
}
@RequestMapping("/")
public String index() {
return "index";
}
/**
* 添加缓存
* @param key 键
* @param value 值
* @return
*/
@GetMapping("/addCache")
@ResponseBody
public Map<String,Object> addCache(@RequestParam("key") String key,
@RequestParam("value") String value) {
Map<String,Object> map = new HashMap<>();
try {
this.cacheChannel.set(REGION_NAME, key, value);
map.put("statu", "1");
map.put("message", "操作成功");
} catch (Exception e) {
e.printStackTrace();
map.put("statu", "0");
map.put("message", "新增缓存失败,原因:" + e.getMessage());
}
return map;
}
/**
* 删除缓存
* @param key 键
* @return
*/
@GetMapping("/deleteCache")
@ResponseBody
public Map<String,Object> deleteCache(@RequestParam("key") String key) {
Map<String,Object> map = new HashMap<>();
try {
this.cacheChannel.evict(REGION_NAME, key);
map.put("statu", "1");
map.put("message", "操作成功");
} catch (Exception e) {
e.printStackTrace();
map.put("statu", "0");
map.put("message", "删除缓存失败,原因:" + e.getMessage());
}
return map;
}
/**
* 获取缓存列表
* @return
*/
@GetMapping("/getCacheList")
@ResponseBody
public Map<String, CacheObject> getCacheList() {
Map<String, CacheObject> map = new HashMap<String, CacheObject>();
try {
map = this.cacheChannel.get(REGION_NAME, this.cacheChannel.keys(REGION_NAME));
} catch (Exception e) {
e.printStackTrace();
}
return map;
}
}运行上面实例,在程序参数中通过 --server.port 指定服务的端口,分别运行 --server.port=8080 和 --server.port=8081。