如何防止缓存中的返回值在spring cacheable中更改

时间:2012-05-31 04:01:07

标签: java spring caching ehcache

我正在使用ehcache作为缓存工具来处理spring 3.1注释缓存。

一个像这样的返回值的方法

@Cacheable("cache")
public MyObject getObj(Object param);

我第一次获得了myobject返回值,并且它是可编辑的。 ehcache可以通过设置“copyOnRead”或“copyOnWrite”来为此做些什么。 它会在读/写时强制序列化对象。 但是第一次spring不会从缓存中获取值,它总是通过方法本身返回。

是否有某种方法可以获得只读返回值?

3 个答案:

答案 0 :(得分:3)

您可以编写自己的方面,始终创建返回值的副本,这将使您独立于某些Ehcache设置。

首先,像@CopyReturnValue这样的标记注释很适合表达切入点:

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

现在,方面可以将此注释用于切入点表达式:

@Aspect
@Component
public class CopyReturnValueAspect {
    @Around("@annotation(CopyReturnValue)")
    public Object doCopyReturnValue(ProceedingJoinPoint pjp) throws Throwable {
        Object retVal = pjp.proceed();
        Object copy = BeanUtils.cloneBean(retVal); // create a copy in some way
        return copy;
    }
}

最后,将注释添加到方法中:

@CopyReturnValue
@Cacheable("cache")
public MyObject getObj(Object param);

对于CopyReturnValueAspect,我使用BeanUtils创建返回值的副本 - 仅作为示例。有关该主题的更多信息,您可能需要查看How to copy properties from one Java bean to another?

哦,不要忘记在Spring配置中启用@AspectJ支持,如果你还没有:

<aop:aspectj-autoproxy />

答案 1 :(得分:1)

我遇到了弹簧缓存的同样问题。我不想从缓存中接收相同的java对象。

在我的情况下,我想缓存具有许多字段的大型java对象,依此类推。因此,使用深层复制复制所有数据类非常痛苦。我阅读了有关使用序列化复制java对象的文章。

http://www.javaworld.com/article/2077578/learn-java/java-tip-76--an-alternative-to-the-deep-copy-technique.html

这让我想到只缓存序列化数据。每次从缓存中读取对象时,都会对其进行反序列化。

对于序列化,我使用了apache commons helper方法

@Override
public SerializedQuestions readUserQuestion(UID questionId, Locale locale) {
 byte[] serializedData = readCachedUserQuestion(questionId, locale);
 Object deserializedobject = org.apache.commons.lang.SerializationUtils.deserialize(serializedData);
 return (SerializedQuestions) deserialize;
}

@Override
@Cacheable(value = SpringCacheKeys.USER_QUESTION_CACHE)
  public byte[] readCachedUserQuestion(UID questionId, Locale locale) {

  //read object from db
  SerializedQuestions questions = new SerializedQuestions()

  return org.apache.commons.lang.SerializationUtils.serialize(questions);
}

如果对 readCachedUserQuestion 的调用可能在同一个类中,则取决于弹簧配置。默认情况下,只缓存对方法的外部调用。

答案 2 :(得分:0)

我找到了一个对我有用的肮脏解决方案。通过创建另一个包含与返回的对象相同的属性的类,然后使用ModelMapper将返回的对象映射到我的新对象。 例如我有一个MyObject类:

    public class MyObject {
     private Long id;
     private Long label;

    //getters and setters
    }

新创建的类:

public class MyObjectCopy {
    private Long id;
    private Long label;

    //getters and setters
}

和一个返回MyObject的可缓存方法:

 @Cacheable("cache")
public MyObject getMyObject();

并防止缓存被修改:我必须将该对象映射到我的classCopy上,然后对其进行处理:

MyObjectCopy myObjectCopy = modelMapper.map(myobject, MyObjectCopy.class);

不要忘记为嵌套对象创建一个副本类;

相关问题