gpt4 book ai didi

java - 通过 SpringCache 缓存嵌套可缓存操作

转载 作者:塔克拉玛干 更新时间:2023-11-01 22:24:56 25 4
gpt4 key购买 nike

我的任务是将 SpringCache 用于我们的一项服务,以减少数据库查找的次数。在测试实现时,我注意到一些可缓存的操作通过日志语句被多次调用。调查表明,如果在可缓存方法中调用可缓存操作,则嵌套操作根本不会被缓存。因此,稍后调用嵌套操作会导致进一步查找。

下面列出了描述问题的简单单元测试:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {SpringCacheTest.Config.class} )
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public class SpringCacheTest {

private final static String CACHE_NAME = "testCache";
private final static Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private final static AtomicInteger methodInvocations = new AtomicInteger(0);

public interface ICacheableService {

String methodA(int length);
String methodB(String name);
}

@Resource
private ICacheableService cache;

@Test
public void testNestedCaching() {

String name = "test";
cache.methodB(name);
assertThat(methodInvocations.get(), is(equalTo(2)));

cache.methodA(name.length());
// should only be 2 as methodA for this length was already invoked before
assertThat(methodInvocations.get(), is(equalTo(3)));
}

@Configuration
public static class Config {

@Bean
public CacheManager getCacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache(CACHE_NAME)));
return cacheManager;
}

@Bean
public ICacheableService getMockedEntityService() {
return new ICacheableService() {
private final Random random = new Random();

@Cacheable(value = CACHE_NAME, key = "#root.methodName.concat('_').concat(#p0)")
public String methodA(int length) {
methodInvocations.incrementAndGet();
LOG.debug("Invoking methodA");
char[] chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toCharArray();
StringBuilder sb = new StringBuilder();
for (int i=0; i<length; i++) {
sb.append(chars[random.nextInt(chars.length)]);
}
String result = sb.toString();
LOG.debug("Returning {} for length: {}", result, length);
return result;
}

@Cacheable(value = CACHE_NAME, key = "#root.methodName.concat('_').concat(#p0)")
public String methodB(String name) {
methodInvocations.incrementAndGet();
LOG.debug("Invoking methodB");

String rand = methodA(name.length());
String result = name+"_"+rand;
LOG.debug("Returning {} for name: {}", result, name);
return result;
}
};
}
}
}

这两种方法的实际工作对于测试用例本身并不重要,因为应该测试缓存。

我以某种方式理解嵌套操作的结果未被缓存的原因,但我想知道是否有可用的配置(我还没有弄清楚)来启用对嵌套可缓存操作的返回值的缓存。

我知道通过重构和提供嵌套操作的返回值作为外部操作的参数是可行的,但是因为这可能涉及更改一些操作(以及对它们进行单元测试)配置或其他在我们的具体案例中,解决方法(如果可用)将更可取。

最佳答案

问题是您直接从 methodB 访问 methodA,因此这会阻止通过处理缓存机制的 Java 代理。此外,您没有添加 @EnableCaching 注释,因此实际上在您的测试中根本没有缓存。

以下测试表明,如果您正确地通过 Spring 创建的代理,嵌套缓存模式将按预期工作:

import static org.junit.Assert.*;

import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCache;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { SpringCacheTest.Config.class })
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public class SpringCacheTest {

private final static String CACHE_NAME = "testCache";
private final static AtomicInteger methodInvocations = new AtomicInteger(0);

public interface ICacheableService {

String methodA(int length);

String methodB(String name);
}

@Resource
private ICacheableService cache;

@Test
public void testNestedCaching() {

String name = "test";
cache.methodB(name);
assertEquals(methodInvocations.get(), 2);

cache.methodA(name.length());
// should only be 2 as methodA for this length was already invoked before
assertEquals(methodInvocations.get(), 2);
}

@Configuration
@EnableCaching
public static class Config {

@Bean
public CacheManager getCacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache(CACHE_NAME)));
return cacheManager;
}

@Bean
public ICacheableService getMockedEntityService() {
return new ICacheableService() {
private final Random random = new Random();

@Autowired
ApplicationContext context;

@Override
@Cacheable(value = CACHE_NAME, key = "#root.methodName.concat('_').concat(#p0)")
public String methodA(int length) {
methodInvocations.incrementAndGet();
System.out.println("Invoking methodA");
char[] chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toCharArray();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < length; i++) {
sb.append(chars[random.nextInt(chars.length)]);
}
String result = sb.toString();
System.out.println("Returning " + result + " for length: " + length);
return result;
}

@Override
@Cacheable(value = CACHE_NAME, key = "#root.methodName.concat('_').concat(#p0)")
public String methodB(String name) {
methodInvocations.incrementAndGet();
System.out.println("Invoking methodB");
ICacheableService cache = context.getBean(ICacheableService.class);
String rand = cache.methodA(name.length());
String result = name + "_" + rand;
System.out.println("Returning " + result + " for name: " + name);
return result;
}
};
}
}
}

关于java - 通过 SpringCache 缓存嵌套可缓存操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29562642/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com