利用Spring的AOP来配置和管理你的二级缓存(EHCache)

简介:

利用Spring的AOP来配置和管理你的二级缓存(EHCache)

       如果我们的项目中采用的是Spring+hibernate来构建的,在缓存方面,我们一定会首先想到Spring自带的EHCache缓存工具,在Spring中集成了目前比较流行的缓存策略EHCache,现在用的比较多的还有像OSCache,MemCached.这些应该是当前用的最多的缓存工具了。
       在Spring+hibernate的这样的框架中,EHCache应该属于二级缓存了,我们知道在Hibernate中已经默认的使用了一级缓存,也就是在Session中。二级缓存应该是SessionFactory的范围了。二级缓存默认不会起作用的,这就需要我们简单的配置一下就可以了。
       在配置之前,我先说明一点,缓存从理论上来说是可以提高你网站系统的性能,但前提就是你要保证你有一个良好的架构设计。比如用Spring+Hibernate构建的系统,如果用单个服务器,用Spring自带的EHCache来做二级缓存是再好不过了。如果你的系统是分布式的系统,有多台服务器,那么MemCached是最好的选择了,一般来说MemCached在做缓存这一块,要比EHCache和OSCache的性能要好点,但是并不是所有的网站用MemCached都能达到事半功倍的,它虽然是比较好,但它有一个前提,那就是你有多台服务器,是分布式的。这样用MemCached对系统的性能一定OK。因为Memcached是“分布式”的内存对象缓存系统,那么就是说,那些不需要“分布”的,不需要共享的,或者干脆规模小到只有一台服务器的应用, MemCached不会带来任何好处,相反还会拖慢系统效率,因为网络连接同样需要资源  .OSCache这个缓存机制的限制就比较少了。它和EHCache差不多。
        在Spring+Hibernate中整合EHCache只需简单的三步。
        第一步:配置缓存文件ehcache.xml,默认放到src目录下。下面是简单的配置。
   <ehcache>
    <!—设置缓存文件 .data 的创建路径。
         如果该路径是 Java 系统参数,当前虚拟机会重新赋值。
         下面的参数这样解释:
         user.home – 用户主目录
         user.dir      – 用户当前工作目录
         java.io.tmpdir – 默认临时文件路径,就是在tomcat的temp目录 -->
    <diskStore path="java.io.tmpdir"/>

    <!—缺省缓存配置。CacheManager 会把这些配置应用到程序中。
        下列属性是 defaultCache 必须的:
        maxInMemory           - 设定内存中创建对象的最大值。
        eternal                        - 设置元素(译注:内存中对象)是否永久驻留。如果是,将忽略超
                                              时限制且元素永不消亡。
        timeToIdleSeconds  - 设置某个元素消亡前的停顿时间。
                                              也就是在一个元素消亡之前,两次访问时间的最大时间间隔值。
                                              这只能在元素不是永久驻留时有效(译注:如果对象永恒不灭,则
                                              设置该属性也无用)。
                                              如果该值是 0 就意味着元素可以停顿无穷长的时间。
        timeToLiveSeconds - 为元素设置消亡前的生存时间。
                                               也就是一个元素从构建到消亡的最大时间间隔值。
                                               这只能在元素不是永久驻留时有效。
        overflowToDisk        - 设置当内存中缓存达到 maxInMemory 限制时元素是否可写到磁盘
                                               上。
        -->
     <!--timeToLiveSeconds的时间一定要大于等于timeToIdleSeconds的时间按-->
    <cache name="DEFAULT_CACHE"
        maxElementsInMemory="1000"
        eternal="false"
        timeToIdleSeconds="500"
        timeToLiveSeconds="500"
        overflowToDisk="true"
        />
</ehcache>
  上面有一个默认的缓存配置,还有一个我们自己配置的缓存,在应用程序中如果不指明缓存的话,就会默认的使用默认的配置属性。
    第二步:用Spring中的强大机制,面向切面的设计AOP.来编写两个类文件,MethodCacheAfterAdvice.java(主要是对脏东西的同步更新)和MethodCacheInterceptor.java(主要使用拦截器来为要缓存的对象建立缓存并缓存)。拦截器的实现机制其实就是我们常用的过滤器。它和过滤器的工作原理一样。以下是这两个文件。
MethodCacheInterceptor.java
 public class MethodCacheInterceptor implements MethodInterceptor,
  InitializingBean {
 private static final Log logger = LogFactory
   .getLog(MethodCacheInterceptor.class);
 private Cache cache;
 public void setCache(Cache cache) {
  this.cache = cache;
 }
 public MethodCacheInterceptor() {
  super();
 }
 /**
  * 拦截Service/DAO的方法,并查找该结果是否存在,如果存在就返回cache中的值, 否则,返回数据库查询结果,并将查询结果放入cache
  */
 public Object invoke(MethodInvocation invocation) throws Throwable {
  String targetName = invocation.getThis().getClass().getName();
  String methodName = invocation.getMethod().getName();
  Object[] arguments = invocation.getArguments();
  Object result;
  logger.debug("Find object from cache is " + cache.getName());
  String cacheKey = getCacheKey(targetName, methodName, arguments);
  Element element = cache.get(cacheKey);
  long startTime = System.currentTimeMillis();
  if (element == null) {
   logger
     .debug("Hold up method , Get method result and create cache........!");
   result = invocation.proceed();
   element = new Element(cacheKey, (Serializable) result);
   cache.put(element);
   long endTime = System.currentTimeMillis();
   logger.info(targetName + "." + methodName + " 方法被首次调用并被缓存。耗时"
     + (endTime - startTime) + "毫秒" + " cacheKey:"
     + element.getKey());
  } else {
   long endTime = System.currentTimeMillis();
   logger.info(targetName + "." + methodName + " 结果从缓存中直接调用。耗时"
     + (endTime - startTime) + "毫秒" + " cacheKey:"
     + element.getKey());
  }
  return element.getValue();
 }
 /**
  * 获得cache key的方法,cache key是Cache中一个Element的唯一标识 cache key包括 包名+类名+方法名+参数
  */
 private String getCacheKey(String targetName, String methodName,
   Object[] arguments) {
  StringBuffer sb = new StringBuffer();
  sb.append(targetName).append(".").append(methodName);
  if ((arguments != null) && (arguments.length != 0)) {
   for (int i = 0; i < arguments.length; i++) {
    sb.append(".").append(arguments[i]);
   }
  }
  return sb.toString();
 }
 /**
  * implement InitializingBean,检查cache是否为空
  */
 public void afterPropertiesSet() throws Exception {
  Assert.notNull(cache,
    "Need a cache. Please use setCache(Cache) create it.");
 }
}
   这个方法实现了两个接口,一个是MethodInterceptor(方法拦截),它主要是在方法的调用前后都可以执行。另一个InitializingBean (初始化Bean)它主要在方法调用之后做一下简单的检查,主要实现写在afterPropertiesSet()中,就可以了 。
MethodCacheAfterAdvice  .java
public class MethodCacheAfterAdvice implements AfterReturningAdvice,
  InitializingBean {
 private static final Log logger = LogFactory
   .getLog(MethodCacheAfterAdvice.class);
 private Cache cache;
 public void setCache(Cache cache) {
  this.cache = cache;
 }
 public MethodCacheAfterAdvice() {
  super();
 }
 public void afterReturning(Object arg0, Method arg1, Object[] arg2,
   Object arg3) throws Throwable {
  String className = arg3.getClass().getName();
  List list = cache.getKeys();
  for (int i = 0; i < list.size(); i++) {
   String cacheKey = String.valueOf(list.get(i));
   if (cacheKey.startsWith(className)) {
    cache.remove(cacheKey);
    logger.debug("remove cache " + cacheKey);
   }
  }
 }
 public void afterPropertiesSet() throws Exception {
  Assert.notNull(cache,
    "Need a cache. Please use setCache(Cache) create it.");
 }
}
  这个方法主要是保证缓存的同步,保持与数据库的数据一致性。
第三步:配置Bean了,applicationContext-ehcache.xml文件就是Spring中的Ioc(控制反转容器)的描述了。上面的只是简单的写了两个方法,具体的能起到什么作用,以及何时起作用,以及怎样用声明式的方式(AOP)和Bean结合。
 <!-- 利用BeanNameAutoProxyCreator自动创建事务代理 -->
 <bean id="transactionInterceptor"
  class="org.springframework.transaction.interceptor.TransactionInterceptor">
  <property name="transactionManager">
   <ref bean="transactionManager" />
  </property>
  <!-- 配置事务属性 -->
  <property name="transactionAttributes">
   <props>
    <prop key="delete*">PROPAGATION_REQUIRED</prop>
    <prop key="update*">PROPAGATION_REQUIRED</prop>
    <prop key="save*">PROPAGATION_REQUIRED</prop>
    <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
    <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
   </props>
  </property>
 </bean>
 
 <!-- 引用ehCache的配置 -->
 <bean id="defaultCacheManager"
  class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
  <property name="configLocation">
   <value>classpath:ehcache.xml</value>
  </property>
 </bean>
 <!-- 定义ehCache的工厂,并设置所使用的Cache name -->
 <bean id="ehCache"
  class="org.springframework.cache.ehcache.EhCacheFactoryBean">
  <property name="cacheManager">
   <ref local="defaultCacheManager" />
  </property>
  <property name="cacheName">
   <value>DEFAULT_CACHE</value>
  </property>
 </bean>
 <!-- find/create cache拦截器 -->
 <bean id="methodCacheInterceptor"
  class="com.w3cs.cache.ehcache.MethodCacheInterceptor">
  <property name="cache">
   <ref local="ehCache" />
  </property>
 </bean>
 <!-- flush cache拦截器 -->
 <bean id="methodCacheAfterAdvice"
  class="com.w3cs.cache.ehcache.MethodCacheAfterAdvice">
  <property name="cache">
   <ref local="ehCache" />
  </property>
 </bean>
 <bean id="methodCachePointCut"
  class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
  <property name="advice">
   <ref local="methodCacheInterceptor" />
  </property>
  <property name="patterns">
   <list>
    <value>.*find.*</value>
    <value>.*get.*</value>
   </list>
  </property>
 </bean>
 <bean id="methodCachePointCutAdvice"
  class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
  <property name="advice">
   <ref local="methodCacheAfterAdvice" />
  </property>
  <property name="patterns">
   <list>
    <value>.*create.*</value>
    <value>.*update.*</value>
    <value>.*delete.*</value>
   </list>
  </property>
 </bean>
 <!-- 自动代理 -->
 <bean id="autoproxy"
  class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
  <!-- 可以是Service或DAO层(最好是针对业务层*Service) -->
  <property name="beanNames">
   <list>
    <value>*DAO</value>
   </list>
  </property>
  <property name="interceptorNames">
   <list>
    <value>methodCachePointCut</value>
    <value>methodCachePointCutAdvice</value>
    <value>transactionInterceptor</value>
   </list>
  </property>
 </bean>
</beans>
        上面我是针对DAO层进行拦截并缓存的,最好是能在业务层进行拦截会更好,你可以根据你的系统具体的设计,如果没有业务层的话,对DAO层拦截也是可以的。拦截采用的是用正规表达式配置的。对find,get的方法只进行缓存,如果 create,update,delete方法进行缓存的同步。对一些频繁的操作最好不要用缓存,缓存的作用就是针对那些不经常变动的操作。
         只需这简单的三部就可以完成EHCache了。最好亲自试一试。我并没有针对里面对方法过细的讲解。其实都很简单,多看看就会明白了。不当之处,敬请原谅。
 
本文出自 “ 魏杰的技术专栏” 博客,请务必保留此出处 http://weijie.blog.51cto.com/340746/68789










本文转自yunlielai51CTO博客,原文链接:http://blog.51cto.com/4925054/1150462,如需转载请自行联系原作者

相关文章
|
5天前
|
Java 开发者 微服务
手写模拟Spring Boot自动配置功能
【11月更文挑战第19天】随着微服务架构的兴起,Spring Boot作为一种快速开发框架,因其简化了Spring应用的初始搭建和开发过程,受到了广大开发者的青睐。自动配置作为Spring Boot的核心特性之一,大大减少了手动配置的工作量,提高了开发效率。
20 0
|
23天前
|
Java API Spring
在 Spring 配置文件中配置 Filter 的步骤
【10月更文挑战第21天】在 Spring 配置文件中配置 Filter 是实现请求过滤的重要手段。通过合理的配置,可以灵活地对请求进行处理,满足各种应用需求。还可以根据具体的项目要求和实际情况,进一步深入研究和优化 Filter 的配置,以提高应用的性能和安全性。
|
10天前
|
XML Java 数据安全/隐私保护
Spring Aop该如何使用
本文介绍了AOP(面向切面编程)的基本概念和术语,并通过具体业务场景演示了如何在Spring框架中使用Spring AOP。文章详细解释了切面、连接点、通知、切点等关键术语,并提供了完整的示例代码,帮助读者轻松理解和应用Spring AOP。
Spring Aop该如何使用
|
15天前
|
Java Spring
[Spring]aop的配置与使用
本文介绍了AOP(面向切面编程)的基本概念和核心思想。AOP是Spring框架的核心功能之一,通过动态代理在不修改原代码的情况下注入新功能。文章详细解释了连接点、切入点、通知、切面等关键概念,并列举了前置通知、后置通知、最终通知、异常通知和环绕通知五种通知类型。
27 1
|
17天前
|
存储 缓存 Java
Spring缓存注解【@Cacheable、@CachePut、@CacheEvict、@Caching、@CacheConfig】使用及注意事项
Spring缓存注解【@Cacheable、@CachePut、@CacheEvict、@Caching、@CacheConfig】使用及注意事项
58 2
|
19天前
|
存储 缓存 监控
配置 Webpack 5 持久化缓存时需要注意哪些安全问题?
【10月更文挑战第23天】通过全面、系统地分析和应对安全问题,能够更好地保障 Webpack 5 持久化缓存的安全,为项目的成功构建和运行提供坚实的安全基础。同时,要保持对安全技术的关注和学习,不断提升安全防范能力,以应对日益复杂的安全挑战。
|
11天前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
24 0
|
1月前
|
消息中间件 缓存 NoSQL
Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。
【10月更文挑战第4天】Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。随着数据增长,有时需要将 Redis 数据导出以进行分析、备份或迁移。本文详细介绍几种导出方法:1)使用 Redis 命令与重定向;2)利用 Redis 的 RDB 和 AOF 持久化功能;3)借助第三方工具如 `redis-dump`。每种方法均附有示例代码,帮助你轻松完成数据导出任务。无论数据量大小,总有一款适合你。
74 6
|
7天前
|
缓存 NoSQL 关系型数据库
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
本文详解缓存雪崩、缓存穿透、缓存并发及缓存预热等问题,提供高可用解决方案,帮助你在大厂面试和实际工作中应对这些常见并发场景。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
|
9天前
|
存储 缓存 NoSQL
【赵渝强老师】基于Redis的旁路缓存架构
本文介绍了引入缓存后的系统架构,通过缓存可以提升访问性能、降低网络拥堵、减轻服务负载和增强可扩展性。文中提供了相关图片和视频讲解,并讨论了数据库读写分离、分库分表等方法来减轻数据库压力。同时,文章也指出了缓存可能带来的复杂度增加、成本提高和数据一致性问题。
【赵渝强老师】基于Redis的旁路缓存架构