前几天搞了一个BUG,吧精力耗尽,也激发了我对Liferay这个框架内部的探究欲望。所以这几天端午节准备对Liferay框架启动过程进行深入研究,来满足自己的好奇心。

 

当我们在地址栏中访问http://localhost:8080时,因为Liferay应用本质上也是一个web应用,所以它会去找ROOT应用的web.xml,因为定义了<welcome-file-list>:

 
  
  1. <welcome-file-list> 
  2.         <welcome-file>index.html</welcome-file> 
  3.         <welcome-file>index.jsp</welcome-file> 
  4.     </welcome-file-list> 

 

所以,它会去找index.jsp:

 
  
  1. ... 
  2. <
  3. ... 
  4. String redirect = null
  5.  
  6. LayoutSet layoutSet = (LayoutSet)request.getAttribute(WebKeys.VIRTUAL_HOST_LAYOUT_SET); 
  7.  
  8. if (layoutSet != null) { 
  9.     long defaultPlid = LayoutLocalServiceUtil.getDefaultPlid(layoutSet.getGroupId(), layoutSet.isPrivateLayout()); 
  10.  
  11.     if (defaultPlid != LayoutConstants.DEFAULT_PLID) { 
  12.         Layout layout = LayoutLocalServiceUtil.getLayout(defaultPlid); 
  13.  
  14.         ServicePreAction servicePreAction = (ServicePreAction)InstancePool.get(ServicePreAction.class.getName()); 
  15.  
  16.         ThemeDisplay themeDisplay = servicePreAction.initThemeDisplay(request, response); 
  17.  
  18.         redirect = PortalUtil.getLayoutURL(layout, themeDisplay); 
  19.     } 
  20.     else { 
  21.         redirect = PortalUtil.getPathMain(); 
  22.     } 
  23. else { 
  24.     redirect = PortalUtil.getHomeURL(request); 
  25.  
  26. if (!request.isRequestedSessionIdFromCookie()) { 
  27.     redirect = PortalUtil.getURLWithSessionId(redirect, session.getId()); 
  28.  
  29. response.setHeader(HttpHeaders.LOCATION, redirect); 
  30.  
  31. response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); 
  32. %> 
  33.  
  34. <html> 
  35.  
  36. <head> 
  37.     <title></title> 
  38.     <meta content="1; url=<%= redirect %>" http-equiv="refresh" /> 
  39. </head> 
  40.  
  41. <body onload="javascript:location.replace('<%= redirect %>')"> 
  42.  
  43. </body> 
  44.  
  45. </html> 

 

其中,最值得看的就是<body οnlοad="javascript:location.replace...),它会在DOM树加载完毕之后触发,那么我们感兴趣的是,这个<%=redirect%>到底是什么呢?

 

寻找<%=redirect%>的值

为此,我们去看上面那段代码,因为第一次登录,所以什么用户信息都没有,所以实际上走的是以下的代码段:

 
  
  1. else { 
  2.         redirect = PortalUtil.getPathMain(); 

 

然后这段代码会调用PortalUtil中的:

 
  
  1. public static String getPathMain() { 
  2.         return getPortal().getPathMain(); 
  3.     } 

 

然后调用PortalImpl类中的:

 
  
  1. public String getPathMain() { 
  2.     return _pathMain; 

而这个_pathMain是由该类中以下代码提供的,在PortalImpl构造器中:

 
  
  1. // Paths 
  2.  
  3. _pathProxy = PropsValues.PORTAL_PROXY_PATH; 
  4.  
  5. _pathContext = ContextPathUtil.getContextPath(PropsValues.PORTAL_CTX); 
  6. _pathContext = _pathProxy.concat(_pathContext); 
  7.  
  8. _pathFriendlyURLPrivateGroup = 
  9.     _pathContext + _PRIVATE_GROUP_SERVLET_MAPPING; 
  10. _pathFriendlyURLPrivateUser = 
  11.     _pathContext + _PRIVATE_USER_SERVLET_MAPPING; 
  12. _pathFriendlyURLPublic = _pathContext + _PUBLIC_GROUP_SERVLET_MAPPING; 
  13. _pathImage = _pathContext + PATH_IMAGE; 
  14. _pathMain = _pathContext + PATH_MAIN; 
  15.  
  16. // Groups 

所以_pathMain为_pathContext+PATH_MAIN ,我们依次来解析:

 

解析 _pathContext:

对于_pathContext,它是ContextPathUtil.getContextPath(PropsValues.PORTAL_CTX);

其中PropsValues.PORTAL_CTX是:

 
  
  1. public static final String PORTAL_CTX = PropsUtil.get(PropsKeys.PORTAL_CTX); 

 

它会去找PropsKeys接口中的常量定义:

 
  
  1. public static final String PORTAL_CTX = "portal.ctx"

 

然后找到并替换之后,去执行PropsUtil.get("portal.ctx");

 
  
  1. public static String get(String key) { 
  2.     return _instance._get(key); 

 

它会访问

 
  
  1. private String _get(String key) { 
  2.         return _getConfiguration().get(key); 
  3.     } 

这段代码最终会访问配置文件,然后读取key-value对到Configuration中,我们在portal.properties中找到了portal.ctx的定义:

 
  
  1. ## 
  2. ## Portal Context 
  3. ## 
  4.  
  5.     # 
  6.     # Specify the path of the portal servlet context. This is needed because 
  7.     # javax.servlet.ServletContext did not have access to the context path until 
  8.     # Java EE 5. 
  9.     # 
  10.     # Set this property if you deploy the portal to another path besides root. 
  11.     # 
  12.     portal.ctx=/ 

所以portal.ctx=/

 

解析PATH_MAIN:

它在Portal接口中有定义:

 
  
  1. public static final String PATH_MAIN = "/c"

 

所以综上所述,在PortalImpl构造器中的_pathMain = _pathContext + PATH_MAIN="/"+"/c"="//c"

它就是<%=redirect%>的值。

 

所以当index.jsp中DOM树加载完毕之后,它会去执行:

 
  
  1. <body onload="javascript:location.replace('//c')">  

 

而对于location.replace(String)方法,它的作用是启动一个不可逆的重定向:参见下面这篇文章:

http://www.roseindia.net/javascript/javascript-location-replace.shtml

所以我们现在知道,在DOM树加载完毕后,它吧请求发送到了http://localhost:8080/c 这个位置。

 

之后的步骤见下一篇博客