• Spring Cache


    Spring Cache

    Spring提供一套对外一致的Cache API(Cache抽象),底层Cache的实现和Cache缓存策略由开发者自行维护。Spring Cache提供Cache、CacheManager接口来进行对Cache的抽象,提供 @Cacheable、@CachePut等注解来进行对Cache的使用。

    Spring Cache用于代码级别的缓存,其一般用在service层。在Dao层一般使用ORM缓存,例如Mybatis缓存。在考虑底层实现时,需要根据具体业务来选择不同的实现,简单的总结如下:

    缓存实现 优势 劣势
    ConcurrentMapCache 简单易用、不需要任何的配置 无缓存策略、不支持集群
    EhCache 高性能、高效率、支持多种缓存配置 需要配置ehcache策略、不支持集群
    Redis 与平台无关的缓存数据库,支持集群,有许多优势(百度一下,你就知道了) 与Java平台无关,访问需要socket通信

    由上可见,我们一般在非集群环境下使用EhCahe,集群环境下使用Redis、Memcached等缓存,具体百度。

    Spring Cache 抽象

    Spring实现了以下Cache的抽象,部分Cache实现了对Spring事务的支持,即事务回滚时缓存也回滚,下面除了GuavaCache外都实现了对Spring事务的支持:

    • EhCache:底层Cache由EhCache实现
    • ConcurrentMapCache:底层由ConcurrentMap实现
    • GuavaCache:底层Cache由Google Guava实现
    • CaffeineCache:底层Cache由caffeine Cache实现
    • JCacheCache:底层Cache由javax.cache.Cache实现

    还有其他开源项目也提供了Spring Cache的实现,例如Spring-boot-starter-data-redis提供了Redis Cache的实现。

    Spring Cache、CacheManager接口API:

    public interface Cache {
        //缓存的名字 
        String getName();
        //得到底层使用的缓存,如Ehcache
        Object getNativeCache();
        //根据key得到一个ValueWrapper,然后调用其get方法获取值
        ValueWrapper get(Object key);   
        <T> T get(Object key, Class<T> type);  
        void put(Object key, Object value);
        void evict(Object key);  
        void clear();  
      
        interface ValueWrapper { //缓存值的Wrapper  
            Object get(); //得到真实的value  
        }  
    }
    
    public interface CacheManager {
        //根据Cache名字获取Cache
        Cache getCache(String name);    
        Collection<String> getCacheNames(); 
    }
    

    Spring Cache API使用:

    @Test
    public void testEhcache() throws IOException {
        User user = new User("kanyuxia", "123456");
        net.sf.ehcache.CacheManager ehCacheManager =
            new net.sf.ehcache.CacheManager(new ClassPathResource("ehcache.xml").getInputStream());
        CacheManager cacheManager = new CacheManager(ehCacheManager);
        Cache cache = cacheManager.getCache("user");
        cache.put(user.getUsername, user);
        Assert.assertEquals(user, cache.get(user.getUsername, User.class));
    }
    

    Spring Cache注解

    Spring中主要基于注解+AOP来操作缓存,这样使得使用缓存比较方便但也有一定的局限性,下面是Spring Cache的几个主要注解:

    • @Cacheable:该注解主要用于查询方法上,每次在调用方法时会先从缓存中去取,如果取不到才会执行方法,并把方法结果放入相应的缓存中。
    • @CachePut:该注解主要用于插入、更新方法上,该方法会直接把方法调用结果放入相应的缓存中。
    • @CacheEvit:该注解主要删除方法上。该方法会删除相应的缓存。
    • @Caching:该注解主要用于在方法上需要多个注解,其是多个注解的组合。
    • @CacheConfig:该注解在Spring 4.1后有效,配置在Class上,它为该类上的所有Cache注解提供一系列默认的配置。

    上诉三个注解的参数信息如下:

    • value、cacheNames:用户配置缓存名称,可配置多个。
    • key:缓存的Key,可以使用Spring EL表达式配置。
    • keyGenerator:缓存Key生成器,如果没有配置Key,则会自动调用生成Key。
    • condition、unless:表示执行缓存行为的条件,可以使用Spring EL表达式配置。
    • beforeInvocation:在 @CachePut、@CacheEvit中使用,表示在方法前执行缓存行为,默认为false。
    • allEntries:在 @CacheEvit中使用,表示缓存中的所有Key。

    在key、condition等中可以使用Spring EL表达式,可以更好的在其中写逻辑,下面是Spring Cache提供我们使用的Spring EL表达式:

    方法名 位置 实例 描述
    methodName root对象 #root.methodName 当前方法名
    method root对象 #root.method 当前方法
    target root对象 #root.target 当前调用该方法的对象
    args root对象 #root.args 当前方法的参数数组
    argument name 执行上下文 #id 当前方法的参数
    result 执行上下文 #result 当前方法的返回值,当且仅当缓存在方法调用后执行才有用

    下面是简单的使用:

    @CachePut(cacheNames = "cache", key = "#result.id")
    public CacheEntity addCache(String name) {
        // 省略处理逻辑
        return cacheEntity;
    }
    
    @Cacheable(cacheNames = "cache", key = "#id")
    public CacheEntity selectCacheById(Long id) {
        return cacheMapper.selectById(id);
    }
    
    @CacheEvict(cacheNames = "cache", key = "#id", condition = "#result eq true")
    public boolean deleteCacheById(Long id) {
        return cacheMapper.deleteById(id);
    }
    
    @Caching(
        put = {
            @CachePut(),
            @CachePut()
        }
    )
    public void testCaching() {
    }
    

    Spring Cache注解相关的问题:

    • 不能够自定义缓存值: Spring @CachePut、@Cacheable会自动把返回值放入缓存中,不能够自定义的放入想要的值。
    • 动态代理带来缓存失效问题: Spring Cache使用AOP+注解进行缓存操作,如果我们使用动态代理且方法是内部调用或者非pulic方法则会出现缓存失效问题,例如:
    public void internalCall() {
        this.internalCall("hello");
    }
    
    @Cacheable(cacheNames = "cache", key = "#key")
    public String internalCall(String key) {
        System.out.println("Execute Internal Call Method");
        return "world";
    }
    

    解决方法:使用AspectJ进行AOP处理。

    Spring Cache使用

    • Spring项目
    <bean id="ehCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
        <property name="configLocation" value="classpath:ehcache.xml"/>
    </bean>
    <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
        <property name="cacheManager" ref="ehcacheManager"/>  
        <property name="transactionAware" value="true"/>  
    </bean>
    
    <cache:annotation-driven cache-manager="cacheManager" proxy-target-class="true"/>
    
    • Spring Boot项目
      Spring Boot项目需要在Configuration类上加 @EnableCache注解,然后在yml文件中选择实现哪种类型的CacheManager,之后Spring Boot会自动注入相关CacheManager。

    Spring Cache问题

    在使用Spring Cache和Shiro时,出现了Cache注解失效问题。主要是因为Shiro会使用AOP代理对象,然后Spring Cache也会使用AOP代理对象,由于先使用Shiro代理会出现Spring Cache代理失败问题,所以我们需要先Cache代理然后再Shiro代理。在自定义Realm时注入的service加上 @Lazy注解就可以使得Shiro后于Cache代理。代码如下:

     @Lazy
     @Autowired
     private UserService userService;
    

    Reference

    http://jinnianshilongnian.iteye.com/blog/2001040
    http://blog.chenfu3991.com/post/ehcache-diff-redis.html
    https://www.ibm.com/developerworks/cn/opensource/os-cn-spring-cache/
    https://stackoverflow.com/questions/21512791/spring-service-with-cacheable-methods-gets-initialized-without-cache-when-autowi

  • 相关阅读:
    MySql状态查看方法 MySql如何查看连接数和状态?
    MySQL连接数超过限制的解决方法
    JS正则表达式获取分组内容实例
    jquery data方法获取某个元素上事件
    javascript浮点数转换成整数三种方法
    ThinkPHP CURD方法中field方法详解
    python3.3使用tkinter实现猜数字游戏代码
    Expo大作战(二十四)--expo sdk api之Accelerometer
    Expo大作战(二十三)--expo中expo kit 高级属性(没干货)
    Expo大作战(二十二)--expo分离后的部署(expokit)
  • 原文地址:https://www.cnblogs.com/maying3010/p/8447702.html
Copyright © 2020-2023  润新知