जेएसएफ कॉल क्यों कई बार मिलता है


256

मान लीजिए कि मैं इस तरह से एक आउटपुट टेक्स्ट घटक निर्दिष्ट करता हूं:

<h:outputText value="#{ManagedBean.someProperty}"/>

यदि मैं गेट मैसेज के लिए एक लॉग मैसेज प्रिंट करता हूं somePropertyऔर पेज लोड करता हूं , तो यह नोटिस करना तुच्छ है कि रिक्वेस्ट को रिक्वेस्ट एक से अधिक बार रिक्वेस्ट की जा रही है (मेरे मामले में दो या तीन बार ऐसा ही हुआ है):

DEBUG 2010-01-18 23:31:40,104 (ManagedBean.java:13) - Getting some property
DEBUG 2010-01-18 23:31:40,104 (ManagedBean.java:13) - Getting some property

यदि मूल्य की somePropertyगणना करना महंगा है, तो यह संभावित रूप से एक समस्या हो सकती है।

मैंने थोड़ा गुगली की और लगा कि यह एक ज्ञात मुद्दा है। एक वर्कअराउंड में एक चेक शामिल करना और यह देखना था कि क्या इसकी गणना पहले ही की जा चुकी है:

private String someProperty;

public String getSomeProperty() {
    if (this.someProperty == null) {
        this.someProperty = this.calculatePropertyValue();
    }
    return this.someProperty;
}

इसके साथ मुख्य समस्या यह है कि आपको बॉयलरप्लेट कोड का भार मिलता है, न कि निजी चर का उल्लेख करने के लिए, जिनकी आपको आवश्यकता नहीं है।

इस दृष्टिकोण के विकल्प क्या हैं? क्या इतना अनावश्यक कोड के बिना इसे प्राप्त करने का एक तरीका है? क्या जेएसएफ को इस तरह से व्यवहार करने से रोकने का कोई तरीका है?

आपके सहयोग के लिए धन्यवाद!

जवाबों:


340

यह आस्थगित अभिव्यक्तियों की प्रकृति के कारण होता है #{}(ध्यान दें कि "विरासत" मानक अभिव्यक्तियाँ ${}ठीक वैसी ही व्यवहार करती हैं जब JSP के बजाय फेसलेट्स का उपयोग किया जाता है)। आस्थगित अभिव्यक्ति का तुरंत मूल्यांकन नहीं किया जाता है, लेकिन एक ValueExpressionऑब्जेक्ट के रूप में बनाया जाता है और कोड के कॉल करने पर अभिव्यक्ति के पीछे गेट्टर विधि को हर बार निष्पादित किया जाता है ValueExpression#getValue()

यह आम तौर पर जेएसएफ अनुरोध-प्रतिक्रिया चक्र के अनुसार एक या दो बार लागू किया जाएगा, यह इस बात पर निर्भर करता है कि घटक एक इनपुट या आउटपुट घटक है ( इसे यहां जानें )। हालांकि, यह गिनती जेएसएफ घटकों (जैसे कि <h:dataTable>और <ui:repeat>), या यहाँ और वहाँ renderedविशेषता जैसे बूलियन अभिव्यक्ति को पुनरावृत्त करने में उपयोग करने पर (बहुत) उच्चतर हो सकती है । जेएसएफ (विशेष रूप से, ईएल) ईएल अभिव्यक्ति के मूल्यांकन किए गए परिणाम को बिल्कुल भी कैश नहीं करेगा क्योंकि यह प्रत्येक कॉल पर अलग-अलग मान लौटा सकता है (उदाहरण के लिए, जब यह वर्तमान में चलने योग्य डेटेबल पंक्ति पर निर्भर है)।

ईएल अभिव्यक्ति का मूल्यांकन करना और एक गेटर विधि को लागू करना एक बहुत ही सस्ता ऑपरेशन है, इसलिए आपको आमतौर पर इस बारे में चिंता नहीं करनी चाहिए। हालाँकि, कहानी तब बदलती है जब आप किसी कारण से गेटर विधि में महंगे DB / व्यावसायिक तर्क का प्रदर्शन कर रहे होते हैं। यह फिर से निष्पादित किया जाएगा!

जेएसएफ बैकिंग बीन्स में गेट्टर विधियों को इस तरह से डिज़ाइन किया जाना चाहिए कि वे पूरी तरह से से पहले से तैयार की गई संपत्ति को वापस कर दें और कुछ और नहीं, बिल्कुल जेबाबीन विनिर्देश के अनुसार । उन्हें कोई महंगा डीबी / व्यावसायिक तर्क बिल्कुल भी नहीं करना चाहिए। उसके लिए बीन @PostConstructऔर / या (एक्शन) श्रोता विधियों का उपयोग किया जाना चाहिए। वे केवल एक बार अनुरोध-आधारित JSF जीवनचक्र के किसी बिंदु पर निष्पादित होते हैं और ठीक यही आप चाहते हैं।

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

public class Bean {

    private SomeObject someProperty;

    @PostConstruct
    public void init() {
        // In @PostConstruct (will be invoked immediately after construction and dependency/property injection).
        someProperty = loadSomeProperty();
    }

    public void onload() {
        // Or in GET action method (e.g. <f:viewAction action>).
        someProperty = loadSomeProperty();
    }           

    public void preRender(ComponentSystemEvent event) {
        // Or in some SystemEvent method (e.g. <f:event type="preRenderView">).
        someProperty = loadSomeProperty();
    }           

    public void change(ValueChangeEvent event) {
        // Or in some FacesEvent method (e.g. <h:inputXxx valueChangeListener>).
        someProperty = loadSomeProperty();
    }

    public void ajaxListener(AjaxBehaviorEvent event) {
        // Or in some BehaviorEvent method (e.g. <f:ajax listener>).
        someProperty = loadSomeProperty();
    }

    public void actionListener(ActionEvent event) {
        // Or in some ActionEvent method (e.g. <h:commandXxx actionListener>).
        someProperty = loadSomeProperty();
    }

    public String submit() {
        // Or in POST action method (e.g. <h:commandXxx action>).
        someProperty = loadSomeProperty();
        return "outcome";
    }

    public SomeObject getSomeProperty() {
        // Just keep getter untouched. It isn't intented to do business logic!
        return someProperty;
    }

}

ध्यान दें कि आपको नौकरी के लिए बीन के निर्माता या आरंभीकरण ब्लॉक का उपयोग नहीं करना चाहिए क्योंकि यह कई बार लागू किया जा सकता है यदि आप बीन प्रबंधन ढांचे का उपयोग कर रहे हैं जो सीडीआई जैसे प्रॉक्सी का उपयोग करता है।

यदि आपके लिए वास्तव में कोई अन्य तरीके नहीं हैं, तो कुछ प्रतिबंधात्मक डिजाइन आवश्यकताओं के कारण, तो आपको गेट्टर विधि के अंदर आलसी लोडिंग का परिचय देना चाहिए। यानी अगर संपत्ति हैnull , तो लोड और असाइन करें, अन्यथा इसे वापस लौटाएं।

    public SomeObject getSomeProperty() {
        // If there are really no other ways, introduce lazy loading.
        if (someProperty == null) {
            someProperty = loadSomeProperty();
        }

        return someProperty;
    }

इस तरह महंगा डीबी / व्यापार तर्क अनावश्यक रूप से हर एक गेट्टर कॉल पर निष्पादित नहीं होगा।

यह सभी देखें:


5
बस व्यापार तर्क करने के लिए गेटर्स का उपयोग न करें। बस इतना ही। अपने कोड तर्क को व्यवस्थित करें। मेरी शर्त यह है कि यह पहले से ही बस कंस्ट्रक्टर, पोस्टकंस्ट्रक्ट या एक्शन विधि का उपयोग करके स्मार्ट तरीके से तय किया गया है।
बालुस

3
-1, दृढ़ता से असहमत। जावाबीन्स कल्पना का पूरा बिंदु गुणों को केवल एक फ़ील्ड मान से अधिक होने की अनुमति देना है, और "व्युत्पन्न गुण" जो कि मक्खी पर गणना की जाती हैं, पूरी तरह से सामान्य हैं। बेमानी कॉल के बारे में चिंता करना समयपूर्व अनुकूलन के अलावा और कुछ नहीं है।
माइकल बोर्गवर्ड

3
उम्मीद करें कि यदि वे डेटा लौटाने से अधिक करते हैं जैसा कि आपने खुद को स्पष्ट रूप से कहा है :)
BalusC

4
आप यह जान सकते हैं कि
गेटर्स

2
@ गृह: यह व्यवहार को नहीं बदलेगा। आप हालांकि आलसी लोडिंग और / या द्वारा वर्तमान चरण आईडी की जांच करके प्राप्तकर्ता के किसी भी व्यावसायिक तर्क को सशर्त रूप से संभाल सकते हैं FacesContext#getCurrentPhaseId()
बालुस

17

JSF 2.0 के साथ आप श्रोता को सिस्टम ईवेंट में संलग्न कर सकते हैं

<h:outputText value="#{ManagedBean.someProperty}">
   <f:event type="preRenderView" listener="#{ManagedBean.loadSomeProperty}" />
</h:outputText>

वैकल्पिक रूप से आप JSF पृष्ठ को एक f:viewटैग में संलग्न कर सकते हैं

<f:view>
   <f:event type="preRenderView" listener="#{ManagedBean.loadSomeProperty}" />

      .. jsf page here...

<f:view>

9

मैंने एक लेख लिखा है कि स्प्रिंग एओपी के साथ जेएसएफ बीन्स गटर को कैसे कैश किया जाए।

मैं एक सरल बनाता हूं MethodInterceptorजो एक विशेष एनोटेशन के साथ एनोटेट किए गए सभी तरीकों को स्वीकार करता है:

public class CacheAdvice implements MethodInterceptor {

private static Logger logger = LoggerFactory.getLogger(CacheAdvice.class);

@Autowired
private CacheService cacheService;

@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {

    String key = methodInvocation.getThis() + methodInvocation.getMethod().getName();

    String thread = Thread.currentThread().getName();

    Object cachedValue = cacheService.getData(thread , key);

    if (cachedValue == null){
        cachedValue = methodInvocation.proceed();
        cacheService.cacheData(thread , key , cachedValue);
        logger.debug("Cache miss " + thread + " " + key);
    }
    else{
        logger.debug("Cached hit " + thread + " " + key);
    }
    return cachedValue;
}


public CacheService getCacheService() {
    return cacheService;
}
public void setCacheService(CacheService cacheService) {
    this.cacheService = cacheService;
}

}

इस इंटरसेप्टर का उपयोग स्प्रिंग कॉन्फ़िगरेशन फ़ाइल में किया जाता है:

    <bean id="advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
    <property name="pointcut">
        <bean class="org.springframework.aop.support.annotation.AnnotationMatchingPointcut">
            <constructor-arg index="0"  name="classAnnotationType" type="java.lang.Class">
                <null/>
            </constructor-arg>
            <constructor-arg index="1" value="com._4dconcept.docAdvance.jsfCache.annotation.Cacheable" name="methodAnnotationType" type="java.lang.Class"/>
        </bean>
    </property>
    <property name="advice">
        <bean class="com._4dconcept.docAdvance.jsfCache.CacheAdvice"/>
    </property>
</bean>

आशा है कि यह मदद करेगा!


6

मूल रूप से प्राइमफेस फोरम @ http://forum.primefaces.org/viewtopic.php?f=3&t=29546 पर पोस्ट किया गया

हाल ही में, मुझे अपने ऐप के प्रदर्शन का मूल्यांकन करते हुए देखा गया है, जेपीए प्रश्नों को ट्यून करते हुए, नामांकित प्रश्नों के साथ गतिशील एसक्यूएल प्रश्नों की जगह, और आज सुबह, मैंने माना कि एक गेट्टर विधि जावा विज़ुअल वीएम के बाकी हिस्सों की तुलना में एक हॉट स्पॉट की अधिक थी। मेरा कोड (या मेरे कोड का बहुमत)।

गेट्टर विधि:

PageNavigationController.getGmapsAutoComplete()

Ui द्वारा संदर्भित: index.xhtml में शामिल करें

नीचे, आप देखेंगे कि जावा विज़ुअल वीएम में PageNavigationController.getGmapsAutoComplete () एक हॉट स्पॉट (प्रदर्शन मुद्दा) है। यदि आप स्क्रीन कैप्चर पर और नीचे देखते हैं, तो आप देखेंगे कि getLazyModel (), PrimeFaces lazy datatable getter मेथड, एक हॉट स्पॉट भी है, केवल तभी जब एंड्यूसर बहुत 'आलसी डिटेटेबल' प्रकार का सामान / संचालन / कार्य कर रहा है ऐप में। :)

जावा विज़ुअल वीएम: हॉट स्पॉट दिखा रहा है

नीचे देखें (मूल) कोड।

public Boolean getGmapsAutoComplete() {
    switch (page) {
        case "/orders/pf_Add.xhtml":
        case "/orders/pf_Edit.xhtml":
        case "/orders/pf_EditDriverVehicles.xhtml":
            gmapsAutoComplete = true;
            break;
        default:
            gmapsAutoComplete = false;
            break;
    }
    return gmapsAutoComplete;
}

Index.xhtml में निम्न द्वारा संदर्भित:

<h:head>
    <ui:include src="#{pageNavigationController.gmapsAutoComplete ? '/head_gmapsAutoComplete.xhtml' : (pageNavigationController.gmaps ? '/head_gmaps.xhtml' : '/head_default.xhtml')}"/>
</h:head>

समाधान: चूंकि यह एक 'गेट्टर' विधि है, इसलिए कोड को स्थानांतरित करें और विधि को कॉल करने के लिए gmapsAutoComplete से पहले असाइन करें; नीचे कोड देखें।

/*
 * 2013-04-06 moved switch {...} to updateGmapsAutoComplete()
 *            because performance = 115ms (hot spot) while
 *            navigating through web app
 */
public Boolean getGmapsAutoComplete() {
    return gmapsAutoComplete;
}

/*
 * ALWAYS call this method after "page = ..."
 */
private void updateGmapsAutoComplete() {
    switch (page) {
        case "/orders/pf_Add.xhtml":
        case "/orders/pf_Edit.xhtml":
        case "/orders/pf_EditDriverVehicles.xhtml":
            gmapsAutoComplete = true;
            break;
        default:
            gmapsAutoComplete = false;
            break;
    }
}

परीक्षण के परिणाम: PageNavigationController.getGmapsAutoComplete () अब जावा विज़ुअल वीएम में एक हॉट स्पॉट नहीं है (अब और भी नहीं दिखा)

इस विषय को साझा करते हुए, क्योंकि कई विशेषज्ञ उपयोगकर्ताओं ने जूनियर जेएसएफ डेवलपर्स को सलाह दी है कि वे 'गेट्टर' तरीकों में कोड न जोड़ें। :)


4

यदि आप CDI का उपयोग कर रहे हैं, तो आप निर्माता विधियों का उपयोग कर सकते हैं। इसे कई बार कहा जाएगा, लेकिन पहली कॉल का परिणाम सेम के दायरे में कैश्ड है और भारी वस्तुओं की गणना या आरंभ करने वाले गेटर्स के लिए कुशल है! अधिक जानकारी के लिए यहाँ देखें ।


3

आप शायद एओपी का उपयोग किसी प्रकार के पहलू को बनाने के लिए कर सकते हैं जो समय के लिए एक विन्यास योग्य राशि के लिए हमारे गेटर्स के परिणामों को कैश कर देते हैं। यह आपको दर्जनों एक्सेसरों में बॉयलरप्लेट कोड को कॉपी-और-पेस्ट करने की आवश्यकता से रोक देगा।


क्या यह स्प्रिंग एओपी आप के बारे में बात कर रहे हैं? क्या आप जानते हैं कि मुझे एक कोड स्निपेट या दो पहलू कहां मिल सकते हैं? स्प्रिंग डॉक्यूमेंटेशन के पूरे 6 वें अध्याय को पढ़कर लगता है जैसे मैं स्प्रिंग का उपयोग नहीं कर रहा हूं;)
सेवास

-1

यदि someProperty का मान गणना करने के लिए महंगा है, तो यह संभावित रूप से एक समस्या हो सकती है।

इसे ही हम समयपूर्व अनुकूलन कहते हैं। एक दुर्लभ मामले में जो एक प्रोफाइलर आपको बताता है कि एक संपत्ति की गणना इतनी असाधारण रूप से महंगी है कि एक महत्वपूर्ण प्रदर्शन प्रभाव के बजाय इसे तीन बार कॉल करना, आप वर्णन के अनुसार कैशिंग जोड़ते हैं। लेकिन जब तक आप कुछ वास्तव में बेवकूफी करते हैं जैसे कि चोरी की घटनाओं को अंजाम देना या किसी गेटटर में डेटाब को एक्सेस करना, आपके कोड में उन स्थानों पर दर्जन भर से अधिक अक्षमताएं हैं जिनके बारे में आपने कभी सोचा नहीं है।


इसलिए सवाल - अगर कुछ प्रतिरूप कुछ गणना करने के लिए महंगे से मेल खाती है (या जैसा कि आप इसे डेटाबेस या फैक्टरिंग प्राइम्स को एक्सेस करने के लिए डालते हैं), तो प्रति अनुरोध कई बार गणना करने से बचने का सबसे अच्छा तरीका क्या है और इसका समाधान मैं प्रश्न में सूचीबद्ध हूं सर्वश्रेष्ठ। यदि आप सवाल का जवाब नहीं दे रहे हैं, तो टिप्पणी पोस्ट करने के लिए एक अच्छी जगह है, नहीं? साथ ही, आपकी पोस्ट BalusC की पोस्ट पर आपकी टिप्पणी के विपरीत लगती है - टिप्पणियों में आप कहते हैं कि यह मक्खी पर गणना करने के लिए ठीक है, और आपकी पोस्ट में आप कहते हैं कि यह बेवकूफी है। क्या मैं पूछ सकता हूं कि आप रेखा कहां खींचते हैं?
सेविस

यह एक फिसलने वाला पैमाना है, न कि श्वेत-श्याम मुद्दा। कुछ चीजें स्पष्ट रूप से कोई समस्या नहीं हैं, उदाहरण के लिए कुछ मूल्यों को जोड़ना, क्योंकि वे एक सेकंड के एक मिलियन से भी कम लेते हैं ( बहुत कम, वास्तव में)। कुछ स्पष्ट रूप से एक समस्या है, जैसे डीबी या फ़ाइल एक्सेस, क्योंकि वे 10ms या उससे अधिक समय ले सकते हैं - और आपको निश्चित रूप से ये जानने की आवश्यकता है ताकि आप उन्हें प्राप्त न कर सकें, न कि केवल गेटर्स में। लेकिन बाकी सब के लिए, वह रेखा है जहां प्रोफाइलर आपको बताता है।
माइकल बोर्गवर्ड

-1

मैं स्टॉक जेएसएफ के बजाय ऐसे फ्रेमवर्क को प्राइमफेस के रूप में उपयोग करने की सलाह दूंगा, वे जेएसएफ टीम ई से पहले ऐसे मुद्दों को संबोधित करते हैं। जी में आप आंशिक जमा सेट कर सकते हैं। अन्यथा BalusC ने इसे अच्छी तरह से समझाया है।


-2

यह अभी भी जेएसएफ में बड़ी समस्या है। Fo उदाहरण यदि आपके पास isPermittedToBlaBlaसुरक्षा जांच के लिए एक विधि है और आपके विचार में आपके पास rendered="#{bean.isPermittedToBlaBla}विधि कई बार कॉल की जाएगी।

सुरक्षा जाँच जटिल हो सकती है। LDAP क्वेरी इत्यादि इसलिए आपको इससे बचना चाहिए

Boolean isAllowed = null ... if(isAllowed==null){...} return isAllowed?

और आपको यह अनुरोध प्रति सत्र सेम के भीतर सुनिश्चित करना होगा।

Ich को लगता है कि JSF को कई कॉल से बचने के लिए यहां कुछ एक्सटेंशन लागू करने चाहिए (जैसे एनोटेशन चरण के @Phase(RENDER_RESPONSE)बाद केवल एक बार इस विधि को शांत करना RENDER_RESPONSE...)


2
आप रिजल्ट RequestParameterMap
Christophe Roussy
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.