在如下图所示的添加Portlet的文本显示,有一个非常有趣的特性:
当我们吧文本的代码替换成小写的"add",则最终显示出来的是大写的"Add",而我们把文本替换成小写的"modify",则原封不动显示的是"modify",这是什么特性呢?
为此,我们可以进行一系列的研究,首先我们定位到显示这个文字的代码,它位于/html/portlet/layout_configuration/view_category.jsp的第138行:
- ....
- <div
- class="lfr-portlet-item <c:if test="<%= portletLocked %>">lfr-portlet-used</c:if> <c:if test="<%= portletInstanceable %>">lfr-instanceable</c:if>"
- id="<portlet:namespace />portletItem<%= portlet.getPortletId() %>"
- instanceable="<%= portletInstanceable %>"
- plid="<%= plid %>"
- portletId="<%= portlet.getPortletId() %>"
- title="<%= PortalUtil.getPortletTitle(portlet, application, locale) %>"
- >
- <p><%= PortalUtil.getPortletTitle(portlet, application, locale) %> <a href="javascript:;"><liferay-ui:message key="modify" /></a></p>
- </div>
- ....
然后这个标记<liferay-ui>是定义在liferay-ui.tld文件中:
我们这里可以看到key是一个必须的属性,并且这个标记对应的java类是com.liferay.taglib.ui.MessageTag:
- <tag>
- <name>message</name>
- <tag-class>com.liferay.taglib.ui.MessageTag</tag-class>
- <body-content>JSP</body-content>
- <attribute>
- <name>arguments</name>
- <required>false</required>
- <rtexprvalue>true</rtexprvalue>
- </attribute>
- <attribute>
- <name>key</name>
- <required>true</required>
- <rtexprvalue>true</rtexprvalue>
- </attribute>
- <attribute>
- <name>translateArguments</name>
- <required>false</required>
- <rtexprvalue>true</rtexprvalue>
- </attribute>
- <attribute>
- <name>unicode</name>
- <required>false</required>
- <rtexprvalue>true</rtexprvalue>
- </attribute>
- </tag>
我们去MessageTag中去找寻对这个key参数的处理,在doEndTag方法中:
- public int doEndTag() throws JspException {
- try {
- String value = StringPool.BLANK;
- if (_arguments == null) {
- if (_unicode) {
- value = UnicodeLanguageUtil.get(pageContext, _key);
- }
- else {
- value = LanguageUtil.get(pageContext, _key);
- }
- }
- else {
- if (_unicode) {
- value = UnicodeLanguageUtil.format(
- pageContext, _key, _arguments, _translateArguments);
- }
- else {
- value = LanguageUtil.format(
- pageContext, _key, _arguments, _translateArguments);
- }
- }
- JspWriter jspWriter = pageContext.getOut();
- jspWriter.write(value);
- return EVAL_PAGE;
- }
- catch (Exception e) {
- throw new JspException(e);
- }
- ..
- }
我们看出来,liferay-ui的最终替换的文本是value值,见26行。所以我们的重心在于我们传入的key如何转为这个value.
因为页面上只有key属性,其他属性都没设置,所以这个value的值来自于第11行:
它会去调用LanguageUtil的get方法:
- public static String get(PageContext pageContext, String key) {
- return getLanguage().get(pageContext, key);
- }
进而调用LanguageImpl的 get(pageContext, key)方法,最终调用get(pageContext,key,key)方法:
- public String get(
- PageContext pageContext, String key, String defaultValue) {
- try {
- return _get(pageContext, null, null, key, defaultValue);
- }
- catch (Exception e) {
- if (_log.isWarnEnabled()) {
- _log.warn(e, e);
- }
- return defaultValue;
- }
- }
我们继续跟进第05行,发现它调用_get方法:
- private String _get(
- PageContext pageContext, PortletConfig portletConfig, Locale locale,
- String key, String defaultValue)
- throws Exception {
- if (PropsValues.TRANSLATIONS_DISABLED) {
- return key;
- }
- if (key == null) {
- return null;
- }
- String value = null;
- if (pageContext != null) {
- HttpServletRequest request =
- (HttpServletRequest)pageContext.getRequest();
- ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
- WebKeys.THEME_DISPLAY);
- locale = themeDisplay.getLocale();
- portletConfig = (PortletConfig)request.getAttribute(
- JavaConstants.JAVAX_PORTLET_CONFIG);
- }
- if (portletConfig != null) {
- ResourceBundle resourceBundle = portletConfig.getResourceBundle(
- locale);
- value = ResourceBundleUtil.getString(resourceBundle, key);
- // LEP-7393
- String portletName = portletConfig.getPortletName();
- if (((value == null) || (value.equals(defaultValue))) &&
- (portletName.equals(PortletKeys.PORTLET_CONFIGURATION))) {
- value = _getPortletConfigurationValue(pageContext, locale, key);
- }
- if (value != null) {
- value = LanguageResources.fixValue(value);
- }
- }
- if ((value == null) || value.equals(defaultValue)) {
- value = LanguageResources.getMessage(locale, key);
- }
- if ((value == null) || value.equals(defaultValue)) {
- if ((key.length() > 0) &&
- (key.charAt(key.length() - 1) == CharPool.CLOSE_BRACKET)) {
- int pos = key.lastIndexOf(CharPool.OPEN_BRACKET);
- if (pos != -1) {
- key = key.substring(0, pos);
- return _get(
- pageContext, portletConfig, locale, key, defaultValue);
- }
- }
- }
- if (value == null) {
- value = defaultValue;
- }
- return value;
- }
首先,在第06行进行translation_disabled判断。这个值最终设置在portal.properties文件中:
- #
- # Set this to true to disable language translations. When a translation is
- # requested for the key "first-name", instead of returning "First Name" in
- # English (or in its relevant locale), it will return "first-name".
- #
- translations.disabled=false
所以,默认是启用了translations的而且key是有值的,比如 “add",所以跳过06-12行:
因为PageContext不为null,所以进入第16行:
我们断点跟踪,假如传入的参数为"key",那么第20,23行都执行了,其中第20行的ThemeDisplay,第23行的locale,第25行的portletConfig都不为null:
所以,我们进入第30行:
而第30行的ResourceBundle我们使用的类是com.liferay.portlet.StrutsResourceBundle,
最终把resourceBundle设置进去,这是通过PortletConfigImpl的getResourceBundle(Locale)来完成的:
所以看出来,resourceBundleId为33enUs,而被设置进去的resourceBundle是PortletResourceBundle的实例,其父类是 StrutsResourceBundle:
所以我们终于找到了value与key关联的桥梁:
- value = ResourceBundleUtil.getString(resourceBundle, key);
我们继续跟进到ResourceBundleUtil的getString方法:
- public static String getString(ResourceBundle resourceBundle, String key) {
- ResourceBundleThreadLocal.setReplace(true);
- String value = null;
- try {
- value = resourceBundle.getString(key);
- }
- finally {
- ResourceBundleThreadLocal.setReplace(false);
- }
- if (NULL_VALUE.equals(value)) {
- value = null;
- }
- return value;
- }
- }
这时会进入ResourceBundle的getString()方法:
- public final String getString(String key) {
- return (String) getObject(key);
- }
因为我们的locale是en_US是默认的Locale,所以它会去找资源包文件Language.properties,如果在上面的就使用对应的value,不在上面的就使用默认值,也就是原来的value的值。
所以最终,当我们传入的key为小写的"search"时,
我们在这个资源包中找到:
所以value就为大写的"Search"。
这刚好和我们调试出来的信息匹配:
证明:
我们再举个例子,如果传入key 为"add",则返回value 为"Add",因为资源文件中有add=Add
如果传入key为“modify",则返回value为”modify",因为资源文件中没匹配项