स्प्रिंग कैश @ उपलब्ध - एक ही सेम की दूसरी विधि से कॉल करते समय काम नहीं कर रहा है


107

एक ही सेम के दूसरे तरीके से कैश्ड विधि को कॉल करने पर स्प्रिंग कैश काम नहीं कर रहा है।

यहाँ मेरी समस्या को स्पष्ट तरीके से समझाने के लिए एक उदाहरण है।

विन्यास:

<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) में बुलाया जाता है , तो कैश का उपयोग नहीं किया जा रहा है।

क्या यह है कि स्प्रिंग कैश कैसे काम करता है या मुझे कुछ याद आ रहा है?


क्या आप someDateपरम के लिए समान मूल्य का उपयोग कर रहे हैं ?
डेवी जू

@ दीप हां, यह वही है
बाला

जवाबों:


158

मेरा मानना ​​है कि यह कैसे काम करता है। पढ़ने से मुझे जो याद है, उससे उत्पन्न एक प्रॉक्सी क्लास है जो सभी अनुरोधों को स्वीकार करता है और कैश किए गए मूल्य के साथ प्रतिक्रिया करता है, लेकिन एक ही क्लास के भीतर 'आंतरिक' कॉल को कैश्ड मान नहीं मिलेगा।

से https://code.google.com/p/ehcache-spring-annotations/wiki/UsingCacheable

प्रॉक्सी के माध्यम से आने वाली केवल बाहरी विधि कॉल इंटरसेप्टेड हैं। इसका मतलब यह है कि स्व-आह्वान, वास्तव में, लक्ष्य वस्तु के भीतर एक विधि जिसे लक्ष्य वस्तु की एक अन्य विधि कहा जाता है, रनचैट पर वास्तविक कैश अवरोधन पैदा नहीं करेगा, भले ही आह्वान विधि @Cacheable के साथ चिह्नित हो।


1
ठीक है, यदि आप दूसरी कॉल को भी उपलब्ध करते हैं, तो यह केवल एक कैश मिस होगा। यही है, केवल पहली कॉल getEmployeeEnrichedData कैश को बायपास करेगी। इसके लिए दूसरा कॉल पहले कॉल से पहले कैश की गई वापसी का उपयोग करेगा। GetEmployeeEnrichedData।
शॉन डी।

1
@Bala मैं एक ही मुद्दा है, मेरे समाधान कदम है @Cacheableडीएओ को :( यदि आपके पास बेहतर समाधान कृपया मुझे बताएँ, धन्यवाद।
VAdaihiep

2
आप कैशे सर्विस भी लिख सकते हैं और सेवा में अपने सभी कैश तरीकों को डाल सकते हैं। जिस सेवा की आपको आवश्यकता है उसे कॉल करें और विधियों को कॉल करें। मेरे मामले में मदद की।
DOUBL3P

वसंत 4.3 के बाद से यह @Resourceस्व-स्वत:
फायरिंग

1
इसके अलावा बाहरी @Cacheableतरीका होना चाहिए public, यह पैकेज-निजी तरीकों पर काम नहीं करता है। मुश्किल रास्ता मिल गया।
आनंद

36

स्प्रिंग 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);
    }
}

2
इसके तहत कोशिश की 4.3.17और यह काम नहीं किया, कॉल selfएक प्रॉक्सी के माध्यम से जाने के लिए नहीं है और कैश (अभी भी) बायपास है।
मदब्राइक्स

मेरे लिए काम किया। कैश हिट। मैं इस तारीख के रूप में नवीनतम वसंत निर्भरता का उपयोग करता हूं।
टॉमस बिसियाक

क्या मैं केवल यही सोचता हूं कि यह पैटर्न को तोड़ता है, एक सिंगलटन मिक्स, इत्यादि की तरह दिखता है?
2mia

मैं स्प्रिंग बूट स्टार्टर संस्करण का उपयोग करता था - 2.1.0। कृपया, और मेरे पास एक ही मुद्दा था। इस विशेष समाधान ने एक आकर्षण की तरह काम किया।
दीपण प्रभु बाबू

18

नीचे दिए गए उदाहरण में मैं एक ही सेम के भीतर से प्रॉक्सी को हिट करने के लिए उपयोग करता हूं, यह @ mario-eis के समाधान के समान है, लेकिन मुझे यह थोड़ा अधिक पठनीय लगता है (शायद यह :-) नहीं है। वैसे भी, मुझे सेवा स्तर पर @ अस्वीकार्य टिप्पणियां रखना पसंद है:

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

स्प्रिंग बीन में नया लेनदेन शुरू करना भी देखें


1
आवेदन के संदर्भ तक पहुंच, उदाहरण के लिए applicationContext.getBean(SettingService.class);, निर्भरता इंजेक्शन के विपरीत है। मैं उस शैली से बचने का सुझाव देता हूं।
सिंगलशॉट

2
हां इससे बचना बेहतर होगा, लेकिन मुझे इस समस्या का बेहतर समाधान नहीं दिखता।
मोलहोम

10

यहाँ मैं एक ही वर्ग के भीतर विधि कॉल के केवल मामूली उपयोग के साथ छोटी परियोजनाओं के लिए क्या कर रहा हूँ। इन-कोड डॉक्यूमेंटेशन की जोरदार सलाह दी जाती है, क्योंकि यह सहकर्मियों के लिए कठिन लग सकता है। लेकिन इसका परीक्षण करना आसान, सरल, जल्दी से प्राप्त करना और मुझे पूर्ण विकसित एस्पेक्टेज इंस्ट्रूमेंटेशन प्रदान करना है। हालाँकि, अधिक भारी उपयोग के लिए मैं 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);
        ...
    }
}

1
आप AspectJ के साथ एक उदाहरण दे सकते हैं?
सर्जियो बिलेलो

यह उत्तर stackoverflow.com/a/34090850/1371329 का एक डुप्लिकेट है ।
jaco0646

3

मेरे मामले में मैं चर जोड़ता हूं:

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

}

यह इस मामले में कैश का उपयोग करेगा।


2

अपने बीन के चारों ओर प्रॉक्सी बनाने के लिए स्टैटिक बुनाई का उपयोग करें। इस मामले में भी 'आंतरिक' तरीके सही तरीके से काम करेंगे


"स्थिर बुनाई" क्या है? Google ज्यादा मदद नहीं करता है। किसी भी संकेत इस अवधारणा को समझने के लिए?
बाला

@ बाला - हमारे प्रोजेक्ट पर उदाहरण के लिए हम <iajcसंकलक (चींटी से) का उपयोग करते हैं जो कैश-सक्षम कक्षाओं के लिए सभी आवश्यक पहलुओं को हल करता है।
डेवई

0

मैं 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);
    }
}
}

0

अब तक का सबसे आसान समाधान इस प्रकार है:

AService.this.getEmployeeData(date);
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.