AOM组件与标准的jsf组件并没有什么不同,既然jsf组件在request bean下可以正常工作,所以从本质上说AOM组件应该也是可以在request bean下正常工作的
但事实上AOM中有些组件、某些场景下会发现用request确实有问题,下面列举一下常见的场景
1. datagrid,dataView,tree等数据组件
这些组件完全展现出来其实是信赖两次请求的,第一次请求渲染出组件的基本代码,第二次请求取得展现组件所需要的数据,如果你用firebug观察一个带有datagrid页面的请求响应就知道我说的什么意思
如果组件的dataProvider在第二次请求时也能取到数据当然不会有什么问题,但有时会写出下面的代码
@ManagedBean(name="testBean",scope=ManagedBeanScope.REQUEST) public class TestBean{ @Bind(id="grid",attribute="value") private List<Data> datas; @BeforeRender public void beforeRender(boolean isPostback){ if(!isPostback){ datas = new ArrayList<Data>(); datas.add(new Data("xxx")); .... } } }
请记住上面是request bean,在每一次请求时bean会重新创建,那么第二次请求时取出的datas可能根本没被初始化,怎么可能取出数据来?
2. bean之间互相注入
有很多地方需要跨页面传递参数,如果全用session bean, 将有一个简单的方法即注入其它的bean,如下代码
@ManagedBean(name="test1Bean",scope=ManagedBeanScope.SESSION) public class Test1Bean{ private String param1; //getter and setter function .... } @ManagedBean(name="test2Bean",scope=ManagedBeanScope.SESSION) public class Test2Bean{ @Inject private Test1Bean test1Bean; @Action public void action(){ System.out.println(test1Bean.getParam1()); } }
但上面的代码一旦换成request bean就会有问题,test1Bean是全新创建的,调用test1Bean.getParam1()当然不能取到值
3. 多次ajax请求
有可能你在一个button的action方法中将Managed Bean中一个字段改成某个值,在另一个button的action方法中又需要访问这个字段,如下代码
@ManagedBean(name="testBean",scope=ManagedBeanScope.SESSION) public class TestBean{ private String state = null; @Action public void action1(){ state = "start"; } @Action public void action2(){ System.out.println(state); } }
如果是request bean上面代码中action2方法显然打印不出"start"
解决方案:
针对这些问题都可以采用把跨请求的数据放在sessionMap的方式解决
//dataGrid二次取数 @ManagedBean(name="testBean",scope=ManagedBeanScope.REQUEST) public class TestBean{ @Bind(id="grid",attribute="value") private List<Data> datas; public void setDatas(List<Data> datas){ Map<String,Object> sessionMap = FacesContext.getExternalContext().getSessionMap(); sessionMap.put("datas_key", datas); } public List<Data> getDatas(){ Map<String,Object> sessionMap = FacesContext.getExternalContext().getSessionMap(); return (List<Data>)sessionMap.get("datas_key"); } @BeforeRender public void beforeRender(boolean isPostback){ if(!isPostback){ datas = new ArrayList<Data>(); datas.add(new Data("xxx")); .... setDatas(datas); } } } //跨页面传参数 @ManagedBean(name="test1Bean",scope=ManagedBeanScope.REQUEST) public class Test1Bean{ private String param1; //getter and setter function .... @Action public String redirect(){ Map<String,Object> sessionMap = FacesContext.getExternalContext().getSessionMap(); sessionMap.put("param1_key",param1); return "view:redirect:test2"; } } @ManagedBean(name="test2Bean",scope=ManagedBeanScope.REQUEST) public class Test2Bean{ @Action public void action(){ Map<String,Object> sessionMap = FacesContext.getExternalContext().getSessionMap(); String param1 = sessionMap.remove("param1_key"); System.out.println(param1); } }
其实目前AOM对于这些保存某些field的状态已经有@SaveState之类的解决方案,但目前还存在一些问题,我们正在想办法积极解决SaveState在某些场景下保存状态不成功的问题
一直对把一些需要保存状态的东西存在session里耿耿于怀,什么时候清理这些东西了?
有人很自然地会说离开页面跳转到其它页面的时候可以清除这些东西,实现方法更简单了,在facelets创建view时直接将存放这些状状状态的map删除掉,示例代码如下:
/** * 摘自primeFaces的optimus-0.8.0-sources.jar * org.primefaces.optimus.application.OptimusViewHandler.java */ public class OptimusViewHandler extends ViewHandlerWrapper{ private ViewHandler base; public OptimusViewHandler(ViewHandler base) { this.base = base; } public UIViewRoot createView(FacesContext facesContext, String viewId) { Map<String, Object> sessionMap = facesContext.getExternalContext().getSessionMap(); sessionMap.put(Constants.VIEW_SCOPE_KEY, new HashMap<String, Object>()); return getWrapped().createView(facesContext, viewId); } protected ViewHandler getWrapped() { return base; } }
真的只是这么简单吗?
其实不然,举个例子,例如一个页面使用iframe方式引入另一个页面,那么在iframe里面的页面如果发生跳转会清空ViewScopeMap,这个时候外面页面在ViewScopeMap中存放的状态也随之清空了,这明显不合理
进而想到是不是应该使用某个参数来表征不同的页面使用同一个viewScope(这里再叫ViewScope也许不太合适了,也许应该说ConversationScope)
那ConversationScope中存放的东西什么时候销毁呢?按照sessionScope的设计,应该设置一个ConversationTimeout的东东,
当会话在ConversationTimeout的时间段内还没有再次被激活,则销毁Conversation
这样想着,一个在jsf中ConversationScope也有了雏形
今天上javaeye看新闻,看到weld的1.0RC出了,之前就研究过webbeans规范,突然想起来它里面就有说到要实现ConversationScope,赶紧把weld下载下来
研究了一下它是怎么实现的,结果觉得很惊奇,它的实现方法跟我上面想的差不多(当然它比我想的更加全面,代码也写得精妙很多,比如还考虑了ConversationAccessConcurrentTimeout)
看样子可以参照它将conversationScope实现出来
PS:不得不惊叹Gavin King确实是个牛人,考虑问题很全面仔细,以后还是应该多看看牛人的代码
uddy状态也随之清空了,这明显不合理状态也随之清空了,这明显不合理