[java] Spring Cache @Cacheable-동일한 Bean의 다른 메소드에서 호출하는 동안 작동하지 않음

동일한 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);
    }
}
}