数据库访问可能是很多网站的瓶颈。动不动就连接池耗尽、内存溢出等。前面已经讲到如果我们的网站是一个分布式的大型站点,那么使用 memcached实现数据库的前端缓存是个很不错的选择;但如果网站本身足够小只有一个服务器,甚至是vps的那种,不推荐使用memcached,使 用Hibernate或者Mybatis框架自带的缓存系统就行了。

一、开启memcached服务器端服务

如果已经安装了memcached服务器端程序,请确认服务器端服务已开启。

二、引入jar

1.  alisoft-xplatform-asf-cache-2.5.1.jar

2.  commons-logging-1.0.4.jar

3.  hessian-3.0.1.jar

4.  log4j-1.2.9.jar

5.  stax-api-1.0.1.jar

6.  wstx-asl-2.0.2.jar

三、创建memcached客户端配置文件

  1. <memcached>
  2. <!-- name 属性是程序中使用Cache的唯一标识;socketpool 属性将会关联到后面的socketpool配置; -->
  3. <client name="mclient_0" compressEnable="true" defaultEncoding="UTF-8"
  4. socketpool="pool_0">
  5. <!-- 可选,用来处理出错情况 -->
  6. <errorHandler>com.alisoft.xplatform.asf.cache.memcached.MemcachedErrorHandler
  7. </errorHandler>
  8. </client>
  9. <!--
  10. name 属性和client 配置中的socketpool 属性相关联。
  11. maintSleep属性是后台线程管理SocketIO池的检查间隔时间,如果设置为0,则表明不需要后台线程维护SocketIO线程池,默认需要管理。
  12. socketTO 属性是Socket操作超时配置,单位ms。 aliveCheck
  13. 属性表示在使用Socket以前是否先检查Socket状态。
  14. -->
  15. <socketpool name="pool_0" maintSleep="5000" socketTO="3000"
  16. failover="true" aliveCheck="true" initConn="5" minConn="5" maxConn="250"
  17. nagle="false">
  18. <!-- 设置memcache服务端实例地址.多个地址用","隔开 -->
  19. <servers>127.0.0.1:11211</servers>
  20. <!--
  21. 可选配置。表明了上面设置的服务器实例的Load权重. 例如 <weights>3,7</weights> 表示30% load 在
  22. 10.2.224.36:33001, 70% load 在 10.2.224.46:33001
  23. <weights>3,7</weights>
  24. -->
  25. </socketpool>
  26. </memcached>

四、创建memcached客户端程序

客户端工具类:

  1. package com.hl.usersmanager.memcached.client;
  2. import com.alisoft.xplatform.asf.cache.ICacheManager;
  3. import com.alisoft.xplatform.asf.cache.IMemcachedCache;
  4. import com.alisoft.xplatform.asf.cache.memcached.CacheUtil;
  5. import com.alisoft.xplatform.asf.cache.memcached.MemcachedCacheManager;
  6. public class MemcachedCache {
  7. private ICacheManager<IMemcachedCache> manager;
  8. private IMemcachedCache cache;
  9. public MemcachedCache(){
  10. manager = CacheUtil.getCacheManager(IMemcachedCache.class,
  11. MemcachedCacheManager.class.getName());
  12. manager.setConfigFile("memcached.xml");
  13. manager.setResponseStatInterval(5*1000);
  14. manager.start();
  15. cache = manager.getCache("mclient_0");
  16. }
  17. /**
  18. * 获取缓存接口
  19. * @return
  20. */
  21. public IMemcachedCache getCache(){
  22. return cache;
  23. }
  24. /**
  25. * 数据放入缓存
  26. * @param key
  27. * @param object
  28. */
  29. public void put(String key,Object object){
  30. cache.put(key, object);
  31. }
  32. /**
  33. * 从缓存中读取数据
  34. * @param key
  35. * @return
  36. */
  37. public Object get(String key){
  38. return cache.get(key);
  39. }
  40. }

五、使用Spring AOP在数据查询的Service层实现数据缓存及读取

实现数据缓存的过程很简单,就是在Service层查询数据库操作前判断要查询的数据在缓存中是否存在,如果不存在就到数据库中查询,查询完成后将数据放入缓存系统;如果要查询的数据在缓存中已经存在,则直接从缓存中读取,不需要操作数据库。这就大大降低了数据库的连接次数。原理就是这么简单。

但是,如果直接对Service层代码进行修改,就违背了“开放-封闭”原则,也会导致缓存系统的操作代码散落到Service层的各处,不方便代码的管理和维护。所以,Spring
AOP华丽登场了。它使用非入侵式的来创建、管理这些缓存操作代码。

关于Spring AOP本身的一些知识,我们这里不做讲述。参考资料:

由于首先要判断查询数据是否存在于缓存系统,如果存在直接从缓存中读取,也就是说Service层的查询代码根本不会执行;另一方面,如果数据在缓存系统中不存在,从数据库查询出的结果,我们需要将其放入缓存系统中。

我们来看Spring AOP的几个装备中哪个适用呢?那就是最强大的环绕通知装备@Around

下面以UserService为例,其源代码如下:

  1. package com.hl.usersmanager.service.impl;
  2. import java.util.List;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.stereotype.Service;
  5. import org.springframework.transaction.annotation.Transactional;
  6. import com.hl.usersmanager.dao.IUserMapper;
  7. import com.hl.usersmanager.model.Users;
  8. import com.hl.usersmanager.service.IUserService;
  9. //使用Service注解 不需要再在配置文件中配置bean
  10. @Service
  11. public class UserServiceImpl implements IUserService{
  12. @Autowired
  13. private IUserMapper userMapper;
  14. @Override
  15. @Transactional
  16. public Users findUserByName(String name) {
  17. return userMapper.findUserByName(name);
  18. }
  19. ……
  20. }

findUserByName主要实现按照用户名查询用户的功能,现在我们使用Spring AOP来实现缓存:

  1. package com.hl.usersmanager.aop.service;
  2. import org.apache.log4j.Logger;
  3. import org.aspectj.lang.ProceedingJoinPoint;
  4. import org.aspectj.lang.annotation.Around;
  5. import org.aspectj.lang.annotation.Aspect;
  6. import org.aspectj.lang.annotation.Pointcut;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import com.hl.usersmanager.memcached.client.MemcachedCache;
  9. import com.hl.usersmanager.model.Users;
  10. @Aspect
  11. public class UserServiceInterceptor {
  12. public static final Logger log = Logger
  13. .getLogger(UserServiceInterceptor.class);
  14. //将缓存客户端工具类 MemcachedCache 织入进来
  15. @Autowired
  16. private MemcachedCache memcachedCache;
  17. /*
  18. * 定义pointcunt
  19. */
  20. @Pointcut("execution(* com.hl.usersmanager.service.impl.UserServiceImpl.*(..))")
  21. public void aPointcut() {
  22. }
  23. /**
  24. * 环绕装备 用于拦截查询 如果缓存中有数据,直接从缓存中读取;否则从数据库读取并将结果放入缓存
  25. *
  26. * @param call
  27. * @param name
  28. * @return
  29. */
  30. @Around("aPointcut()&&args(name)")
  31. public Users doFindUserByNameAround(ProceedingJoinPoint call, String name) {
  32. Users users = null;
  33. if (memcachedCache.getCache().containsKey("findUserByName_" + name)) {
  34. users = (Users) memcachedCache.get("findUserByName_" + name);
  35. log.debug("从缓存中读取!findUserByName_" + name);
  36. } else {
  37. try {
  38. users = (Users) call.proceed();
  39. if (users != null) {
  40. memcachedCache.put("findUserByName_" + name, users);
  41. log.debug("缓存装备被执行:findUserByName_" + name);
  42. }
  43. } catch (Throwable e) {
  44. e.printStackTrace();
  45. }
  46. }
  47. return users;
  48. }
  49. }

环绕通知装备需要一个ProceedingJoinPoint 类型的参数,它的强大之处在于可以代理一个我们的切入点,指定切入点方法是否执行,或者获取执行后的返回结果!!

memcachedCache.getCache().containsKey("findUserByName_" + name)

可以判断缓存中是否有指定的数据。如果有则直接从缓存中读取:

users = (Users) memcachedCache.get("findUserByName_" + name);

否则调用切入点UserServiceImpl的findUserByName方法:

users = (Users) call.proceed();

call.proceed()表示执行切入点的方法。

使用Spring AOP以后,整个缓存系统代码看起来 就是这么优雅!UserServiceImpl根本不知道外界发了什么,更不知道外界调用它的findUserByName的时候已经被拦截了!

那天不用缓存系统,只需要将Aop这块的代码去掉即可。

当然,我们还需要在Spring配置文件中注册一个memcached客户端工具类的bean:

    1. <!-- MemcachedCache缓存 -->
    2. <bean id="MemcachedCache" class="com.hl.usersmanager.memcached.client.MemcachedCache"></bean>

使用Memcached、Spring AOP构建数据库前端缓存框架的更多相关文章

  1. spring aop实现数据库的读写分离

    为了减轻数据库的压力,一般会使用数据库主从(master/slave)的方式,但是这种方式会给应用程序带来一定的麻烦,比如说,应用程序如何做到把数据写到master库,而读取数据的时候,从slave库 ...

  2. Spring AOP 实现数据库读写分离

    背景 我们一般应用对数据库而言都是"读多写少",也就说对数据库读取数据的压力比较大,有一个思路就是说采用数据库集群的方案, 其中一个是主库,负责写入数据,我们称之为:写库: 其它都 ...

  3. LinkedHashMap+Spring Aop实现简易的缓存系统

    之前介绍说要做在线文库的系统,当数据量大的时候,根据标签tag的对文档信息的查询将是一个很耗时的工作,原来分析LinkedHashMap源码的时候了解到它有一个双向链表的结构,可以通过将刚被访问的元素 ...

  4. Spring AOP就是这么简单啦

    前言 只有光头才能变强 上一篇已经讲解了Spring IOC知识点一网打尽!,这篇主要是讲解Spring的AOP模块~ 之前我已经写过一篇关于AOP的文章了,那篇把比较重要的知识点都讲解过了一篇啦:S ...

  5. Comparing Spring AOP and AspectJ

    AOP 概念 在我们开始之前 , 让我们做一个快速.高级别审查的核心术语和概念 : 方面 — —标准 / 特征代码被分散在多个场所中的应用 , 通常不同于实际的业务逻辑 (例如 , 交易管理) .各方 ...

  6. 比较 Spring AOP 与 AspectJ

    本文翻译自博客Comparing Spring AOP and AspectJ(转载:https://juejin.im/post/5a695b3cf265da3e47449471) 介绍 如今有多个 ...

  7. spring AOP 之一:spring AOP功能介绍

    一.AOP简介 AOP:是一种面向切面的编程范式,是一种编程思想,旨在通过分离横切关注点,提高模块化,可以跨越对象关注点.Aop的典型应用即spring的事务机制,日志记录.利用AOP可以对业务逻辑的 ...

  8. spring Aop概念

    面向切面编程(AOP)通过提供另外一种思考程序结构的途经来弥补面向对象编程(OOP)的不足.在OOP中模块化的关键单元是类(classes),而在AOP中模块化的单元则是切面.切面能对关注点进行模块化 ...

  9. Spring AOP初步总结(一)

    学习AOP有段时间了,一直没空总结一下,导致有些知识点都遗忘了,之后会把以前学过的Spring核心相关的知识点总结一轮... 先大体介绍下Spring AOP的特点(均摘自"Spring i ...

随机推荐

  1. 1、C语言中的函数指针

    一 通常的函数调用 void MyFun(int x); //此处的申明也可写成:void MyFun( int ); int main(int argc, char* argv[]) { MyFun ...

  2. docker no permmition problem

    resolved by: sudo docker run --privileged ....

  3. Fortify对移动应用安全的支持

    Fortify对移动应用安全的支持http://www.docin.com/p-768827684.html

  4. 项目中 mysql中的内容关于上架时间和下架时间

    隐藏左边导航 在mysql中,是存放的10位的时间截,在后台添加时,时间的格式是:'Y-m-d H:i',没有秒的 字段 字段名称 字段类型 是否为空 默认值 备注 publish_up int(11 ...

  5. js内置对象处理-打印学生成绩单

    效果图: 任务: 1.通过js的内置对象得到当前日期 var date=new Date(); var year=date.toString().slice(11,15); document.writ ...

  6. JAXB - The Object Factory

    Usually hidden in the middle of the list of the classes derived from the types defined in an XML sch ...

  7. 用JQuery中的Ajax方法获取web service等后台程序中的方法

    用JQuery中的Ajax方法获取web service等后台程序中的方法 1.准备需要被前台html页面调用的web Service,这里我们就用ws来代替了,代码如下: using System; ...

  8. PetaPoco更新记录方法

    /// <summary> /// Performs an SQL update /// </summary> /// <param name="tableNa ...

  9. NSdata 与 NSString,Byte数组,UIImage 的相互转换

    1. NSData 与 NSString NSData-> NSString NSString *aString = [[NSString alloc] initWithData:adataen ...

  10. iOS block的使用

    明明知道block是一个很重要的知识点,很久不用就又忘了,这是在网上看到的一个例子.(晚上回去整理另外的一个) 在视图A上有一个按钮(用来在点击的时候推出视图b)和一个label(用来显示从b传回来的 ...