152

同じ 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

メソッド呼び出しは、予想どおり 2 番目の呼び出しでgetEmployeeDataキャッシュemployeeDataを使用します。ただし、getEmployeeDataメソッドがAServiceクラス内で呼び出される場合 ( getEmployeeEnrichedData)、Cache は使用されていません。

これはスプリングキャッシュの仕組みですか、それとも何か不足していますか?

4

10 に答える 10

221

これがその仕組みだと思います。私が読んだ記憶によると、すべてのリクエストをインターセプトしてキャッシュされた値で応答するプロキシ クラスが生成されていますが、同じクラス内の「内部」呼び出しはキャッシュされた値を取得しません。

https://code.google.com/p/ehcache-spring-annotations/wiki/UsingCacheableから

プロキシ経由で着信する外部メソッド呼び出しのみがインターセプトされます。これは、呼び出されたメソッドが @Cacheable でマークされていても、実際には、ターゲット オブジェクト内のメソッドがターゲット オブジェクトの別のメソッドを呼び出す自己呼び出しによって、実行時に実際のキャッシュ インターセプトが発生しないことを意味します。

于 2013-06-03T14:58:13.603 に答える
57

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);
    }
}
于 2018-02-19T13:16:22.780 に答える
13

これは、同じクラス内のメソッド呼び出しの使用がわずかしかない小さなプロジェクトに対して私が行うことです。コード内のドキュメントは、同僚にとって奇妙に見える可能性があるため、強くお勧めします。しかし、それはテストが簡単で、シンプルで、すぐに達成でき、完全な 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);
        ...
    }
}
于 2015-12-04T15:03:46.153 に答える
4

私の場合、変数を追加します:

@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);
    ...
}

}

この場合、キャッシュが使用されます。

于 2018-10-29T15:41:32.737 に答える
1

静的ウィービングを使用して、Bean の周りにプロキシを作成します。この場合、「内部」メソッドでさえ正しく機能します

于 2013-06-03T15:15:25.727 に答える