how to prevent a return value from cache to be cha

2019-04-16 03:17发布

i'm working around spring 3.1 annotation cache with ehcache as a cache implement.

a method with return value like this

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

i got a myobject return value for the first time,and it's editable. ehcache can do something for that by setting "copyOnRead" or "copyOnWrite". it will force serialize object on read/write. but at the first time spring will not get value from cache,it always return by method itself.

is there some way to get a readonly return value?

2条回答
ら.Afraid
2楼-- · 2019-04-16 03:46

I had the same problem with the spring cache. I didn't want to receive the same java objects from the cache.

In my case i want to cache big java objects with many fields and so on. So it is very painful to copy all the data classes with deep copy. I read the article about copy java objects with serialization.

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

This brought me to the idea to cache only the serialized data. Every time a object is read from the cache it is deserialized.

For the serialization i used apache commons helper methods

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

It depends on the spring configuration if the call to readCachedUserQuestion could be in the same class or not. Per default only extern calls to a method are cached.

查看更多
3楼-- · 2019-04-16 03:54

You could write your own aspect that always creates a copy of the returned value, which would make you independent of some Ehcache settings.

At first, a marker annotation like @CopyReturnValue would be nice for expressing the pointcut:

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

Now, the aspect can use this annotation for the pointcut expression:

@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;
    }
}

Finally, add the annotation to your method:

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

For the CopyReturnValueAspect I use BeanUtils to create a copy of the returned value - just as an example. For further information on that topic, you might want to look at How to copy properties from one Java bean to another?

Oh, don't forget to enable @AspectJ support in you Spring configuration if you haven't already:

<aop:aspectj-autoproxy />
查看更多
登录 后发表回答