我们正在尝试在Java中创建Cache的基本实现。为此,我们创建了一个注释@Cached,定义为:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Cached {

    <T> Class<T> keyClass();

    <V> Class<V> valueClass();

    String cacheId();
}

这将导致错误:@interface members may not have type parameters。但是,这是必需的,因为我们希望转换返回的方法的值并将其存储在缓存中。我们正在使用AspectJ截获需要缓存其结果的方法调用。
我们可以获得结果的另一种方式是什么?由于并非所有方法都具有相同的签名,因此我们必须依靠标记为@Cached的方法来实现我们的缓存。

更新
这是将数据存储在缓存中的类:
public class Cache<K extends Serializable, V> {
   private Map<K, CacheEntry<V> cache;
   // Some other fields and accessors
}
CacheEntry类的定义为:
public class CacheEntry<V> {
   private V value;
   // Some other fields used for invalidating the cache entry and accessors
}

现在,当访问缓存时,我想做这样的事情:
cache.getCache().values().stream()
        .map(value -> cached.valueClass().cast(value))
        .collect(Collectors.toList());

在上面的代码中,cached是方法中使用的@Cached批注的引用,如下所示:
@Cached(keyClass = Long.class, valueClass = Person.class, cacheId = "personCache")
List<Person> findAll();

最佳答案

在这种情况下,cached.valueClass().cast(value)无法为您提供帮助,主要是因为编译器无法强制cached.valueClass()Cached.valueClass()兼容。

使用类型转换帮助缓存客户端的最佳方法是让它们在读取时指定目标类型:

public <V> List<V> getAllCachedValues(Class<V> valueClass) {
    return cache.getCache().values().stream()
    .map(value -> valueClass.cast(value))
    .collect(Collectors.toList());
}

或与此未经检查的演员:
public <V> List<V> getAllCachedValues() {
    return cache.getCache().values().stream()
    .map(value -> (V) value)
    .collect(Collectors.toList());
}

以下是一些用法,用于说明尝试使用注释强制执行类型安全性是多么不现实:
  • 您不能在返回类型和注释元数据之间强制兼容:
    @Cached(keyClass = Long.class, valueClass = Person.class, cacheId = "personCache")
    List<Banana> findAll();
    
  • 客户端可以选择与实际缓存实例关联的TV(否则,这将毫无意义,对吧?)。话虽如此,这是可能的:
    @Cached(keyClass = Long.class, valueClass = Person.class, cacheId = "personCache")
    
    //and then
    Cache<Long, Banana> cacheInstance = cacheManager.getCache("personCache");
    

    当然,您可以将Class参数添加到getCache:
    Cache<Long, Banana> cacheInstance = cacheManager.getCache("personCache",
                        Long.class, Banana.class);
    

    并使用实际的Class实例进行验证。但是,当您可以采用第一种方法(在检索时进行广播)时,这样做的目的是什么?最重要的是,调用者最终将确保类型兼容性,而注释元数据则无济于事。

  • 我认为,如果您要将缓存的值序列化为JSON和XML等格式,并且需要原始类来在缓存被命中时反序列化它们(当然,希望客户端进行转换),那么@Cached(keyClass = Long.class, valueClass = Person.class, cacheId = "personCache")可能会有助于输入。

    关于java - 使用Class <T>作为Java中注释的属性,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/58518232/

    10-10 19:22