동일한 Bean의 다른 메소드에서 캐시 된 메소드를 호출 할 때 Spring 캐시가 작동하지 않습니다.
다음은 내 문제를 명확하게 설명하는 예입니다.
구성 :
<cache:annotation-driven cache-manager="myCacheManager" />
<bean id="myCacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager" ref="myCache" />
</bean>
<!-- Ehcache library setup -->
<bean id="myCache"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:shared="true">
<property name="configLocation" value="classpath:ehcache.xml"></property>
</bean>
<cache name="employeeData" maxElementsInMemory="100"/>
캐시 된 서비스 :
@Named("aService")
public class AService {
@Cacheable("employeeData")
public List<EmployeeData> getEmployeeData(Date date){
..println("Cache is not being used");
...
}
public List<EmployeeEnrichedData> getEmployeeEnrichedData(Date date){
List<EmployeeData> employeeData = getEmployeeData(date);
...
}
}
결과 :
aService.getEmployeeData(someDate);
output: Cache is not being used
aService.getEmployeeData(someDate);
output:
aService.getEmployeeEnrichedData(someDate);
output: Cache is not being used
getEmployeeData
메소드 호출의 사용은 캐시 employeeData
예상대로 두 번째 호출에. 그러나 클래스 getEmployeeData
내 에서 메서드가 호출 되면 AService
( getEmployeeEnrichedData
) Cache가 사용되지 않습니다.
이것이 스프링 캐시가 작동하는 방식입니까 아니면 내가 뭔가를 놓치고 있습니까?
답변
나는 이것이 작동하는 방식이라고 믿습니다. 내가 읽은 내용에서 모든 요청을 가로 채고 캐시 된 값으로 응답하는 프록시 클래스가 생성되었지만 동일한 클래스 내의 ‘내부’호출은 캐시 된 값을 얻지 못합니다.
에서 https://code.google.com/p/ehcache-spring-annotations/wiki/UsingCacheable
프록시를 통해 들어오는 외부 메서드 호출 만 차단됩니다. 즉, 자체 호출은 실제로 대상 개체의 다른 메서드를 호출하는 대상 개체 내의 메서드가 호출 된 메서드가 @Cacheable로 표시되어 있더라도 런타임에 실제 캐시 가로 채기로 이어지지 않음을 의미합니다.
답변
Spring 4.3부터 주석에 대한 자체 자동 배선 을 사용하여 문제를 해결할 수 있습니다 @Resource
.
@Component
@CacheConfig(cacheNames = "SphereClientFactoryCache")
public class CacheableSphereClientFactoryImpl implements SphereClientFactory {
/**
* 1. Self-autowired reference to proxified bean of this class.
*/
@Resource
private SphereClientFactory self;
@Override
@Cacheable(sync = true)
public SphereClient createSphereClient(@Nonnull TenantConfig tenantConfig) {
// 2. call cached method using self-bean
return self.createSphereClient(tenantConfig.getSphereClientConfig());
}
@Override
@Cacheable(sync = true)
public SphereClient createSphereClient(@Nonnull SphereClientConfig clientConfig) {
return CtpClientConfigurationUtils.createSphereClient(clientConfig);
}
}
답변
아래 예제는 동일한 빈 내에서 프록시를 누르는 데 사용하는 것입니다. @ mario-eis의 솔루션과 비슷하지만 조금 더 읽기 쉽습니다 (아마도 :-가 아닐 수도 있습니다). 어쨌든 서비스 수준에서 @Cacheable 주석을 유지하고 싶습니다.
@Service
@Transactional(readOnly=true)
public class SettingServiceImpl implements SettingService {
@Inject
private SettingRepository settingRepository;
@Inject
private ApplicationContext applicationContext;
@Override
@Cacheable("settingsCache")
public String findValue(String name) {
Setting setting = settingRepository.findOne(name);
if(setting == null){
return null;
}
return setting.getValue();
}
@Override
public Boolean findBoolean(String name) {
String value = getSpringProxy().findValue(name);
if (value == null) {
return null;
}
return Boolean.valueOf(value);
}
/**
* Use proxy to hit cache
*/
private SettingService getSpringProxy() {
return applicationContext.getBean(SettingService.class);
}
...
Spring Bean에서 새 트랜잭션 시작 도 참조하십시오.
답변
다음은 동일한 클래스 내에서 메서드 호출을 약간만 사용하는 소규모 프로젝트에 대해 수행하는 작업입니다. 코드 내 문서는 동료들에게 어려움을 겪을 수 있으므로 강력하게 권장됩니다. 그러나 테스트하기 쉽고, 간단하고, 빠르게 달성 할 수 있으며, 완벽한 AspectJ 도구를 사용할 수 있습니다. 그러나 더 많이 사용하려면 AspectJ 솔루션을 조언합니다.
@Service
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
class AService {
private final AService _aService;
@Autowired
public AService(AService aService) {
_aService = aService;
}
@Cacheable("employeeData")
public List<EmployeeData> getEmployeeData(Date date){
..println("Cache is not being used");
...
}
public List<EmployeeEnrichedData> getEmployeeEnrichedData(Date date){
List<EmployeeData> employeeData = _aService.getEmployeeData(date);
...
}
}
답변
내 경우에는 변수를 추가합니다.
@Autowired
private AService aService;
나는 전화를 그래서 getEmployeeData
를 사용하여 방법을aService
@Named("aService")
public class AService {
@Cacheable("employeeData")
public List<EmployeeData> getEmployeeData(Date date){
..println("Cache is not being used");
...
}
public List<EmployeeEnrichedData> getEmployeeEnrichedData(Date date){
List<EmployeeData> employeeData = aService.getEmployeeData(date);
...
}
}
이 경우 캐시를 사용합니다.
답변
정적 위빙을 사용하여 빈 주변에 프록시를 만듭니다. 이 경우 ‘내부’방법도 올바르게 작동합니다.
답변
FactoryInternalCache
이 목적을 위해 실제 캐시와 함께 내부 내부 빈 ( )을 사용합니다.
@Component
public class CacheableClientFactoryImpl implements ClientFactory {
private final FactoryInternalCache factoryInternalCache;
@Autowired
public CacheableClientFactoryImpl(@Nonnull FactoryInternalCache factoryInternalCache) {
this.factoryInternalCache = factoryInternalCache;
}
/**
* Returns cached client instance from cache.
*/
@Override
public Client createClient(@Nonnull AggregatedConfig aggregateConfig) {
return factoryInternalCache.createClient(aggregateConfig.getClientConfig());
}
/**
* Returns cached client instance from cache.
*/
@Override
public Client createClient(@Nonnull ClientConfig clientConfig) {
return factoryInternalCache.createClient(clientConfig);
}
/**
* Spring caching feature works over AOP proxies, thus internal calls to cached methods don't work. That's why
* this internal bean is created: it "proxifies" overloaded {@code #createClient(...)} methods
* to real AOP proxified cacheable bean method {@link #createClient}.
*
* @see <a href="/programming/16899604/spring-cache-cacheable-not-working-while-calling-from-another-method-of-the-s">Spring Cache @Cacheable - not working while calling from another method of the same bean</a>
* @see <a href="/programming/12115996/spring-cache-cacheable-method-ignored-when-called-from-within-the-same-class">Spring cache @Cacheable method ignored when called from within the same class</a>
*/
@EnableCaching
@CacheConfig(cacheNames = "ClientFactoryCache")
static class FactoryInternalCache {
@Cacheable(sync = true)
public Client createClient(@Nonnull ClientConfig clientConfig) {
return ClientCreationUtils.createClient(clientConfig);
}
}
}