假设我像这样指定了一个outputText组件:
<h:outputText value="#{ManagedBean.someProperty}"/>
如果我打印一条日志消息,当someProperty的getter被调用并加载页面时,很容易注意到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以这种方式行为?
谢谢你的建议!
我写过一篇关于如何用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;
}
}
这个拦截器用于spring配置文件:
<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中比我的其余代码(或大部分代码)更像一个热点。
Getter方法:
PageNavigationController.getGmapsAutoComplete()
由ui:include在index.xhtml中引用
下面,你会看到pagenavigationcontroller . getmapsautocomplete()在Java Visual VM中是一个热点(性能问题)。如果你再往下看,在屏幕截图上,你会看到getLazyModel(), PrimeFaces惰性数据表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>
解决方案:因为这是一个'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中的热点(甚至不再显示)
分享这个主题,因为许多专家用户建议初级JSF开发人员不要在“getter”方法中添加代码。:)