• Spring缓存机制(转)


    Spring的缓存机制非常灵活,可以对容器中任意Bean或者Bean的方法进行缓存,因此这种缓存机制可以在JavaEE应用的任何层次上进行缓存。

    Spring缓存底层也是需要借助其他缓存工具来实现,例如EhCache(Hibernate缓存工具),上层则以统一API编程。

    要使用Spring缓存,需要以下三步

    • 1.向Spring配置文件导入context:命名空间
    • 2.在Spring配置文件启用缓存,具体是添加 <cache:annotation-driven cache-manager="缓存管理器ID" />
    • 3.配置缓存管理器,不同的缓存实现配置不同,如果是EhCache,需要先配置一个ehcache.xml

    例如

    复制代码
     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <ehcache>
     3     <diskStore path="java.io.tmpdir" />
     4     <!-- 配置默认的缓存区 -->
     5     <defaultCache
     6         maxElementsInMemory="10000"
     7         eternal="false"
     8         timeToIdleSeconds="120"
     9         timeToLiveSeconds="120"
    10         maxElementsOnDisk="10000000"
    11         diskExpiryThreadIntervalSeconds="120"
    12         memoryStoreEvictionPolicy="LRU"/>
    13     <!-- 配置名为users的缓存区 -->
    14     <cache name="users"
    15         maxElementsInMemory="10000"
    16         eternal="false"
    17         overflowToDisk="true"
    18         timeToIdleSeconds="300"
    19         timeToLiveSeconds="600" />
    20 </ehcache>
    复制代码

    上面的ehcache.xml配置了两个缓存区,Spring中的Bean将会缓存在这些缓存区中,一般的,Spring容器中有多少个Bean,就会在ehcache中定义多少个缓存区。

    接着在Spring配置文件中配置缓存管理器如下,其中第一个Bean是一个工厂Bean,用来配置EhCache的CacheManager, 第二个Bean才是为Spring缓存配置的缓存管理器,所以将第一个Bean注入第二个Bean。

    复制代码
     1     <cache:annotation-driven cache-manager="cacheManager" />
     2 
     3     <!-- 配置EhCache的CacheManager
     4     通过configLocation指定ehcache.xml文件的位置 -->
     5     <bean id="ehCacheManager"
     6         class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"
     7         p:configLocation="classpath:ehcache.xml"
     8         p:shared="false" />
     9     <!-- 配置基于EhCache的缓存管理器
    10     并将EhCache的CacheManager注入该缓存管理器Bean -->
    11     <bean id="cacheManager"
    12         class="org.springframework.cache.ehcache.EhCacheCacheManager"
    13         p:cacheManager-ref="ehCacheManager" > 
    14     </bean>
    复制代码

    下面是一个完整的Spring配置,

    复制代码
     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans xmlns="http://www.springframework.org/schema/beans"
     3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     4     xmlns:p="http://www.springframework.org/schema/p"
     5     xmlns:cache="http://www.springframework.org/schema/cache"
     6     xmlns:context="http://www.springframework.org/schema/context"
     7     xsi:schemaLocation="http://www.springframework.org/schema/beans 
     8     http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
     9     http://www.springframework.org/schema/cache
    10     http://www.springframework.org/schema/cache/spring-cache-4.0.xsd
    11     http://www.springframework.org/schema/context
    12     http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    13 
    14     <context:component-scan 
    15         base-package="com.service"/>
    16         
    17     <cache:annotation-driven cache-manager="cacheManager" />
    18 
    19     <!-- 配置EhCache的CacheManager
    20     通过configLocation指定ehcache.xml文件的位置 -->
    21     <bean id="ehCacheManager"
    22         class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"
    23         p:configLocation="classpath:ehcache.xml"
    24         p:shared="false" />
    25     <!-- 配置基于EhCache的缓存管理器
    26     并将EhCache的CacheManager注入该缓存管理器Bean -->
    27     <bean id="cacheManager"
    28         class="org.springframework.cache.ehcache.EhCacheCacheManager"
    29         p:cacheManager-ref="ehCacheManager" > 
    30     </bean>
    31     
    32 </beans>
    复制代码

    下面将以@Cacheable为例,演示Spring基于EhCache缓存的用法。@Cacheable用于修饰类或者方法,如果修饰类,则类中所有方法都会被缓存。

    类级别的缓存

    例如有如下Bean类,

    复制代码
     1 @Service("userService")
     2 @Cacheable(value="users")
     3 public class UserServiceImpl implements UserService {
     4 
     5     @Override
     6     public User getUsersByNameAndAge(String name, int age) {
     7         System.out.println("正在执行getUsersByNameAndAge()..");
     8         return new User(name,age);
     9     }
    10 
    11     @Override
    12     public User getAnotherUser(String name, int age) {
    13         System.out.println("正在执行getAnotherUser()..");
    14         return new User(name,age);
    15     }
    16 }
    复制代码

    基于类的缓存,将会缓存类中的所有方法,缓存之后,程序调用该类实例的任何方法,只要传入的参数相同,Spring将不会真正执行该方法,而是直接根据传入的参数去查找缓存中的数据!

    比如像下面这样使用缓存数据,

    复制代码
    1     public static void test2() {
    2         ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
    3         UserService us = ctx.getBean("userService", UserService.class);
    4         User u1 = us.getUsersByNameAndAge("张三", 50);
    5         //由于第二次调用userService方法时,使用了相同参数,那么真正的方法将不会执行,
    6         //Spring将直接从缓存按参数查找数据
    7         User u2 = us.getAnotherUser("张三", 50);
    8         System.out.println(u1==u2);
    9     }
    复制代码

    输出结果,

    1 正在执行getUsersByNameAndAge()..
    2 true

    可以看到,上面的getAnotherUser()并没有真正执行,因为传入的参数与之前的方法传入的参数相同,于是Spring直接从缓存区数据了。

    上面的Bean类中的注解@Cacheable除了必选属性value之外,还有key, condition,, unless属性,后面三个都是用来设置Spring存储策略,对于基于类的缓存来说,Spring默认以方法传入的参数作为key去缓存中查找结果。

    当然我们也可以修改key的策略,让Spring按照其他标准,比如按照第一个参数是否相同来作为key,在缓存中查找结果。

    将上面的Bean类修改如下,

    复制代码
    1 @Service("userService")
    2 @Cacheable(value="users", key="#name")
    3 public class UserServiceImpl implements UserService {
    4 
    5     @Override
    6     public User getUsersByNameAndAge(String name, int age) {
    复制代码

    意味着我们传入相同的name,Spring就不会真正执行方法。只有name不同的时候,方法才会真正执行,例如下面,

    复制代码
    1     public static void test2() {
    2         ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
    3         UserService us = ctx.getBean("userService", UserService.class);
    4         User u1 = us.getUsersByNameAndAge("张三", 50);
    5         //将@Cacheable的key参数改为key="#name"之后,下面的方法将可以执行。
    6         User u2 = us.getAnotherUser("李四", 50);
    7         System.out.println(u1==u2);
    8     }
    复制代码

    可以看到这回getAnotherUser()方法得到执行了,

    1 正在执行getUsersByNameAndAge()..
    2 正在执行getAnotherUser()..
    3 false

    我们也可以设置condition属性,例如,

    复制代码
    1 @Service("userService")
    2 @Cacheable(value="users", condition="#age<100")
    3 public class UserServiceImpl implements UserService {
    4 
    5     @Override
    6     public User getUsersByNameAndAge(String name, int age) {
    复制代码

    那么对于下面的代码来说,两个方法都不会被缓存,Spring每次都是执行真正的方法取结果,

    复制代码
    1     public static void test2() {
    2         ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
    3         UserService us = ctx.getBean("userService", UserService.class);
    4         User u1 = us.getUsersByNameAndAge("张三", 500);
    5         User u2 = us.getAnotherUser("李四", 500);
    6         System.out.println(u1==u2);
    7     }
    复制代码

    执行结果,

    1 正在执行getUsersByNameAndAge()..
    2 正在执行getAnotherUser()..
    3 false

    方法级别的缓存

    方法级别的缓存则只会对方法起作用了,不同的方法可以设置不用的缓存区,例如下面这样,

    复制代码
     1 @Service("userService")
     2 public class UserServiceImpl implements UserService {
     3 
     4     @Cacheable("users1")
     5     @Override
     6     public User getUsersByNameAndAge(String name, int age) {
     7         System.out.println("正在执行getUsersByNameAndAge()..");
     8         return new User(name,age);
     9     }
    10 
    11     @Cacheable("users2")
    12     @Override
    13     public User getAnotherUser(String name, int age) {
    14         System.out.println("正在执行getAnotherUser()..");
    15         return new User(name,age);
    16     }
    17 }
    复制代码

    使用下面的测试代码,

    复制代码
     1     public static void test2() {
     2         ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
     3         UserService us = ctx.getBean("userService", UserService.class);
     4         //第一次执行方法,方法将会真正执行并缓存
     5         User u1 = us.getUsersByNameAndAge("张三", 500);
     6         //虽然下面方法传入相同参数,但是因为这两个方法在不同的缓存区,所以无法使用缓存数据
     7         User u2 = us.getAnotherUser("张三", 500);
     8         System.out.println(u1==u2);
     9         //上面已经缓存过,这里不会真正执行,直接使用缓存
    10         User u3 = us.getAnotherUser("张三", 500);
    11         System.out.println(u3==u2);
    12     }
    复制代码

    执行结果,

    1 正在执行getUsersByNameAndAge()..
    2 正在执行getAnotherUser()..
    3 false
    4 true

    使用@CacheEvict清除缓存

    被@CacheEvict修饰的方法可以用来清除缓存,使用@CacheEvict可以指定如下属性。

    allEntries, 是否清空整个缓存区

    beforeInvocation: 是否在执行方法之前清除缓存。默认是方法执行成功之后才清除。

    condiition以及key, 与@Cacheable中一样的含义。

    下面示范简单用啊,

    复制代码
     1 @Service("userService")
     2 @Cacheable("users")
     3 public class UserServiceImpl implements UserService {
     4 
     5     @Override
     6     public User getUsersByNameAndAge(String name, int age) {
     7         System.out.println("正在执行getUsersByNameAndAge()..");
     8         return new User(name,age);
     9     }
    10 
    11     @Override
    12     public User getAnotherUser(String name, int age) {
    13         System.out.println("正在执行getAnotherUser()..");
    14         return new User(name,age);
    15     }
    16     //指定根据name,age参数清楚缓存
    17     @CacheEvict(value="users")
    18     public void evictUser(String name, int age) {
    19         System.out.println("--正在清空"+name+","+age+"对应的缓存--");
    20     }
    21     
    22     //指定清除user缓存区所有缓存的数据
    23     @CacheEvict(value="users", allEntries=true)
    24     public void evictAll() {
    25         System.out.println("--正在清空整个缓存--");
    26     }
    27 }
    复制代码

    下面是测试类,

    复制代码
     1     public static void test2() {
     2         ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
     3         UserService us = ctx.getBean("userService", UserService.class);
     4         //系统会缓存两个方法
     5         User u1 = us.getUsersByNameAndAge("张三", 500);
     6         User u2 = us.getAnotherUser("李四",400);
     7         //调用evictUser()方法清除缓冲区指定的数据
     8         us.evictUser("李四", 400);
     9         //前面清除了  李四, 400 的缓存,下面的方法返回的数据将会再次被缓存
    10         User u3 = us.getAnotherUser("李四", 400);
    11         System.out.println(us == u3);    //false
    12         //前面已经缓存了 张三, 500的数据,下面方法不会重新执行,直接取缓存中的数据
    13         User u4 = us.getAnotherUser("张三", 500);
    14         System.out.println(u1==u4); //输出true
    15         //清空整个缓存
    16         us.evictAll();
    17         //由于整个缓存都已经被清空,下面的代码都会被重新执行
    18         User u5 = us.getAnotherUser("张三", 500);
    19         User u6 = us.getAnotherUser("李四", 400);
    20         System.out.println(u1==u5); //输出false
    21         System.out.println(u3==u6); //输出false
    22     }
    复制代码

    执行结果,

    复制代码
     1 正在执行getUsersByNameAndAge()..
     2 正在执行getAnotherUser()..
     3 --正在清空李四,400对应的缓存--
     4 正在执行getAnotherUser()..
     5 false
     6 true
     7 --正在清空整个缓存--
     8 正在执行getAnotherUser()..
     9 正在执行getAnotherUser()..
    10 false
    11 false
    复制代码
  • 相关阅读:
    node js 修改js代码自动发布到服务器
    NodeJS创建 HTTP 服务器
    node.js安装环境的检查和配置
    JQuery中extend使用
    DataTable 转换为List
    sql 修改表结构 schema
    iOS自动处理键盘事件的第三方库:IQKeyboardManager
    IOS8下的远程推送(转载)
    OC中运行出错( Unknown class <XXX> in InterfaceBuilder file.) 解决办法
    UISegmentedControl
  • 原文地址:https://www.cnblogs.com/weizhxa/p/12066043.html
Copyright © 2020-2023  润新知