前几天搞了一个BUG,吧精力耗尽,也激发了我对Liferay这个框架内部的探究欲望。所以这几天端午节准备对Liferay框架启动过程进行深入研究,来满足自己的好奇心。
当我们在地址栏中访问http://localhost:8080时,因为Liferay应用本质上也是一个web应用,所以它会去找ROOT应用的web.xml,因为定义了<welcome-file-list>:
- <welcome-file-list>
- <welcome-file>index.html</welcome-file>
- <welcome-file>index.jsp</welcome-file>
- </welcome-file-list>
所以,它会去找index.jsp:
- ...
- <%
- ...
- String redirect = null;
- LayoutSet layoutSet = (LayoutSet)request.getAttribute(WebKeys.VIRTUAL_HOST_LAYOUT_SET);
- if (layoutSet != null) {
- long defaultPlid = LayoutLocalServiceUtil.getDefaultPlid(layoutSet.getGroupId(), layoutSet.isPrivateLayout());
- if (defaultPlid != LayoutConstants.DEFAULT_PLID) {
- Layout layout = LayoutLocalServiceUtil.getLayout(defaultPlid);
- ServicePreAction servicePreAction = (ServicePreAction)InstancePool.get(ServicePreAction.class.getName());
- ThemeDisplay themeDisplay = servicePreAction.initThemeDisplay(request, response);
- redirect = PortalUtil.getLayoutURL(layout, themeDisplay);
- }
- else {
- redirect = PortalUtil.getPathMain();
- }
- }
- else {
- redirect = PortalUtil.getHomeURL(request);
- }
- if (!request.isRequestedSessionIdFromCookie()) {
- redirect = PortalUtil.getURLWithSessionId(redirect, session.getId());
- }
- response.setHeader(HttpHeaders.LOCATION, redirect);
- response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
- %>
- <html>
- <head>
- <title></title>
- <meta content="1; url=<%= redirect %>" http-equiv="refresh" />
- </head>
- <body onload="javascript:location.replace('<%= redirect %>')">
- </body>
- </html>
其中,最值得看的就是<body onload="javascript:location.replace...),它会在DOM树加载完毕之后触发,那么我们感兴趣的是,这个<%=redirect%>到底是什么呢?
寻找<%=redirect%>的值
为此,我们去看上面那段代码,因为第一次登录,所以什么用户信息都没有,所以实际上走的是以下的代码段:
- else {
- redirect = PortalUtil.getPathMain();
- }
然后这段代码会调用PortalUtil中的:
- public static String getPathMain() {
- return getPortal().getPathMain();
- }
然后调用PortalImpl类中的:
- public String getPathMain() {
- return _pathMain;
- }
而这个_pathMain是由该类中以下代码提供的,在PortalImpl构造器中:
- // Paths
- _pathProxy = PropsValues.PORTAL_PROXY_PATH;
- _pathContext = ContextPathUtil.getContextPath(PropsValues.PORTAL_CTX);
- _pathContext = _pathProxy.concat(_pathContext);
- _pathFriendlyURLPrivateGroup =
- _pathContext + _PRIVATE_GROUP_SERVLET_MAPPING;
- _pathFriendlyURLPrivateUser =
- _pathContext + _PRIVATE_USER_SERVLET_MAPPING;
- _pathFriendlyURLPublic = _pathContext + _PUBLIC_GROUP_SERVLET_MAPPING;
- _pathImage = _pathContext + PATH_IMAGE;
- _pathMain = _pathContext + PATH_MAIN;
- // Groups
所以_pathMain为_pathContext+PATH_MAIN ,我们依次来解析:
解析 _pathContext:
对于_pathContext,它是ContextPathUtil.getContextPath(PropsValues.PORTAL_CTX);
其中PropsValues.PORTAL_CTX是:
- public static final String PORTAL_CTX = PropsUtil.get(PropsKeys.PORTAL_CTX);
它会去找PropsKeys接口中的常量定义:
- public static final String PORTAL_CTX = "portal.ctx";
然后找到并替换之后,去执行PropsUtil.get("portal.ctx");
- public static String get(String key) {
- return _instance._get(key);
- }
它会访问
- private String _get(String key) {
- return _getConfiguration().get(key);
- }
这段代码最终会访问配置文件,然后读取key-value对到Configuration中,我们在portal.properties中找到了portal.ctx的定义:
- ##
- ## Portal Context
- ##
- #
- # Specify the path of the portal servlet context. This is needed because
- # javax.servlet.ServletContext did not have access to the context path until
- # Java EE 5.
- #
- # Set this property if you deploy the portal to another path besides root.
- #
- portal.ctx=/
所以portal.ctx=/
解析PATH_MAIN:
它在Portal接口中有定义:
- public static final String PATH_MAIN = "/c";
所以综上所述,在PortalImpl构造器中的_pathMain = _pathContext + PATH_MAIN="/"+"/c"="//c"
它就是<%=redirect%>的值。
所以当index.jsp中DOM树加载完毕之后,它会去执行:
- <body onload="javascript:location.replace('//c')">
而对于location.replace(String)方法,它的作用是启动一个不可逆的重定向:参见下面这篇文章:
http://www.roseindia.net/javascript/javascript-location-replace.shtml
所以我们现在知道,在DOM树加载完毕后,它吧请求发送到了http://localhost:8080/c 这个位置。
之后的步骤见下一篇博客