Spring @CacheEvict使用通配符 [英] Spring @CacheEvict using wildcards

查看:49
本文介绍了Spring @CacheEvict使用通配符的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在@CacheEvict中可以使用通配符吗?

我有一个具有多租户的应用程序,有时需要从租户的缓存中逐出所有数据,而不是从系统中的所有租户的缓存中逐出.

请考虑以下方法:

  @Cacheable(value ="users",key ="T(Security).getTenant()+#user.key")公共列表<用户>getUsers(用户){...} 

所以,我想做些类似的事情:

  @CacheEvict(value ="users",key ="T(Security).getTenant()+ *")public void deleteOrganization(组织机构){...} 

反正有做吗?

解决方案

与宇宙中每个问题的99%一样,答案是:它取决于.如果您的缓存管理器实现了与此相关的功能,那就太好了.但这似乎并非如此.

如果您使用的是 SimpleCacheManager (这是Spring提供的基本的内存缓存管理器),则可能是使用了Spring附带的 ConcurrentMapCache .尽管不可能扩展 ConcurrentMapCache 来处理密钥中的通配符(因为高速缓存存储是私有的,并且您无法访问它),但是您可以将其用作自己实现的灵感./p>

下面有一个可能的实现方式(除了检查它是否正常工作外,我没有做太多其他的测试).这是 ConcurrentMapCache 的纯副本,其中对 evict()方法进行了修改.区别在于,此版本的 evict()会处理密钥以查看它是否为正则表达式.在这种情况下,它会遍历商店中的所有键并逐出与正则表达式匹配的键.

 包com.sigraweb.cache;导入java.io.Serializable;导入java.util.concurrent.ConcurrentHashMap;导入java.util.concurrent.ConcurrentMap;导入org.springframework.cache.Cache;导入org.springframework.cache.support.SimpleValueWrapper;导入org.springframework.util.Assert;公共类RegexKeyCache实现Cache {私有静态最终对象NULL_HOLDER = new NullHolder();私有的最终字符串名称;私有最终ConcurrentMap< Object,Object>店铺;私有最终布尔值allowNullValues;公共RegexKeyCache(字符串名称){this(name,new ConcurrentHashMap< Object,Object>(256),true);}公共RegexKeyCache(字符串名称,boolean allowNullValues){this(name,new ConcurrentHashMap< Object,Object>(256),allowNullValues);}公共RegexKeyCache(字符串名称,ConcurrentMap< Object,Object>存储,布尔值allowNullValues){Assert.notNull(name,名称不能为null");Assert.notNull(store,"Store不能为null");this.name =名称;this.store =商店;this.allowNullValues = allowNullValues;}@Override公共最终字符串getName(){返回this.name;}@Override公共最终ConcurrentMap< Object,Object>getNativeCache(){返回this.store;}public final boolean isAllowNullValues(){返回this.allowNullValues;}@Override公共ValueWrapper get(对象密钥){对象值= this.store.get(key);返回toWrapper(value);}@Override@SuppressWarnings(未选中")公共< T>T get(Object key,Class< T>类型){对象值= fromStoreValue(this.store.get(key));if(value!= null&& type!= null&&!type.isInstance(value)){抛出新的IllegalStateException(缓存的值不是必需的类型[" + type.getName()+]:" +值);}返回(T)值;}@Overridepublic void put(对象键,对象值){this.store.put(key,toStoreValue(value));}@Overridepublic ValueWrapper putIfAbsent(Object key,Object value){存在的对象= this.store.putIfAbsent(key,value);返回包装器(现有);}@Override公共无效evct(对象键){this.store.remove(key);如果(key.toString().startsWith("regex:")){字符串r = key.toString().replace("regex:",");for(对象k:this.store.keySet()){如果(k.toString().matches(r)){this.store.remove(k);}}}}@Override公共无效clear(){this.store.clear();}受保护的对象fromStoreValue(Object storeValue){如果(this.allowNullValues&&storeValue == NULL_HOLDER){返回null;}返回storeValue;}受保护的对象toStoreValue(Object userValue){如果(this.allowNullValues&& userValue == null){返回NULL_HOLDER;}返回userValue;}私有ValueWrapper toWrapper(对象值){return(值!= null?new SimpleValueWrapper(fromStoreValue(value)):null);}@SuppressWarnings(序列")私有静态类NullHolder实现了Serializable {}} 

我相信读者知道如何使用自定义缓存实现来初始化缓存管理器.那里有很多文档,向您展示如何做到这一点.正确配置项目后,您可以正常使用注释,如下所示:

  @CacheEvict(value = {" CacheName},键="'regex:#tenant'+'.*')公共myMethod(字符串租户){...} 

同样,这远未经过适当的测试,但是它为您提供了一种执行所需操作的方法.如果您使用的是其他缓存管理器,则可以类似地扩展其缓存实现.

Is there any way of using wildcards in @CacheEvict?

I have an application with multi-tenancy that sometimes needs to evict all the data from the cache of the tenant, but not of all tenants in the system.

Consider the following method:

@Cacheable(value="users", key="T(Security).getTenant() + #user.key")
public List<User> getUsers(User user) {
    ...
}

So, I would like to do something like:

@CacheEvict(value="users", key="T(Security).getTenant() + *")
public void deleteOrganization(Organization organization) {
    ...
}

Is there anyway to do it?

解决方案

As with 99% of every question in the universe, the answer is: it depends. If your cache manager implements something that deals with that, great. But that doesn't seem to be the case.

If you're using SimpleCacheManager, which is a basic in-memory cache manager provided by Spring, you're probably using ConcurrentMapCache that also comes with Spring. Although it's not possible to extend ConcurrentMapCache to deal with wildcards in keys (because the cache store is private and you can't access it), you could just use it as an inspiration for your own implementation.

Below there's a possible implementation (I didn't really test it much other than to check if it's working). This is a plain copy of ConcurrentMapCache with a modification on the evict() method. The difference is that this version of evict() treats the key to see if it's a regex. In that case, it iterates through all the keys in the store and evict the ones that match the regex.

package com.sigraweb.cache;

import java.io.Serializable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;
import org.springframework.util.Assert;

public class RegexKeyCache implements Cache {
    private static final Object NULL_HOLDER = new NullHolder();

    private final String name;

    private final ConcurrentMap<Object, Object> store;

    private final boolean allowNullValues;

    public RegexKeyCache(String name) {
        this(name, new ConcurrentHashMap<Object, Object>(256), true);
    }

    public RegexKeyCache(String name, boolean allowNullValues) {
        this(name, new ConcurrentHashMap<Object, Object>(256), allowNullValues);
    }

    public RegexKeyCache(String name, ConcurrentMap<Object, Object> store, boolean allowNullValues) {
        Assert.notNull(name, "Name must not be null");
        Assert.notNull(store, "Store must not be null");
        this.name = name;
        this.store = store;
        this.allowNullValues = allowNullValues;
    }

    @Override
    public final String getName() {
        return this.name;
    }

    @Override
    public final ConcurrentMap<Object, Object> getNativeCache() {
        return this.store;
    }

    public final boolean isAllowNullValues() {
        return this.allowNullValues;
    }

    @Override
    public ValueWrapper get(Object key) {
        Object value = this.store.get(key);
        return toWrapper(value);
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> T get(Object key, Class<T> type) {
        Object value = fromStoreValue(this.store.get(key));
        if (value != null && type != null && !type.isInstance(value)) {
            throw new IllegalStateException("Cached value is not of required type [" + type.getName() + "]: " + value);
        }
        return (T) value;
    }

    @Override
    public void put(Object key, Object value) {
        this.store.put(key, toStoreValue(value));
    }

    @Override
    public ValueWrapper putIfAbsent(Object key, Object value) {
        Object existing = this.store.putIfAbsent(key, value);
        return toWrapper(existing);
    }

    @Override
    public void evict(Object key) {
        this.store.remove(key);
        if (key.toString().startsWith("regex:")) {
            String r = key.toString().replace("regex:", "");
            for (Object k : this.store.keySet()) {
                if (k.toString().matches(r)) {
                    this.store.remove(k);
                }
            }
        }
    }

    @Override
    public void clear() {
        this.store.clear();
    }

    protected Object fromStoreValue(Object storeValue) {
        if (this.allowNullValues && storeValue == NULL_HOLDER) {
            return null;
        }
        return storeValue;
    }

    protected Object toStoreValue(Object userValue) {
        if (this.allowNullValues && userValue == null) {
            return NULL_HOLDER;
        }
        return userValue;
    }

    private ValueWrapper toWrapper(Object value) {
        return (value != null ? new SimpleValueWrapper(fromStoreValue(value)) : null);
    }

    @SuppressWarnings("serial")
    private static class NullHolder implements Serializable {
    }
}

I trust that readers know how to initialize the cache manager with a custom cache implementation. There's lots of documentation out there that shows you how to do that. After your project is properly configured, you can use the annotation normally like so:

@CacheEvict(value = { "cacheName" }, key = "'regex:#tenant'+'.*'")
public myMethod(String tenant){
...
}

Again, this is far from being properly tested, but it gives you a way to do what you want. If you're using another cache manager, you could extends its cache implementation similarly.

这篇关于Spring @CacheEvict使用通配符的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆