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

 

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

   
   
   
   
  1. <welcome-file-list> 
  2.         <welcome-file>index.htmlwelcome-file> 
  3.         <welcome-file>index.jspwelcome-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> 

 

其中,最值得看的就是

  • else { 
  •         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 这个位置。

     

    之后的步骤见下一篇博客