spring @Cacheable继承

时间:2015-04-07 21:43:07

标签: java spring spring-cache

如何进行以下工作?

public abstract class MyAbstractOne {
      @Cacheable(value="myCache")
      public MyObject getObject() {
         //some code
         return object;
      }
}

子类

public class MySubClass extends MyAbstractOne {
      @Cacheable(value="myCache")
      public MyOtherObject getObjectConcrete() {
         //some code
         return object;
      }
}

以及这些对象的用户

//from autowired instance
@Autowired MySubClass subObject;

然后在某处

//first call - may not retrieve cached objects
obj1 = subObject.getMyObject();

//second call - SHOULD retrieve a cached objects
obj2 = subObject.getMyObject();

为什么会失败

assertTrue(obj1.equals(obj2));

但getMyObjectConcrete的相同内容不会失败。

1 个答案:

答案 0 :(得分:3)

也许您需要检查应用程序域对象上的“equals”(和“hashCode”)实现;确保它们得到妥善实施并形成良好(见Effective Java, 2nd Edition, Item 8 - Obey the the general contract when overriding equals)。

我能够得到一个类似于上面代码片段的小型简单应用程序......

package org.spring.cache;

import static org.junit.Assert.*;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.ObjectUtils;

/**
 * The CachingWithConcurrentMapTest class is a test suite of test cases testing the contract and functionality
 * of @Cacheable inheritance.
 *
 * @author John Blum
 * @see org.junit.Test
 * @see org.junit.runner.RunWith
 * @see org.springframework.cache.annotation.Cacheable
 * @see org.springframework.test.context.ContextConfiguration
 * @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner
 * @since 1.0.0
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@SuppressWarnings("unused")
public class CachingWithConcurrentMapTest {

  @Autowired
  private FactorialComputeService computeService;

  @Test
  public void testCachedObject() {
    ValueHolder<Long> twoSquared = computeService.squared(2l);
    ValueHolder<Long> twoSquaredAgain = computeService.squared(2l);

    assertEquals(twoSquared, twoSquaredAgain);
    assertSame(twoSquared, twoSquaredAgain);

    ValueHolder<Long> fourFactorial = computeService.factorial(4l);
    ValueHolder<Long> fourFactorialAgain = computeService.factorial(4l);

    assertEquals(fourFactorial, fourFactorialAgain);
    assertSame(fourFactorial, fourFactorialAgain);
    assertNotSame(twoSquared, fourFactorial);

    ValueHolder<Long> eightSquared = computeService.squared(8l);
    ValueHolder<Long> eightSquaredAgain = computeService.squared(8l);

    assertEquals(eightSquared, eightSquaredAgain);
    assertSame(eightSquared, eightSquaredAgain);
    assertNotSame(twoSquared, eightSquared);
    assertNotSame(fourFactorial, eightSquared);
  }

  @Service
  public static class SquaredComputeService {

    @Cacheable("Computations")
    public ValueHolder<Long> squared(Long value) {
      return new ValueHolder<>(value * value);
    }
  }

  @Service
  public static class FactorialComputeService extends SquaredComputeService {

    @Cacheable("Computations")
    public ValueHolder<Long> factorial(Long value) {
      return new ValueHolder<>(computeFactorial(value));
    }

    protected long computeFactorial(long value) {
      long result = value;
      while (--value > 0) {
        result *= value;
      }
      return result;
    }
  }

  public static class ValueHolder<T> {

    private T value;

    public ValueHolder() {
      this(null);
    }

    public ValueHolder(final T value) {
      this.value = value;
    }

    public T getValue() {
      return value;
    }

    public void setValue(final T value) {
      this.value = value;
    }

    @Override
    public boolean equals(final Object obj) {
      if (obj == this) {
        return true;
      }

      if (!(obj instanceof ValueHolder)) {
        return false;
      }

      ValueHolder that = (ValueHolder) obj;

      return ObjectUtils.nullSafeEquals(this.getValue(), that.getValue());
    }

    @Override
    public int hashCode() {
      int hashValue = 17;
      hashValue = 37 * hashValue + ObjectUtils.nullSafeHashCode(getValue());
      return hashValue;
    }

    @Override
    public String toString() {
      return String.format("{ @type = %1$s, value = %2$s }", getClass().getName(), getValue());
    }
  }
}

相应的Spring配置......

<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
">

  <context:annotation-config/>

  <cache:annotation-driven/>

  <bean id="cacheManager" class="org.springframework.cache.concurrent.ConcurrentMapCacheManager"/>

  <bean id="service" class="org.spring.cache.CachingWithConcurrentMapTest.FactorialComputeService"/>

</beans>

请记住,某些Cache实现(例如Pivotal GemFire)可以选择“copy-on-read”或序列化存储在Cache中的值。前者对于像交易这样的问题很有用,而后者总是适用于也是分布数据网格的缓存,其中(缓存)数据在节点集群中被分区(分片)。

可能有许多因素会影响您的结果(即等于方法构造,读取属性,序列化),因此请查看特定缓存实现的设置。

如果您的示例有任何问题,或者我们的问题仍然存在,请随时跟进。也许您可以了解您的应用程序域对象类型和正在使用的缓存实现/配置。

感谢。