利用Spring AOP 更新memcached 缓存策略的实现(二)

来源:互联网 发布:俄克拉荷马大学知乎 编辑:程序博客网 时间:2024/04/19 07:18

承接上文,本人终于履行承诺,实现了不使用数据库实现更新缓存的方法,上文链接:利用Spring AOP 更新memcached 缓存策略的实现(一)

实现思路:

1. 执行业务逻辑查询时,第一次在memcached中不存在,则将查询结果序列化后存入memcached中(key:业务方法名+参数类型+参数值+版本号 转 MD5),并且存入当前业务包的版本号(key:业务包名,value:版本号)

2. 执行业务逻辑查询时,检索memcached中已存在key,反序列化后返回Sevice

3. 修改操作时,修改业务逻辑之后,对应业务的包名版本号自增长,查询时重新存入memcached,原key值慢慢的慢慢的就等着死亡吧~~

具体代码:

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@Inheritedpublic @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[i]+"&");            if (null == args[i]) {                sb.append("null");            } else if ("".equals(args[i])) {                sb.append("*");            } else {                sb.append(args[i]);            }        }        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;@Servicepublic 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;@Repositorypublic 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());        }    }}
复制代码

具体测试方法同上文


转载地址: http://www.cnblogs.com/cczhoufeng/archive/2013/04/10/3011648.html

0 0