引入:

大家都知道我们可以用Session来存储属性,但是Liferay PortletSession有些特殊,它可以用不同的scope来存储不同的属性,我们的sample代码如下:

154156521.png

我们这里就来研究PortletSession的细节,比如,它是如何做到吧属性存放在不同的scope上的。


调试分析

对于第一种情况:session.getAttribute("foo",PortletSession.PORTAL_SCOPE),它其实调用的是PortletSessionImpl的getAttribute()方法的如下分支:

154430719.png

其实,从宏观上看,它依然是从httpSession()上取得一个属性,只不过这个属性的key不再是原来的key,而是一个封装值,这个封装的值是通过_getPortletScopeName(name)获取的。我们看下这个方法的细节,它会调用_getPortletScopeName(_portletName,_plid,name)方法,也就是这个产生的key由portletName,portletLayoutId和要求的属性的key 共同决定的:


具体,它的调用可以看出:

154830287.png

它会在getPortletScope(portletName,plid调用之后,后面再跟上?name 来获取最终的key.


而getPortletScope(portletName,plid)的调用则非常简单,如下:

155031834.png

它会依次附加上Portlet_SCOPE_NAMESPACE(javax.portlet.p.)和portletName,_LAYOUT_,和plid.

因为上述参数都已知,所以很容易就得出了getPortletScope(portletName,plid)的返回值:

155337795.png


这样,再拼接上问号 ?和属性的原始key,之后,很快就可以得到最终存储在httpSession上的key了。

这个值是javax.portlet.p.logsearchportlet_WAR_logsearchportlet_LAYOUT_21601?foo


对于第二种情况,session.getAttribute("bar",PortletSession.APPLICATION_SCOPE),它其实调用的是HttpSession的getAttribute()方法的如下分支:

161007833.png

所以直截了当,它直接是从httpSession上根据传入的属性key获取对应的属性value。


总结:

通过这个简单的小实验,我们可以看出以下结论:

(1)PortletSession上获取变量在PORTLET_SCOPE和APPLICATION_SCOPE其实本质没什么不同,都是从HttpSession上获取变量,只不过变量的key 不同,对于PORTLET_SCOPE,这个key在原始的key前面加了很多字符串作为最终的key,而对于APPLICATION_SCOPE,则这个key就是最终的key.

(2)对于PORTLET_SCOPE,最终属性的 key字符串模式为Portlet名字空间+portletName+(_LAYOUT_)+portletLayoutId+"?"+原始keyPortlet名字空间.所以它portletName,portletLayoutId和原始key共同决定,我们也可以把这个结论进行延伸。因为不同的portlet的portletName肯定不同进而这个key肯定不同,所以PortletSession无法共享其他portlet放在PORTLET_SCOPE上的属性,就算这些portlet位于同一个session中。因为同一个portlet如果多次部署,那么layoutId肯定不同进而这个key肯定不同,所以PortletSession无法共享这个portlet部署多次后,在以前实例中存放在PORTLET_SCOPE上的属性,就算这些portlet位于同一个session中。 但是,同一个session下的多个portlet或者单portlet的多个实例依然可以通过APPLICATION_SCOPE来共享属性。

(3)正是因为PortletSession可以通过对属性的key进行改造来区分APPLICATION_SCOPE和PORTLET_SCOPE上的属性,所以PortletSession的示意图如下:

160723163.png