[solon-data](/article/14) 插件在完成 @Transaction 注解的支持同时，还提供了 @Cache、@CachePut、@CacheRemove 注解的支持；可以为业务开发提供良好的便利性


Solon 的缓存注解只支持：@Controller 、@Remoting 、@Component 注解类下的方法。相对于 Spring Boot ，功能类似；但提供了基于 key 和 tags 的两套管理方案。


* key ：相当于缓存的唯一标识，没有指定时会自动生成（要注意key冲突。自动生成规则：所有参数名与值组合生成的MD5）
* tags：相当于缓存的索引，可以有多个（用于批量删除）


tags 的原理是把相关的 key 收集成一个 List 并存储，所以不能关联太多的 key。否则性能会很差！（一般用于“分页”缓存的批量删除）

### 1、示例

从 Demo 开始，先感受一把

```java
@Controller
public class CacheController {
    /**
     * 执行结果缓存10秒，使用 key=test:${label} 并添加 test 标签 //tags也支持表达式写法：tags=${label}
     * */
    @Cache(key="test:${label}", tags = "test" , seconds = 10)
    @Mapping("/cache/")
    public Object test(int label) {
        return new Date();
    }

    /**
     * 执行后，清除 标签为 test  的所有缓存
     * */
    @CacheRemove(tags = "test")
    @Mapping("/cache/clear")
    public String clear() {
        return "清除成功(其实无效)-" + new Date();
    }

    /**
     * 执行后，更新 key=test:${label}  的缓存
     * */
    @CachePut(key = "test:${label}")
    @Mapping("/cache/clear2")
    public Object clear2(int label) {
        return new Date();
    }
}
```

用在 Service 类上的 Demo：

```java
//比如，加在 Service 类上
@Component
public class AccountService{
    @Db("db2")  
    AccountMapper accountDao;  
    
    @Transaction
    public void addAccount(UserModel user){
        accountDao.insert(user);
    }
    
    //当有缓存，则直接读取缓存；没有，则执行并缓存
    @Cache
    public UserModel getAccount(long userId){
        return accountDao.getById(userId);
    }
}
```

### 2、缓存 key 的模板支持

```java
@Controller
public class CacheController {
    //使用入参
    @Cache(key="test:${label}", seconds = 10)
    @Mapping("/demo1/")
    public Object demo1(int label) {
        return new Date();
    }
    
    //使用入参的属性
    @Cache(key="test:${user.userId}:${map.label}",  seconds = 10)
    @Mapping("/demo2/")
    public Object demo2(UserDto user, Map<String,String> map) {
        return new Date();
    }
    
    //使用返回值（适合 CachePut 和 CacheRemove）
    @CachePut(key="test:${.label}", seconds = 10)
    @Mapping("/demo1/")
    public Object demo1() {
        Map map = new HashMap();
        map.puth("label",1);
        return map;
    }
}
```

### 3、注解说明

**@Cache 注解：**

| 属性 | 说明 | 
| -------- | -------- | 
| service()     | 缓存服务     | 
| seconds()     | 缓存时间     | 
| key()     | 缓存唯一标识     | 
| tags()     | 缓存标签，多个以逗号隔开（为当前缓存块添加标签，用于清除）     | 

**@CachePut 注解：**

| 属性 | 说明 | 
| -------- | -------- | 
| service()     | 缓存服务     | 
| seconds()     | 缓存时间     | 
| key()     | 缓存唯一标识     | 
| tags()     | 缓存标签，多个以逗号隔开（为当前缓存块添加标签，用于清除）     | 


**@CacheRemove 注解：**

| 属性 | 说明 | 
| -------- | -------- | 
| service()     | 缓存服务     | 
| keys()     | 缓存唯一标识，多个以逗号隔开      |
| tags()     | 缓存标签，多个以逗号隔开（方便清除一批key）     | 



### 4、定制分布式缓存

Solon 的缓存标签，是通过CacheService接口提供支持的（默认内置了 LocalCacheService）。定制起来也相当的方便，比如：对接Memcached...

#### a. 了解 CacheService 接口：

```java
public interface CacheService {
    //保存
    void store(String key, Object obj, int seconds);

    //获取
    Object get(String key);

    //移除
    void remove(String key);
}
```

#### b. 定制基于 Memcached 缓存服务：

```java
public class MemCacheService implements CacheService{
    private MemcachedClient _cache = null;
    public MemCacheService(Properties props){
        //略...
    }
  
    @Override
    public void store(String key, Object obj, int seconds) {
        if (_cache != null) {
            _cache.set(key, seconds, obj);
        }
    }
    
    @Override
    public Object get(String key) {
        if (_cache != null) {
            return _cache.get(key);
        } else {
            return null;
        }
    }
    
    @Override
    public void remove(String key) {
        if (_cache != null) {
            _cache.delete(key);
        }
    }
}
```

#### c. 通过配置换掉默认的缓存服务（默认的服务为 LocalCacheService）：

```java
@Configuration
public class Config {
    //此缓存，将替代默认的缓存服务
    @Bean
    public CacheService cache(@Inject("${cache}") Properties props) {
        return new MemCacheService(props);
    }
}
```


