Cela est dû à la nature des expressions différées #{}
(notez que les expressions standard "héritées" ${}
se comportent exactement de la même manière lorsque Facelets est utilisé à la place de JSP). L'expression différée n'est pas immédiatement évaluée, mais créée en tant ValueExpression
qu'objet et la méthode getter derrière l'expression est exécutée à chaque fois que le code appelle ValueExpression#getValue()
.
Cela sera normalement invoqué une ou deux fois par cycle de demande-réponse JSF, selon que le composant est un composant d'entrée ou de sortie ( découvrez-le ici ). Cependant, ce nombre peut augmenter (beaucoup) plus haut lorsqu'il est utilisé dans l'itération de composants JSF (tels que <h:dataTable>
et <ui:repeat>
), ou ici et là dans une expression booléenne comme l' rendered
attribut. JSF (en particulier, EL) ne mettra pas du tout en cache le résultat évalué de l'expression EL car il peut renvoyer des valeurs différentes à chaque appel (par exemple, lorsqu'il dépend de la ligne datable actuellement itérée).
Évaluer une expression EL et invoquer une méthode getter est une opération très bon marché, donc vous ne devriez généralement pas vous en soucier du tout. Cependant, l'histoire change lorsque vous effectuez une logique DB / métier coûteuse dans la méthode getter pour une raison quelconque. Ce serait à chaque fois réexécuté!
Les meilleures méthodes dans les beans de sauvegarde JSF doivent être conçues de manière à renvoyer uniquement la propriété déjà préparée et rien de plus, exactement selon la spécification Javabeans . Ils ne devraient pas du tout faire de logique DB / métier coûteuse. Pour cela, les @PostConstruct
méthodes d'écoute du bean et / ou (action) doivent être utilisées. Ils ne sont exécutés qu'une seule fois à un moment donné du cycle de vie JSF basé sur les demandes et c'est exactement ce que vous voulez.
Voici un résumé de toutes les bonnes façons de prérégler / charger une propriété.
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;
}
}
Notez que vous ne devez pas utiliser le constructeur ou le bloc d'initialisation du bean pour le travail car il peut être invoqué plusieurs fois si vous utilisez un framework de gestion de bean qui utilise des proxys, tels que CDI.
S'il n'y a vraiment pas d'autre moyen pour vous, en raison de certaines exigences de conception restrictives, vous devez introduire le chargement paresseux dans la méthode getter. C'est-à-dire si la propriété est null
, puis chargez-la et affectez-la à la propriété, sinon renvoyez-la.
public SomeObject getSomeProperty() {
// If there are really no other ways, introduce lazy loading.
if (someProperty == null) {
someProperty = loadSomeProperty();
}
return someProperty;
}
De cette façon, la logique DB / métier coûteuse ne sera pas inutilement exécutée à chaque appel getter unique.
Voir également:
FacesContext#getCurrentPhaseId()
.Avec JSF 2.0, vous pouvez attacher un écouteur à un événement système
Vous pouvez également inclure la page JSF dans une
f:view
balisela source
J'ai écrit un article sur la façon de mettre en cache le getter de beans JSF avec Spring AOP.
Je crée un simple
MethodInterceptor
qui intercepte toutes les méthodes annotées avec une annotation spéciale:Cet intercepteur est utilisé dans un fichier de configuration de ressort:
J'espère que cela vous aidera!
la source
Publié à l'origine sur le forum PrimeFaces @ http://forum.primefaces.org/viewtopic.php?f=3&t=29546
Récemment, j'ai été obsédé par l'évaluation des performances de mon application, le réglage des requêtes JPA, le remplacement des requêtes SQL dynamiques par des requêtes nommées, et ce matin, j'ai reconnu qu'une méthode getter était plus un HOT SPOT dans Java Visual VM que le reste de mon code (ou la majorité de mon code).
Méthode Getter:
Référencé par ui: include in dans index.xhtml
Ci-dessous, vous verrez que PageNavigationController.getGmapsAutoComplete () est un HOT SPOT (problème de performance) dans Java Visual VM. Si vous regardez plus bas, sur la capture d'écran, vous verrez que getLazyModel (), la méthode getter dateable lazy PrimeFaces, est également un point chaud, uniquement lorsque l'utilisateur final fait beaucoup de choses 'opérations paresseuses'. dans l'application. :)
Voir le code (original) ci-dessous.
Référencé par ce qui suit dans index.xhtml:
Solution: puisqu'il s'agit d'une méthode «getter», déplacez le code et attribuez une valeur à gmapsAutoComplete avant d'appeler la méthode; voir le code ci-dessous.
Résultats des tests: PageNavigationController.getGmapsAutoComplete () n'est plus un HOT SPOT dans Java Visual VM (n'apparaît même plus)
Partager ce sujet, car de nombreux utilisateurs experts ont conseillé aux développeurs JSF juniors de ne PAS ajouter de code dans les méthodes «getter». :)
la source
Si vous utilisez CDI, vous pouvez utiliser les méthodes Producers. Il sera appelé plusieurs fois, mais le résultat du premier appel est mis en cache dans la portée du bean et est efficace pour les getters qui calculent ou initialisent des objets lourds! Voir ici , pour plus d'informations.
la source
Vous pouvez probablement utiliser AOP pour créer une sorte d'aspect qui met en cache les résultats de nos getters pendant une durée configurable. Cela vous éviterait d'avoir à copier et coller du code passe-partout dans des dizaines d'accesseurs.
la source
C'est ce que nous appelons une optimisation prématurée. Dans les rares cas où un profileur vous indique que le calcul d'une propriété est si extraordinairement coûteux que son appel trois fois plutôt qu'une fois a un impact significatif sur les performances, vous ajoutez la mise en cache comme vous le décrivez. Mais à moins que vous ne fassiez quelque chose de vraiment stupide comme factoriser des nombres premiers ou accéder à une base de données dans un getter, votre code a probablement une douzaine d'inefficacités pires dans des endroits auxquels vous n'avez jamais pensé.
la source
Je conseillerais également d'utiliser un tel cadre comme Primefaces au lieu du stock JSF, ils traitent ces problèmes avant l'équipe JSF e. g dans les primefaces, vous pouvez définir une soumission partielle. Sinon, BalusC l'a bien expliqué.
la source
C'est toujours un gros problème en JSF. Par exemple, si vous avez une méthode
isPermittedToBlaBla
pour les contrôles de sécurité et que selon vous, vous avezrendered="#{bean.isPermittedToBlaBla}
alors la méthode sera appelée plusieurs fois.Le contrôle de sécurité pourrait être compliqué, par exemple. Requête LDAP etc. Vous devez donc éviter cela avec
et vous devez vous assurer au sein d'une session bean ceci par requête.
Je pense que JSF doit implémenter ici quelques extensions pour éviter les appels multiples (ex: annotation ne
@Phase(RENDER_RESPONSE)
calle cette méthode qu'une seule fois après laRENDER_RESPONSE
phase ...)la source