浅谈缓存-注解驱动的缓存 Spring cache介绍

本文介绍了Spring Cache中的@Cacheable、@CachePut和@CacheEvict注解,详细阐述了它们的作用和配置方法,并通过原始方法调用图和动态代理调用图进行辅助理解。
摘要由CSDN通过智能技术生成
在我们平常的工作当中,有好多场景需要用到缓存技术,如redis,ehcache等来加速数据的访问。作为浅谈缓存的第一篇笔者不想谈论具体的缓存技术,我更想介绍一下Spring中每次阅读都会使我心中泛起波澜的一个东西,那就是基于注解的缓存技术。我们先看Spring参考文档中的一句话。
Since version 3.1, Spring Framework provides support for transparently adding caching into an existing Spring application. Similar to the  transaction  support, the caching abstraction allows consistent use of various caching solutions with minimal impact on the code.
简单翻译一下,基于注解的缓存技术是Spring3.1版本引入的,其目的是为了给一个已有的Spring应用最小侵入的引入缓存。其原理与Spring的事务类似都是基于Spring的AOP技术。他本质上不是一个具体的缓存实现方案(例如redis EHCache等),而是一个对缓存使用的抽象,通过在既有代码中添加少量的annotation,即能达到缓存方法返回对象的效果。她支持开箱即用的缓存临时存储方案,也支持集成主流的专业缓存,如:redis,EHCache。好,废话少说,下面笔者通过几个简单例子来总结看看Spring Cache的优点,并通过源码对其原理做一个简单介绍。

  注意:笔者的代码都是基于Spring boot 1.5.9.release版本,代码的风格也都是Spring boot式的。

先建一个基于Spring Boot 的web项目,本文的编程用例都会在此项目中完成。项目的pom.xml文件如下。

<? xml version ="1.0" encoding ="UTF-8" ?>
    <modelVersion>4.0.0 </modelVersion>

    <groupId>com.snow.cache </groupId>
    <artifactId>snow-cache </artifactId>
    <version>0.0.1-SNAPSHOT </version>
    <packaging>jar </packaging>

    <name>snow-cache </name>
    <description>Demo for Spring Cache </description>

    <parent>
        <groupId>org.springframework.boot </groupId>
        <artifactId>spring-boot-starter-parent </artifactId>
        <version>1.5.9.RELEASE </version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8 </project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8 </project.reporting.outputEncoding>
        <java.version>1.8 </java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot </groupId>
            <artifactId>spring-boot-starter-web </artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot </groupId>
            <artifactId>spring-boot-starter-test </artifactId>
            <scope>test </scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot </groupId>
                <artifactId>spring-boot-maven-plugin </artifactId>
            </plugin>
        </plugins>
    </build>
</project>

partI.传统缓存的用法
在聊Spring Cache之前,我们先来看看我们以前是怎么使用缓存的。这里笔者不打算引入任何第三方缓存而是自己实现一个简易的缓存。
案例:电商的场景中,商品信息需要经常被用户浏览,不做缓存数据库肯定吃不消。我们就来对商品信息的查询做缓存,以商品信息的编号为key,商品信息对象为value,当以相同的编号查询商品时,若缓存中有,直接从缓存中返回结果,否则从数据库中查询,并将查询结果放入缓存。

先定义一个产品实体类代码如下:
package com.snow.cache ;

import java.io.Serializable ;

/**
*
* @author snowwolf-louis
* @date 18/4/10
*/
public class Product implements Serializable{
    private Long id ;
    private String name ;
    private String desc ;

    public Long getId() {
        return id ;
    }

    public void setId(Long id) {
        this. id = id ;
    }

    public String getName() {
        return name ;
    }

    public void setName(String name) {
        this. name = name ;
    }

    public String getDesc() {
        return desc ;
    }

    public void setDesc(String desc) {
        this. desc = desc ;
    }

    @Override
    public String toString() {
        return "Product{" +
                "id=" + id +
                ", name='" + name + ' \' ' +
                ", desc='" + desc + ' \' ' +
                '}' ;
    }
}

在定义一个缓存管理器,负责缓存逻辑,支持对象的增加,修改和删除。代码如下:
package com.snow.cache.tradition ;

import java.util.Map ;
import java.util.concurrent.ConcurrentHashMap ;

/**
*
* @author snowwolf-louis
* @date 18/4/10
*/
public class SimpleCacheManager< T> {
    /**
     * 用ConcurrentHashMap做为缓存
     */
    private Map<String , T> cache = new ConcurrentHashMap<String , T>() ;

    /**
     * 从缓存中获取给定key的值
     * @param key
     * @return 返回缓存中指定key的值,若无则返回null
     */
    public T get(String key){
        return cache.get(key) ;
    }

    /**
     * 将数据(key,value)放入缓存
     * @param key
     * @param value
     */
    public void put(String key , T value){
        cache.put(key ,value) ;
    }

    /**
     * 清除缓存中指定key的的数据
     * @param key
     */
    public void evict(String key){
        if ( cache.containsKey(key)){
            cache.remove(key) ;
        }
    }

    /**
     * 清空缓存
     */
    public void clearAll(){
        cache.clear() ;
    }
}

将我们自定义的缓存管理器交给Spring容器管理

package com.snow.cache.tradition.config ;

import com.snow.cache.Product ;
import com.snow.cache.tradition.SimpleCacheManager ;
import org.springframework.context.annotation.Bean ;
import org.springframework.context.annotation.Configuration ;

/**
* 传统缓存用法的配置类
* @author snowwolf-louis
* @date 18/4/10
*/
@Configuration
public class SimpleCacheConfig {

    @Bean
    public SimpleCacheManager<Product> cacheManager(){
        return new SimpleCacheManager<Product>() ;
    }
}


对商品的业务操作接口

package com.snow.cache.tradition.service ;

import com.snow.cache.Product ;

/**
* 对商品的业务操作接口
* @author snowwolf-louis
* @date 18/4/10
*/
public interface ProductService {

    /**
     * 根据id查询商品信息
     * @param id
     * @return
     */
    public Product getProductById(Long id) ;

}

对商品的业务操作实现

package com.snow.cache.tradition.service ;

import com.snow.cache.Product ;
import com.snow.cache.tradition.SimpleCacheManager ;
import org.springframework.beans.factory.annotation.Autowired ;
import org.springframework.stereotype.Service ;

import java.util.Objects ;


/**
* @author snowwolf-louis
* @date 18/4/10
*/
@Service
public class ProductServiceImpl implements ProductService {

    @Autowired
    private SimpleCacheManager<Product> cacheManager ;

    @Override
    public Product getProductById(Long id) {

        if (Objects. isNull(id)){
            throw new NullPointerException( "id不能为空") ;
        }

        Product product = null;
        //首先从缓存查询
        product = cacheManager.get(id.toString()) ;
        if (product != null) {
            return product ;
        }

        //缓存中没有在从数据库中查找,我这里就不从数据库中查了,而是调用一个私有方法模拟从数据库中查
        product = getproductFromDB(id) ;

        //将数据库中查到数据装载进缓存
        if (product != null && product.getId() != null){
            cacheManager.put(product.getId().toString() ,product) ;
        }

        return product ;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值