Spring缓存
    spring缓存可以对容器中的任意bean或bean的方法增加缓存。
启用spring缓存
   导入cache:命名空间之后,启动spring环境还要两步:
    1)在spring配置文件中添加<cache:annotation-driven cache-manager="缓存管理器ID"/>,该元素指定spring根据注解来启动Bean级别或方法级别的缓存。
     2)针对不同的缓存实现配置对应的缓存管理器。
第一步中,<cache:annotation../>通过cache-manager显示指定容器中缓存管理器ID,改属性默认值是cacheManager,也就是说,如果容器中缓存管理器ID设为cacheManager,则可省略<cache:annotation../>的cache-manager属性。
 
下面是spring内置缓存实现和EHcache缓存实现
一、spring内置缓存实现
      spring内置缓存实现只是一种内存中的缓存,并非真正的缓存实现,因此通常只用于简单的测试环境,实际项目中不建议用。
     Spring内置的缓存实现使用SimpleCacheManager作为缓存管理器,使用SimpleCacheManager配置缓存非常简单,直接在spring容器中配置改Bean,然后通过<property../>驱动改缓存管理器执行setCaches()方法来设置缓存区即可。
     SimpleCacheManager是一种内存中的缓存区,底层直接使用了JDK的ConcurrentMap来实现缓存,SimpleCacheManager使用了ConcurrentMapFactoryBean作为缓存区,每个ConcurrentMapFactoryBean配置一个缓存区。
     以下是spring内置缓存的缓存管理器配置。
     <bean id="cacheManager" class=
         "org.springframework.cache.support.SimpleCacheManager">
         <!-- 配置缓存区-->
         <property name="caches">
               <set>
                      <!--  使用ConcurrentMapFactoryBean配置缓存区,下面列出多个
                              缓存区,P:name用于缓存区指定名字  -->
                      <bean class=
                          "org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean"
                          p:name="default"/>
                      <bean class = 
                           "org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean"
                          p:name="users"/>
            </property>
     </bean>
以上配置了两个缓存区,default、users。缓存区名字很重要,后面使用注解驱动缓存时需要根据缓存区名字来指定将缓存数据放入指定的缓存区。
 
二、EHcache缓存实现
     前期准备:
          将ehcache-core-2.3.4.jar、slf4j-api-1.6.1.jar放到lib目录下
    1)在类路径下添加一个ehcache.xml配置文件。配置如下:
           <ehcache>
 
                 <diskStore path="java.io.tmpdir"/>
                     <defaultCache
                         maxElementsInMemory="10000"
                         eternal="false"
                         overflowToDisk="true"
                         memoryStoreEvictionPolicy="LRU"
                         timeToIdLeSeconds="120"
                         timeToLiveSeconds="120"
                         maxElementsOnDisk="10000000"
                         diskExpiryThreadIntervalSeconds="120"
                         />
    
                     <cache name="users"
                         maxElementsInMemory="10000"
                         eternal="false"
                         overflowToDisk="true"
                         memoryStoreEvictionPolicy="LRU"
                         timeToIdLeSeconds="120"
                         timeToLiveSeconds="120"
                         maxElementsOnDisk="10000000"
                         diskExpiryThreadIntervalSeconds="120"
                 </ehcache>
         以上配置了两个缓存区,一个匿名的、默认的缓存区。一个是名为users的缓存区。
        
         2)、在spring配置文件中添加下配置:
              在<beans>中引入p:命名空间
               xmlns:p="http://www.springframework.org/schema/p"
 
             <!-- 配置Ehcache的CacheManager 
                   通过configLocation指定ehcache.xml文件的位置 -->
             <bean id="ehCacheManager"
                    class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean“
                    p:configLocation="classpath:ehcache.xml"
                    p:shared="false"/>
              <!-- 配置基于EhCache的缓存管理器
                    并将EhCache的CacheManager注入改缓存管理器Bean-->
               <bean id="cacheManager"
                      class="org.springframework.cache.ehcache.EhCacheManager"
                      p:cacheManager-ref="ehCacheManger"/>
           
             上面配置文件中,第一个Bean是一个工厂Bean,它用于配置EhCache的CacheManager;第二个Bean才是spring缓存配置的基于EhCache的缓存管理器,改缓存器需要依赖于ehCacheManager,因此将第一个Bean注入到第二个Bean中。
 
三、使用@Cacheable执行缓存   (以下实例将应用一、二中配置的缓存管理器)
    @Cacheable可用于修饰类或修饰方法。但使用@Cacheable修饰类时,用于告诉Spring在类级别上进行缓存——程序调用该类的任何方法时都需要缓存,而且共享一个缓存区;当使用@Cacheable修饰方法时,只有当程序调用改方法时才需要缓存。
 
1、类级别的缓存
      使用@Cacheable修饰类是,控制spring在类级别进行缓存。这样当程序调用该类的任意方法时,只要传入参数相同,spring就会缓存。
     程序清单
     @Service("userService")
     @Cacheable(value="users")  //@Cacheable(value="users" key="#name")
     public class UserServiceImpl implements UserService{
          public User getusersByNameAndAge(String name,int age){
                 System.out.println("--正在执行getusersByNameAndAge方法--”);
                  return new User(name,age);
          }
          public User getAnotherUser(String name,int age){
              System.out.println("--正在执行getAnotherUser方法--”);
                  return new User(name,age);
           }
    }
   程序清单:
             public class SpringTest{
               public static void main(String[] args){
                    ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config.xml");
                    UserService us = ctx.getBean("userService“,UserService.class);
                    User u1= us.getusersByNameAndAge("孙悟空”,500);
                    User u2= us.getAnotherUser("孙悟空”,500);
                    System.out.println(u1==u2);
               }
执行结果如下:
            --正在执行getusersByNameAndAge方法--
             true
      从结果可看出,程序并未调用getAnotherUser方法。由此可见,类级别的缓存默认以所有的参数作为key来缓存方法返回的数据——同一个类中,不管调用哪个方法,只要调用方法传入的参数相同,spring都会直接利用缓存区中的数据。
       @Cacheable有以下几个属性:
           value:必须属性。用于指定多个缓冲区的名字。指定返回方法的数据放在哪个缓存区。
           key:通过SpEL表达式指定缓存的Key。(调用方法是否需要重复执行,看key。key值默认为所有参数)
          condition:该属性返回一个boolean值的SpEL表达式
 2、类级别的缓存
       使用@Cacheable修饰方法时,就可控制spring在方法级别的缓存,这样当程序调用该方法时,只要传入的参数相同,spring就会使用缓存。
      @Service("userService")
      public  class UserServiceImpl implements UserService{
                 @Cacheable(value="users1")
                 public User getUsersByNameAndAge(String name,int age){
                       System.out.println("--正在执行 getUsersByNameAndAge查询方法--”);
                        return new User(name,age);
                  }
                 @Cacheable(value="user2")
                  public User getAnotherUser(String name,int age){
                            System.out.println("--正在执行 getAnotherUser查询方法--”);
                             return new User(name,age);
                  }
            } 
     程序清单:
             public class SpringTest{
               public static void main(String[] args){
                    ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config.xml");
                    UserService us = ctx.getBean("userService“,UserService.class);
                    User u1= us.getusersByNameAndAge("孙悟空”,500);
                    User u2= us.getAnotherUser("孙悟空”,500);
                    System.out.println(u1==u2);
                   User u3= us.getAnotherUser("孙悟空”,500);
                    System.out.println(u2==u3);
               }
执行结果:
    --正在执行 getUsersByNameAndAge查询方法--
    --正在执行 getAnotherUser查询方法--
    false
    true
三、清除缓存区
       被@CacheEvict注释修饰的方法用于清除缓存,@CacheEvict有如下几个注解:
           value:必须属性,用于指定该方法用于清除哪个缓存区中的数据。
           allEntries:该属性指定是否清除整个缓存区
           beforeInvocation:该属性指定是否在执行方法之前清除缓存。默认是在方法成功完成之后才清缓存。
           condition:只有满足条件是,才清除缓存。
           key:指定key,清除指定缓存的key
           程序清单:
               @Service("userService")
      @Cacheable(value="users")
      public  class UserServiceImpl implements UserService{
                 
                 public User getUsersByNameAndAge(String name,int age){
                       System.out.println("--正在执行 getUsersByNameAndAge查询方法--”);
                        return new User(name,age);
                  }
              
                  public User getAnotherUser(String name,int age){
                            System.out.println("--正在执行 getAnotherUser查询方法--”);
                             return new User(name,age);
                  }
                 @CacheEvict(value="users")
                 public void evictUser(String name,int age){
                          System.out.println("--正在清除”+name+","+age+"对应的缓存区--");
                 }
                @CacheEvict(value="users",allEntries= true)
                 public void evictAll(String name,int age){
                          System.out.println("--正在清除整个缓存区--");
 
                 }
            } 
     程序清单:
             public class SpringTest{
               public static void main(String[] args){
                    ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config.xml");
                    UserService us = ctx.getBean("userService“,UserService.class);
                    User u1= us.getusersByNameAndAge("孙悟空”,500);
                    User u2= us.getAnotherUser("猪八戒”,400);
                   us.evictUser("猪八戒”,400);
                   //"猪八戒”,400缓存区中数据被清除
                     User u3= us.getAnotherUser("猪八戒”,400);
                    System.out.println(u2==u3);
                     User u4= us.getAnotherUser("孙悟空”,500);
  System.out.println(u1==u4);
                     us.evictAll();
                    User u5= us.getAnotherUser("孙悟空”,500);
                    User u6= us.getAnotherUser("猪八戒”,400);
                   System.out.println(u1==u5);
                    System.out.println(u3==u6);
 
               }
 
执行结果:
 --正在执行 getUsersByNameAndAge查询方法--
    --正在执行 getAnotherUser查询方法--
    --正在清除猪八戒,400对应的缓存---
 --正在执行 getAnotherUser查询方法--
    false
     true
      --正在清空整个缓存区--
        --正在执行 getAnotherUser查询方法--
 --正在执行 getAnotherUser查询方法--
   false
   fasle