Isso é causado pela natureza das expressões adiadas #{}
(observe que expressões padrão "legadas" ${}
se comportam exatamente da mesma maneira quando Facelets é usado em vez de JSP). A expressão adiada não é avaliada imediatamente , mas criada como um ValueExpression
objeto e o método getter por trás da expressão é executado sempre que o código chama ValueExpression#getValue()
.
Isso normalmente será chamado uma ou duas vezes por ciclo de solicitação-resposta JSF, dependendo se o componente é um componente de entrada ou saída ( saiba aqui ). No entanto, essa contagem pode subir (muito) mais alto quando usada na iteração de componentes JSF (como <h:dataTable>
e <ui:repeat>
), ou aqui e ali em uma expressão booleana como o rendered
atributo O JSF (especificamente, EL) não armazenará em cache o resultado avaliado da expressão EL, pois pode retornar valores diferentes em cada chamada (por exemplo, quando depende da linha de dados iterada no momento).
Avaliar uma expressão EL e chamar um método getter é uma operação muito barata, portanto você geralmente não deve se preocupar com isso. No entanto, a história muda quando você está executando uma lógica de banco de dados / negócios cara no método getter, por algum motivo. Isso seria reexecutado toda vez!
Os métodos Getter nos beans de backup JSF devem ser projetados para que retornem apenas a propriedade já preparada e nada mais, exatamente conforme a especificação Javabeans . Eles não devem executar nenhuma lógica cara de negócios / banco de dados. Para isso @PostConstruct
, devem ser utilizados os métodos do ouvinte do bean e / ou (ação). Eles são executados apenas uma vez em algum momento do ciclo de vida JSF baseado em solicitação e é exatamente isso que você deseja.
Aqui está um resumo de todas as maneiras corretas de predefinir / carregar uma propriedade.
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;
}
}
Observe que você não deve usar o construtor ou o bloco de inicialização do bean para o trabalho, pois ele pode ser chamado várias vezes se você estiver usando uma estrutura de gerenciamento de bean que usa proxies, como CDI.
Se você realmente não tem outras maneiras, devido a alguns requisitos restritivos de design, introduza um carregamento lento dentro do método getter. Ou seja, se a propriedade estiver null
, carregue e atribua-a à propriedade, caso contrário, devolva-a.
public SomeObject getSomeProperty() {
// If there are really no other ways, introduce lazy loading.
if (someProperty == null) {
someProperty = loadSomeProperty();
}
return someProperty;
}
Dessa forma, a dispendiosa lógica de negócios / banco de dados não será desnecessariamente executada em todas as chamadas getter.
Veja também:
FacesContext#getCurrentPhaseId()
.Com o JSF 2.0, você pode anexar um ouvinte a um evento do sistema
Como alternativa, você pode colocar a página JSF em uma
f:view
tagfonte
Eu escrevi um artigo sobre como armazenar em cache o getter de beans JSF com o Spring AOP.
Eu crio um simples
MethodInterceptor
que intercepta todos os métodos anotados com uma anotação especial:Este interceptor é usado em um arquivo de configuração de mola:
Espero que ajude!
fonte
Originalmente publicado no fórum PrimeFaces @ http://forum.primefaces.org/viewtopic.php?f=3&t=29546
Recentemente, fiquei obcecado em avaliar o desempenho do meu aplicativo, ajustando as consultas JPA, substituindo as consultas dinâmicas SQL por consultas nomeadas e, nesta manhã, reconheci que um método getter era mais um HOT SPOT no Java Visual VM do que o resto de meu código (ou a maioria do meu código).
Método Getter:
Referenciado pela interface do usuário: include in in index.xhtml
Abaixo, você verá que PageNavigationController.getGmapsAutoComplete () é um HOT SPOT (problema de desempenho) no Java Visual VM. Se você olhar mais para baixo, na captura de tela, verá que o método getLazyModel (), o PrimeFaces GetFaz Datatable lento, também é um ponto de acesso, apenas quando o usuário final faz um monte de coisas / operações / tarefas do tipo 'Data Preguiçosa' no aplicativo. :)
Veja o código (original) abaixo.
Referenciado pelo seguinte em index.xhtml:
Solução: como esse é um método 'getter', mova o código e atribua valor a gmapsAutoComplete antes de o método ser chamado; veja o código abaixo.
Resultados do teste: PageNavigationController.getGmapsAutoComplete () não é mais um HOT SPOT no Java Visual VM (nem aparece mais)
Compartilhando este tópico, uma vez que muitos usuários experientes aconselharam os desenvolvedores juniores do JSF a NÃO adicionar código nos métodos 'getter'. :)
fonte
Se você estiver usando CDI, poderá usar os métodos Producers. Ele será chamado muitas vezes, mas o resultado da primeira chamada é armazenado em cache no escopo do bean e é eficiente para getters que estão computando ou inicializando objetos pesados! Veja aqui , para mais informações.
fonte
Você provavelmente poderia usar o AOP para criar algum tipo de Aspect que armazenou em cache os resultados de nossos getters por um período configurável. Isso impediria a necessidade de copiar e colar o código padrão em dezenas de acessadores.
fonte
Isso é o que chamamos de otimização prematura. Nos raros casos em que um criador de perfil diz que o cálculo de uma propriedade é tão extraordinariamente caro que chamá-la três vezes em vez de uma vez tem um impacto significativo no desempenho, você adiciona o cache conforme descreve. Mas, a menos que você faça algo realmente estúpido, como fatorar primos ou acessar um banco de dados em um getter, seu código provavelmente terá uma dúzia de ineficiências piores em lugares em que você nunca pensou.
fonte
Eu também recomendaria usar o Framework como Primefaces em vez do JSF padrão, eles abordam esses problemas antes da equipe do JSF e. g nos primefaces, você pode definir o envio parcial. Caso contrário, o BalusC explicou bem.
fonte
Ainda é um grande problema no JSF. Por exemplo, se você possui um método
isPermittedToBlaBla
para verificações de segurança e, na sua opinião, possuirendered="#{bean.isPermittedToBlaBla}
, o método será chamado várias vezes.A verificação de segurança pode ser complicada, por exemplo. Consulta LDAP etc. Portanto, você deve evitar isso com
e você deve garantir isso dentro de um bean de sessão por solicitação.
Acho que o JSF deve implementar aqui algumas extensões para evitar várias chamadas (por exemplo, anotação chamada
@Phase(RENDER_RESPONSE)
esse método apenas uma vez após aRENDER_RESPONSE
fase ...)fonte