【问题标题】:how to prevent a return value from cache to be changed in spring cacheable如何防止在spring cacheable中更改缓存的返回值
【发布时间】:2012-06-05 08:04:30
【问题描述】:

我正在使用 ehcache 作为缓存实现来解决 spring 3.1 注释缓存。

这样的返回值的方法

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

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

有没有办法获得只读返回值?

【问题讨论】:

    标签: java spring caching ehcache


    【解决方案1】:

    您可以编写自己的方面,始终创建返回值的副本,这将使您独立于某些 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?

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

    <aop:aspectj-autoproxy />
    

    【讨论】:

    • 其实可以,但是有点重量级。我必须记住@CopyReturnValue 和@Cacheable("cache") 的顺序。
    • 我不知道为什么当方法首先成功运行时spring没有从缓存中获取对象(缓存中没有obj,它会将obj存储到缓存中)。它应该先存储到缓存中然后取出来,这样copyOnWrite就可以了。
    • @Foxswily:我同意你的观点,但 EhCacheInterceptor(第 140-155 行)目前不能这样工作。它返回原件。您已经描述了应该做些什么来使其按照您需要的方式工作。这只是一行代码......也许告诉来自ehcache-spring-annotations或DIY的人......
    • 谢谢@jeha,我会告诉spring的人(spring 3.1可以做ehcache-spring-annotations之类的东西)
    【解决方案2】:

    我在使用 spring 缓存时遇到了同样的问题。我不想从缓存中接收相同的 java 对象。

    就我而言,我想缓存具有许多字段等的大型 Java 对象。所以用深拷贝来拷贝所有的数据类是很痛苦的。我阅读了有关使用序列化复制 java 对象的文章。

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

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

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

    @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 的调用可能在同一个类中,则取决于 spring 配置。默认情况下,仅缓存对方法的外部调用。

    【讨论】:

      【解决方案3】:

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

          public class MyObject {
           private Long id;
           private Long label;
      
          //getters and setters
          }
      

      新的 Created 类:

      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);
      

      不要忘记为嵌套对象创建一个复制类;

      【讨论】:

        猜你喜欢
        • 2018-02-07
        • 2012-08-20
        • 2016-12-31
        • 2021-08-22
        • 2018-03-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-06-30
        相关资源
        最近更新 更多