设为首页 收藏本站
查看: 1137|回复: 0

[经验分享] 利用Spring AOP 更新memcached 缓存策略的实现(二)

[复制链接]

尚未签到

发表于 2015-8-31 12:58:55 | 显示全部楼层 |阅读模式
  原创文章,请尊重作者的辛勤劳动,转载请注明!
  承接上文,本人终于履行承诺,实现了不使用数据库实现更新缓存的方法,上文链接:利用Spring AOP 更新memcached 缓存策略的实现(一)
  实现思路:
  1. 执行业务逻辑查询时,第一次在memcached中不存在,则将查询结果序列化后存入memcached中(key:业务方法名+参数类型+参数值+版本号 转 MD5),并且存入当前业务包的版本号(key:业务包名,value:版本号)
  2. 执行业务逻辑查询时,检索memcached中已存在key,反序列化后返回Sevice
  3. 修改操作时,修改业务逻辑之后,对应业务的包名版本号自增长,查询时重新存入memcached,原key值慢慢的慢慢的就等着死亡吧~~
  具体代码:
DSC0000.jpg
  1. 配置spring aop 查看上文
  2. 下载memcached的jar包,相关方法请参考本人之前的博客:这里
  3. 编写两个注解,分别为@Cache和@CacheUpdate



package com.dsideal.common;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 用于查找的时候,放置缓存信息
* @author Administrator
*
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Cache {
//key的前缀
    String prefix();
//缓存有效期 1000*60*60*2=2小时
long expiration() default 1000*60*60*2;
}


package com.dsideal.common;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
/**
* 修改时标注
* @author 周枫
*
*/
public @interface CacheUpdate {
//key的前缀
    String prefix();
}
  4. memcached的配置类为



package com.dsideal.sys.memcached;
import java.util.Date;
import com.danga.MemCached.MemCachedClient;
import com.danga.MemCached.SockIOPool;
public class MemcacheBlog {
// 创建全局的唯一实例

static MemCachedClient memCachedClient=null;
// 设置与缓存服务器的连接池
static{  
//memcached服务器端IP和端口
String[] servers = {"192.168.100.102:11211"};   
SockIOPool pool = SockIOPool.getInstance();   
pool.setServers(servers);   
pool.setFailover(true);   
// 设置初始连接数、最小和最大连接数以及最大处理时间   
/*    pool.setInitConn(5);
pool.setMinConn(5);
pool.setMaxConn(250);
pool.setMaxIdle(1000 * 60 * 60 * 6); */  
pool.setInitConn(10);   
pool.setMinConn(5);   
pool.setMaxConn(250);   
pool.setMaintSleep(30);  // 设置主线程的睡眠时间   
// 设置TCP的参数,连接超时等   
pool.setNagle(false);   
pool.setSocketTO(3000);   
pool.setAliveCheck(true);   
pool.initialize();   
memCachedClient = new MemCachedClient();      
memCachedClient.setPrimitiveAsString(true);
}  
/**
* <p>功能:
* get:获取数据的方法
* get_multi:一次取得多条数据;getmulti可以非同步地同时取得多个键值, 其速度要比循环调用get快数十倍
* </p>
* @author 周枫
* @date 2013-4-3
* @param
* @return Object
*/
public static Object get(String key)  
{  
return memCachedClient.get(key);  
}  
/**
* <p>功能:
* add:仅当存储空间中不存在键相同的数据时才保存
* replace:仅当存储空间中存在键相同的数据时才保存
* set:与add和replace不同,无论何时都保存</p>
* @author 周枫
* @date 2013-4-3
* @param
* @return Object
*/
public static boolean set(String key,Object o)  
{  
return memCachedClient.set(key, o);      
}  
public static boolean set(String key,Object o,Date ExpireTime)  
{         
return memCachedClient.set(key, o, ExpireTime);  
}  
public static boolean exists(String key)  
{  
return memCachedClient.keyExists(key);  
}  
public static boolean delete(String key)  
{  
return memCachedClient.delete(key);  
}  
public static boolean replace(String key,Object o)  
{  
return memCachedClient.replace(key, o);  
}  
}
  5. spring-aop实现类



package com.dsideal.sys.memcached;
import java.lang.reflect.Method;  
import java.util.ArrayList;
import java.util.Date;  
import java.util.List;  
import javax.annotation.Resource;  
import org.aspectj.lang.ProceedingJoinPoint;  
import org.aspectj.lang.Signature;  
import org.aspectj.lang.annotation.Around;  
import org.aspectj.lang.annotation.Aspect;  
import org.aspectj.lang.annotation.Pointcut;  
import org.aspectj.lang.reflect.MethodSignature;  
import org.springframework.stereotype.Component;  
import com.alibaba.fastjson.JSON;
import com.dsideal.common.Cache;
import com.dsideal.common.CacheUpdate;
import com.dsideal.common.Flush;
import com.dsideal.sys.bean.CacheLog;
import com.dsideal.sys.bean.SysLoginPersonBean;
import com.dsideal.sys.service.ICacheLogService;
import com.dsideal.common.MD5;
@Aspect  
@Component
public class CacheAopNoMysql {
//  @Pointcut("execution(* add*(..)) || (execution(* del*(..))) || (execution(* get*(..)))")  
//* com.dsideal.sys.service.impl.*.getMemcache*(..)
@Pointcut("execution (* com.dsideal.sys.service.impl.*.memcache*(..))")  
public void pointcut(){}  
//方法执行前调用  
//@Before("pointcut()")  
public void before() {  
System.out.println("before");  //2
    }  
@Resource
private ICacheLogService cacheLogService;  

//方法执行的前后调用   
@Around("pointcut()")  
//ProceedingJoinPoint 目标类连接点对象
public Object doAround(ProceedingJoinPoint call) throws Throwable{  
//返回最终结果
Object result = null;  
//定义版本号,默认为1
String prefixValue = "1";
Method[] methods = call.getTarget().getClass().getDeclaredMethods();   
Signature signature = call.getSignature();  
MethodSignature methodSignature = (MethodSignature) signature;   
Method method = methodSignature.getMethod();  
for(Method m:methods){
//循环方法,找匹配的方法进行执行  
if(m.getName().equals(method.getName())){
//增加
if(m.isAnnotationPresent(Cache.class)){  
Cache cache = m.getAnnotation(Cache.class);
Object tempType = m.getGenericReturnType();
//System.out.println(m.get);
//如果memcached中存在
if(cache!=null){  
//获取注解前缀,这里为 sys,实际使用是为各个业务包名
String prefix = cache.prefix();  
//获取版本号
if(null != MemcacheBlog.get(prefix)){
prefixValue = MemcacheBlog.get(prefix).toString();
}
//获取方法名+参数类型+参数值+版本号 转 MD5
String tempKey = this.getKey(method, call.getArgs(), prefixValue);  
//存入memcached的最终key值
String key = prefix+"_"+tempKey;  
result =MemcacheBlog.get(key);  
if(null == result){  
try {  
//执行aop拦截的方法
result = call.proceed();  
//获取注解配置memcached死亡时间
long expiration = cache.expiration();
//1000*60*60*2==2小时过期  
Date d=new Date();  
//memcached死亡时间
d=new Date(d.getTime()+expiration);
//利用fastjson序列化list<bean>存入memcached中
//具体fastjson使用方法请参考:http://www.cnblogs.com/cczhoufeng/archive/2013/04/03/2997871.html
if(prefixValue.equals("1")){
MemcacheBlog.set(prefix, prefixValue);
}
MemcacheBlog.set(key, JSON.toJSONString(result), d);                                
} catch (Throwable e) {  
e.printStackTrace();  
}  
}  else {
//如果memcached中存在结果,需要将result反序列化后返回结果
String memresult = result.toString();
//反序列化
List<SysLoginPersonBean> list = JSON.parseArray(memresult, SysLoginPersonBean.class);
result = list;
//这里是利用fastjson反序列化输出的方法
//String memresult = result.toString();
//List<SysLoginPersonBean> list =  JSON.parseArray(memresult, SysLoginPersonBean.class);
//for (int i = 0; i < list.size(); i++) {
//    System.out.println(list.get(i).getReal_name());
//}
                        }
}  
} else  if(m.isAnnotationPresent(CacheUpdate.class)){  
//如果修改操作时
CacheUpdate cUpdate = m.getAnnotation(CacheUpdate.class);  
if(cUpdate!=null){  
result = call.proceed();
String prefix = cUpdate.prefix();  
//获取当前版本号
if(null != MemcacheBlog.get(prefix)){
prefixValue = MemcacheBlog.get(prefix).toString();
}
//修改后,版本号+1
MemcacheBlog.replace(prefix, Integer.parseInt(prefixValue.toString()) + 1);
System.out.println(MemcacheBlog.get(prefix).toString());
}  
}else{  
try {  
result = call.proceed();  
} catch (Throwable e) {  
e.printStackTrace();  
}  
}  
break;  
}  
}
return result;
}
/**
* 组装key值
* @param method
* @param args
* @return
*/  
private String getKey(Method method, Object [] args, String prefixValue){  
StringBuffer sb = new StringBuffer();   
//获取方法名
String methodName = method.getName();
//获取参数类型
Object[] classTemps = method.getParameterTypes();
//存入方法名
        sb.append(methodName);
for (int i = 0; i < args.length; i++) {
sb.append(classTemps+"&");
if (null == args) {
sb.append("null");
} else if ("".equals(args)) {
sb.append("*");
} else {
sb.append(args);
}
}
sb.append(prefixValue);
return MD5.getMD5(sb.toString());  
}  
}
  6. MD5的实现类,见上文
  7. 我做列子用的业务表,mysql数据库,见上文
  8. 实体bean,见上文
  9. 接口(这里我偷懒了,和上文使用的是同一个接口文件,用不到的自己过滤下吧,呵呵)



package com.dsideal.sys.service;
import java.util.List;
import com.dsideal.sys.bean.CacheLog;
import com.dsideal.sys.bean.SysLoginPersonBean;
public interface ICacheLogService {
/**
* <p>功能:增加memcached数据文件到临时表中</p>
* @author 周枫
* @date 2013-4-9
* @param
* @return void
*/
public void add(CacheLog log);
/**
* <p>功能:查询以prefix为前缀的所有key值,在更新删除时使用此方法</p>
* @author 周枫
* @date 2013-4-9
* @param
* @return List<CacheLog>
*/
public List<CacheLog> findListByPrefix(String prefix);
/**
* <p>功能:删除操作时,aop拦截</p>
* @author 周枫
* @date 2013-4-9
* @param
* @return void
*/
public void memcacheDeleteByPrefix();
/**
* <p>功能:删除临时表记录的数据</p>
* @author 周枫
* @date 2013-4-9
* @param
* @return int
*/
public int deleteByPrefix(String prefix);
/**
* <p>功能:查找例子,查找所有人员数据,后面的person_id没有使用,只是为了测试key值的生成策略</p>
* @author 周枫
* @date 2013-4-9
* @param
* @return List<SysLoginPersonBean>
*/
public List<SysLoginPersonBean> memcacheFindAll(int b_use,String person_id);
/**
* <p>功能:测试方法,可以忽略</p>
* @author 周枫
* @date 2013-4-9
* @param
* @return List<CacheLog>
*/
public List<CacheLog> memcacheCacheLogFindAll();
/**
* <p>功能:修改人员</p>
* @author 周枫
* @date 2013-4-9
* @param
* @return int
*/
public int memcacheupdateSysLoginPersonBean(String prefix,String person_id,String real_name);
/**
* <p>功能:不用数据库临时表更新缓存时修改人员</p>
* @author 周枫
* @date 2013-4-9
* @param
* @return int
*/
public int memcacheUpdateNoSql(String prefix,String person_id,String real_name);

}
  10. 接口的实现类,"sys"为业务包名,为key的前缀,expiration:自定义memcached死亡时间,版本号无死亡时间



package com.dsideal.sys.service.impl;
import java.util.List;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.dsideal.common.Cache;
import com.dsideal.common.CacheUpdate;
import com.dsideal.common.Flush;
import com.dsideal.sys.bean.CacheLog;
import com.dsideal.sys.bean.SysLoginPersonBean;
import com.dsideal.sys.dao.CacheLogDao;
import com.dsideal.sys.service.ICacheLogService;
@Service
public class CacheLogServiceImpl implements ICacheLogService {
@Resource
private CacheLogDao dao;
@Override
public void add(CacheLog log) {
dao.add(log);
}
@Override
public List<CacheLog> findListByPrefix(String prefix) {
// TODO Auto-generated method stub
return dao.findListByPrefix(prefix);
}
@Override
@Flush(prefix="sys")
public void memcacheDeleteByPrefix() {
// TODO Auto-generated method stub
    }
@Override
public int deleteByPrefix(String prefix) {
// TODO Auto-generated method stub
return dao.deleteByPrefix(prefix);
}
@Override
@Cache(prefix="sys",expiration=1000*60*60*2)
public List<SysLoginPersonBean> memcacheFindAll(int b_use,String person_id) {
// TODO Auto-generated method stub
return dao.findAll(b_use);
}
@Override
@Cache(prefix="sys",expiration=1000*60*60*2)
public List<CacheLog> memcacheCacheLogFindAll() {
// TODO Auto-generated method stub
return dao.findCacheLogAll();
}
@Override
@Flush(prefix="sys")
public int memcacheupdateSysLoginPersonBean(String prefix,String person_id,String real_name) {
return dao.updateSysLoginPersonBean(person_id,real_name);
}
@Override
@CacheUpdate(prefix="sys")
public int memcacheUpdateNoSql(String prefix,String person_id,String real_name) {
return dao.updateSysLoginPersonBean(person_id,real_name);
}
}
  11. dao层



package com.dsideal.sys.dao;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Repository;
import java.util.Date;
import java.util.List;
import com.dsideal.common.BaseDao;
import com.dsideal.common.Utils.RSMapper;
import com.dsideal.sys.bean.CacheLog;
import com.dsideal.sys.bean.SysLoginPersonBean;

@Repository
public class CacheLogDao extends BaseDao {
public void add(CacheLog log) {
try {
String sql = "INSERT INTO t_cache_log(prefix,cache_key,add_time) VALUES (?,?,?)";
int result = 0;
Date now = new Date();
result = this.jdbcTemplate.update(sql, log.getPrefix(),log.getCache_key(),now);
System.out.println("增加成功");
} catch (DataAccessException e) {
e.printStackTrace();
System.out.println("增加报错");
}
}
public int deleteByPrefix(String prefix) {
try {
String sql = "DELETE FROM t_cache_log WHERE prefix = ?";
int result = 0;
result = this.jdbcTemplate.update(sql, prefix);
return result;
} catch (DataAccessException e) {
e.printStackTrace();
}
return 0;
}
public List<CacheLog> findListByPrefix(String prefix) {
try {
String sql = "SELECT * FROM t_cache_log WHERE prefix = ?";
return RSMapper.queryList(jdbcTemplate, sql, CacheLog.class, prefix);
} catch (DataAccessException e) {
e.printStackTrace();
}
return null;
}
public List<SysLoginPersonBean> findAll(int b_use) {
try {
String sql = "SELECT * FROM t_sys_loginperson WHERE b_use = ?";
return RSMapper.queryList(jdbcTemplate, sql, SysLoginPersonBean.class,b_use);
} catch (DataAccessException e) {
e.printStackTrace();
}
return null;
}
public List<CacheLog> findCacheLogAll() {
try {
String sql = "SELECT * FROM t_cache_log";
return RSMapper.queryList(jdbcTemplate, sql, CacheLog.class);
} catch (DataAccessException e) {
e.printStackTrace();
}
return null;
}
public int updateSysLoginPersonBean(String person_id,String real_name) {
int result = 0;
try {
String sql = "UPDATE t_sys_loginperson SET real_name = ? WHERE person_id = ?";
return this.jdbcTemplate.update(sql, real_name,person_id);
} catch (DataAccessException e) {
e.printStackTrace();
System.out.println("修改时报错");
}
return result;
}
}
  12. Service测试类



package com.dsideal.sys.test;
import java.util.List;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.dsideal.sys.bean.CacheLog;
import com.dsideal.sys.bean.SysLoginPersonBean;
import com.dsideal.sys.service.ICacheLogService;
import com.dsideal.sys.service.impl.CacheLogServiceImpl;
@RunWith(SpringJUnit4ClassRunner.class)
//指定Spring的配置文件 /为classpath下
@ContextConfiguration(locations = {"/spring-mvc.xml"})
public class CacheLogServiceTest {
@Autowired
private ICacheLogService impl;
@Before //在每个测试用例方法之前都会执行  
public void init(){  
}  
@After //在每个测试用例执行完之后执行  
public void destory(){  
}  
@Test
public void add() {
CacheLog log = new CacheLog();
impl.add(log);
}
@Test
public void findAll() {
int b_use = 1;
String person_id = "";
List<SysLoginPersonBean> list = impl.memcacheFindAll(b_use,person_id);
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i).getReal_name());
}
}
@Test
public void deleteByPrefix() {
System.out.println("1");
impl.memcacheDeleteByPrefix();
System.out.println("删除成功");
}
@Test
public void findCacheLogAll() {
List<CacheLog> list = impl.memcacheCacheLogFindAll();
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i).getCache_key());
}
}
@Test
public void updateToDeleteToFind() {
String prefix = "sys";
String person_id1 = "0E04DE60-7264-4FE7-9A6C-5AB843B603CC";
String person_id2 = "";
String real_name = "初中历史管理员1";
int result = impl.memcacheupdateSysLoginPersonBean(prefix, person_id1, real_name);
if (result > 0) {
System.out.println("修改成功");
}
int b_use = 1;
List<SysLoginPersonBean> list = impl.memcacheFindAll(b_use,person_id2);
System.out.println("查询成功");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i).getReal_name());
}
}
@Test
public void updateNoSqlToDeleteToFind() {
String prefix = "sys";
String person_id1 = "0E04DE60-7264-4FE7-9A6C-5AB843B603CC";
String person_id2 = "";
String real_name = "初中历史管理员2";
int result = impl.memcacheUpdateNoSql(prefix, person_id1, real_name);
if (result > 0) {
System.out.println("修改成功");
}
int b_use = 1;
List<SysLoginPersonBean> list = impl.memcacheFindAll(b_use,person_id2);
System.out.println("查询成功");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i).getReal_name());
}
}
}
  具体测试方法同上文
  PS: 具体测试方法就是以上的步骤了,代码写的有点乱,如果有问题,希望大家可以指出,谢谢~~

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-106826-1-1.html 上篇帖子: Memcached在Asp.NET中的使用 下篇帖子: 利用Attribute与PIAB构建支持memcached功能的数据访问层
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表