Spring Cache @Cacheable - 같은 빈의 다른 메서드에서 호출 중 작동하지 않음
같은 빈의 다른 메서드에서 캐시된 메서드를 호출할 때 스프링 캐시가 작동하지 않습니다.
여기 내 문제를 명확하게 설명하는 예가 있다.
설정:
<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
캐시는 사용되지 않습니다.
이것이 스프링 캐시의 작동 방식입니까, 아니면 뭔가 부족한 것일까요?
이렇게 하는 것 같아요.제가 읽은 것으로 기억하는데, 모든 요청을 대행 수신하고 캐시된 값으로 응답하는 프록시 클래스가 생성되어 있는데, 같은 클래스 내의 "internal" 콜은 캐시된 값을 얻지 못합니다.
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에서 새 트랜잭션 시작을 참조하십시오.
다음은 같은 클래스 내에서 메서드 호출을 거의 사용하지 않는 소규모 프로젝트에서 수행하는 작업입니다.인코드 문서는 동료에게 부담스럽게 보일 수 있으므로 강력히 권장됩니다.단, 테스트도 쉽고, 심플하고, 실현도 빠르며, Aspect J의 완전한 계측을 불필요하게 할 수 있습니다.다만, 보다 많은 사용량에 대해서는, Aspect J 솔루션을 추천합니다.
@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);
...
}
}
이 경우 캐시를 사용합니다.
네, 다른 게시물에 이미 언급된 이유로 인해 캐싱은 실행되지 않습니다.다만, 그 방법을 독자적인 클래스(이 경우는 서비스)에 넣는 것으로 해결하겠습니다.이를 통해 코드를 쉽게 유지/테스트하고 이해할 수 있습니다.
@Service // or @Named("aService")
public class AService {
@Autowired //or how you inject your dependencies
private EmployeeService employeeService;
public List<EmployeeData> getEmployeeData(Date date){
employeeService.getEmployeeData(date);
}
public List<EmployeeEnrichedData> getEmployeeEnrichedData(Date date){
List<EmployeeData> employeeData = getEmployeeData(date);
...
}
}
@Service // or @Named("employeeService")
public class EmployeeService {
@Cacheable("employeeData")
public List<EmployeeData> getEmployeeData(Date date){
println("This will be called only once for same 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="https://stackoverflow.com/questions/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="https://stackoverflow.com/questions/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);
}
}
}
가장 쉬운 접근법이라고 생각되는 것을 공유하겠습니다.
- 컨텍스트를 사용하지 를 자동 하기 위해 사용합니다.
this
.
업데이트된 코드는 다음과 같습니다.
@Controller
public class TestController {
@Autowired TestController self;
@RequestMapping("/test")
public String testView(){
self.expensiveMethod();
return "test";
}
@Cacheable("ones")
public void expensiveMethod(){
System.out.println("Cache is not being used");
}
}
언급URL : https://stackoverflow.com/questions/16899604/spring-cache-cacheable-not-working-while-calling-from-another-method-of-the-s
'programing' 카테고리의 다른 글
Vuex 저장소 개체에서 계산된 속성이 업데이트되지 않음 (0) | 2022.07.21 |
---|---|
express.body get 메서드가 req.body 값을 가져올 수 없습니다. (0) | 2022.07.21 |
최적의 Java 이미지 처리 라이브러리/접근법은 무엇입니까? (0) | 2022.07.21 |
ISO 8601 형식으로 날짜, 시간 및 분을 사용하여 현재 모멘트를 얻는 방법은 무엇입니까? (0) | 2022.07.21 |
"calloc"의 첫 번째 "c"는 무엇을 의미합니까? (0) | 2022.07.21 |