JSF가 게터를 여러 번 호출하는 이유
다음과 같이 outputText 구성 요소를 지정한다고 가정 해 보겠습니다.
<h:outputText value="#{ManagedBean.someProperty}"/>
getter for someProperty
가 호출 될 때 로그 메시지를 인쇄 하고 페이지를로드하는 경우 getter가 요청 당 두 번 이상 호출되고 있음을 알 수 있습니다 (두 번 또는 세 번 내 경우에 발생 함).
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;
}
이것의 주요 문제는 필요없는 개인 변수는 말할 것도없고 많은 상용구 코드를 얻는다는 것입니다.
이 방법의 대안은 무엇입니까? 너무 많은 불필요한 코드없이 이것을 달성 할 수있는 방법이 있습니까? JSF가 이런 식으로 동작하지 못하게하는 방법이 있습니까?
입력 해 주셔서 감사합니다!
이는 지연된 표현식의 특성으로 인해 발생합니다 #{}
( ${}
JSP 대신 Facelets를 사용하는 경우 "레거시"표준 표현식이 정확히 동일하게 작동 함). 지연된 표현식은 즉시 평가 되지 않지만 ValueExpression
오브젝트 로 작성되며 표현식 뒤의 getter 메소드는 코드가 호출 할 때마다 실행됩니다 ValueExpression#getValue()
.
이것은 일반적으로 컴포넌트가 입력 컴포넌트인지 출력 컴포넌트인지에 따라 JSF 요청-응답주기 당 1 ~ 2 회 호출됩니다 ( 여기 학습 ). 그러나 JSF 구성 요소 (예 : <h:dataTable>
및 <ui:repeat>
) 를 반복 하거나 rendered
속성 과 같은 부울 표현식으로 사용하면이 수가 더 많아 질 수 있습니다 . JSF (특히 EL)는 각 호출에서 다른 값을 반환 할 수 있으므로 (예 : 현재 반복되는 데이터 테이블 행에 종속 된 경우) EL 표현식의 평가 결과를 전혀 캐시하지 않습니다 .
EL 표현식을 평가하고 getter 메소드를 호출하는 것은 매우 저렴한 조작이므로 일반적으로 전혀 걱정하지 않아도됩니다. 그러나 어떤 이유로 getter 메소드에서 비싼 DB / 비즈니스 로직을 수행하면 스토리가 변경됩니다. 이것은 매번 다시 실행됩니다!
JSF 지원 Bean의 Getter 메소드 는 Javabeans 스펙에 따라 이미 준비된 특성 만 리턴 하고 더 이상 아무것도 리턴하지 않도록 설계해야합니다 . 고가의 DB / 비즈니스 로직을 전혀 사용하지 않아야합니다. 이를 위해 콩 및 / 또는 (액션) 리스너 메소드를 사용해야합니다. 요청 기반 JSF 라이프 사이클의 특정 시점에서 한 번만 실행 되며 정확히 원하는 것입니다.@PostConstruct
다음은 속성을 사전 설정 /로드하는 다른 모든 올바른 방법에 대한 요약입니다 .
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;
}
}
당신이해야합니다 하지 당신이 그런 CDI와 같은 프록시를 사용하는 콩 관리 프레임 워크를 사용하는 경우가 여러 번 호출 할 수 있기 때문에 작업에 빈의 생성자 또는 초기화 블록을 사용합니다.
제한적인 디자인 요구 사항으로 인해 실제로 다른 방법이 없다면 getter 메소드 내부에 게으른 로딩을 도입해야합니다. 즉, 속성이 null
인 경우 속성 을로드하고 속성에 할당하고 그렇지 않으면 반환합니다.
public SomeObject getSomeProperty() {
// If there are really no other ways, introduce lazy loading.
if (someProperty == null) {
someProperty = loadSomeProperty();
}
return someProperty;
}
이런 식으로 모든 단일 getter 호출에서 고가의 DB / 비즈니스 로직이 불필요하게 실행되는 것은 아닙니다.
또한보십시오:
- 렌더링 된 속성이 getter를 여러 번 호출하는 이유는 무엇입니까?
- 페이지로드시 JSF 관리 Bean 조치 호출
- h : dataTable의 데이터베이스에서 모델을 언제 어떻게로드해야합니까?
- 데이터베이스에서 h : selectOneMenu의 옵션을 채우는 방법은 무엇입니까?
- p : graphicImage 및 StreamedContent를 사용하여 데이터베이스의 동적 이미지 표시
- JSF 페이지에서 EL 변수 정의 및 재사용
- 서버 요청 후 JSF보기의 렌더링 시간 측정
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>
Spring AOP로 JSF bean getter를 캐시하는 방법에 대한 기사 를 작성했습니다 .
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>
그것이 도움이되기를 바랍니다!
원래 PrimeFaces 포럼 @ http://forum.primefaces.org/viewtopic.php?f=3&t=29546에 게시 됨
최근에, 나는 앱의 성능을 평가하고, JPA 쿼리를 튜닝하고, 동적 SQL 쿼리를 명명 된 쿼리로 대체하고, 오늘 아침에 getter 메소드가 Java Visual VM에서 HOT SPOT보다 더 많은 것을 인식했습니다. 내 코드 (또는 대부분의 코드).
게터 방법 :
PageNavigationController.getGmapsAutoComplete()
index.xhtml의 ui : include에 의해 참조 됨
아래에서 PageNavigationController.getGmapsAutoComplete ()가 Java Visual VM의 HOT SPOT (성능 문제)임을 알 수 있습니다. 자세히 살펴보면, 화면 캡처에서 PrimeFaces 게으른 데이터 테이블 게터 메소드 인 getLazyModel ()이 핫스팟이라는 것을 알 수 있습니다. 앱에서. :)
아래의 (원본) 코드를 참조하십시오.
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>
해결책 : 이것은 'getter'메서드이므로 메소드를 호출하기 전에 코드를 이동하고 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 ()는 더 이상 Java Visual VM에서 HOT SPOT이 아닙니다 (더 이상 표시되지 않음).
많은 전문가 사용자들이 주니어 JSF 개발자들에게 'getter'메소드에 코드를 추가하지 말라고 권고했기 때문에이 주제를 공유했습니다. :)
CDI를 사용하는 경우 생산자 방법을 사용할 수 있습니다. 여러 번 호출되지만 첫 번째 호출의 결과는 Bean 범위에 캐시되며 무거운 오브젝트를 계산하거나 초기화하는 게터에 효율적입니다! 자세한 내용은 여기를 참조 하십시오 .
아마도 AOP를 사용하여 구성 가능한 시간 동안 게터의 결과를 캐시하는 일종의 Aspect를 만들 수 있습니다. 이렇게하면 수십 명의 접근자가 상용구 코드를 복사하여 붙여 넣을 필요가 없습니다.
someProperty의 값이 계산 비용이 비싸면 문제가 될 수 있습니다.
이것이 바로 조기 최적화라고합니다. 드문 경우이지만 프로파일 러가 속성 계산이 너무 비싸서 한 번이 아니라 세 번 호출하면 성능에 큰 영향을 미친다고 설명하는 경우 설명에 따라 캐싱을 추가합니다. 그러나 프라 터를 팩터링하거나 게터에서 데이터베이스에 액세스하는 것과 같이 실제로 어리석은 짓을하지 않는 한, 코드는 생각하지 못한 곳에서 열악한 비 효율성을 가질 가능성이 높습니다.
또한 JSF 재고 대신 Primefaces와 같은 프레임 워크를 사용하는 것이 좋습니다. JSF 팀 e보다 먼저 이러한 문제를 해결합니다. g 글 머리 기호로 부분 제출을 설정할 수 있습니다. 그렇지 않으면 BalusC가 잘 설명했습니다.
JSF에서는 여전히 큰 문제입니다. 예를 들어 isPermittedToBlaBla
보안 검사 rendered="#{bean.isPermittedToBlaBla}
방법이 있고 보기에 있으면 메서드가 여러 번 호출됩니다.
보안 점검은 복잡 할 수 있습니다 (예 :). LDAP 쿼리 등을 사용하면
Boolean isAllowed = null ... if(isAllowed==null){...} return isAllowed?
세션 Bean 내에서 요청마다이를 확인해야합니다.
Ich는 JSF가 여러 호출을 피하기 위해 여기에 몇 가지 확장을 구현해야한다고 생각합니다 (예 : 주석은 @Phase(RENDER_RESPONSE)
이 메소드를 RENDER_RESPONSE
단계 후에 한 번만 호출합니다 ...)
참고 URL : https://stackoverflow.com/questions/2090033/why-jsf-calls-getters-multiple-times
'Programing' 카테고리의 다른 글
유효한 SSL 인증서는 어디서 구입할 수 있습니까? (0) | 2020.04.04 |
---|---|
단위 테스트를위한 NUnit 대 Visual Studio 2008의 테스트 프로젝트? (0) | 2020.04.04 |
예외를 던질 때 어느 부분이 비쌉니까? (0) | 2020.04.04 |
Java에서 C # 'var'키워드에 해당하는 것은 무엇입니까? (0) | 2020.04.04 |
AngularJS URL에서 조각 식별자 제거 (# 기호) (0) | 2020.04.04 |