在MainServlet中,初始化Portlets之后,就开始初始化布局模板了,对应代码是:
- if (_log.isDebugEnabled()) {
- _log.debug("Initialize layout templates");
- }
- try {
- initLayoutTemplates(pluginPackage, portlets);
- }
- ..
它会调用initLayoutTemplates方法:
- protected void initLayoutTemplates(
- PluginPackage pluginPackage, List<Portlet> portlets)
- throws Exception {
- ServletContext servletContext = getServletContext();
- String[] xmls = new String[] {
- HttpUtil.URLtoString(
- servletContext.getResource(
- "/WEB-INF/liferay-layout-templates.xml")),
- HttpUtil.URLtoString(
- servletContext.getResource(
- "/WEB-INF/liferay-layout-templates-ext.xml"))
- };
- LayoutTemplateLocalServiceUtil.init(
- servletContext, xmls, pluginPackage);
- }
首先,第05行取得servlet上下文,然后第07-14行去ROOT下的/WEB-INF中去找2个XML配置文件,我看了下Liferay CE版本下的liferay-layout-template.xml,发现这里定义了2套模板,一套是<standard>,一套是<custom>,,每套下又细分为各种小模板,每种模板都用tpl文件来定义。
然后在第16行,调用LayoutTemplateLocalServiceUtil.init方法来读取这些xml配置文件,并且进行初始化这些模板:
- public static java.util.List<com.liferay.portal.kernel.util.ObjectValuePair<java.lang.String, java.lang.Boolean>> init(
- java.lang.String servletContextName,
- javax.servlet.ServletContext servletContext, java.lang.String[] xmls,
- com.liferay.portal.kernel.plugin.PluginPackage pluginPackage) {
- return getService()
- .init(servletContextName, servletContext, xmls, pluginPackage);
- }
它最终会调用LayoutTemplateLocalServiceImpl类的init()方法:
- public List<ObjectValuePair<String, Boolean>> init(
- String servletContextName, ServletContext servletContext, String[] xmls,
- PluginPackage pluginPackage) {
- List<ObjectValuePair<String, Boolean>> layoutTemplateIds =
- new ArrayList<ObjectValuePair<String, Boolean>>();
- try {
- for (int i = 0; i < xmls.length; i++) {
- Set<ObjectValuePair<String, Boolean>> curLayoutTemplateIds =
- _readLayoutTemplates(
- servletContextName, servletContext, xmls[i],
- pluginPackage);
- Iterator<ObjectValuePair<String, Boolean>> itr =
- curLayoutTemplateIds.iterator();
- while (itr.hasNext()) {
- ObjectValuePair<String, Boolean> ovp = itr.next();
- if (!layoutTemplateIds.contains(ovp)) {
- layoutTemplateIds.add(ovp);
- }
- }
- }
- }
- catch (Exception e) {
- _log.error(e, e);
- }
- return layoutTemplateIds;
- }
所以,它遍历所有xml文件(liferay-layout-template.xml和liferay-layout-template-ext.xml),并且调用_readLayoutTemplates方法来解析这些文件:
- private Set<ObjectValuePair<String, Boolean>> _readLayoutTemplates(
- String servletContextName, ServletContext servletContext,
- String xml, PluginPackage pluginPackage)
- throws Exception {
- Set<ObjectValuePair<String, Boolean>> layoutTemplateIds =
- new HashSet<ObjectValuePair<String, Boolean>>();
- if (xml == null) {
- return layoutTemplateIds;
- }
- Document doc = SAXReaderUtil.read(xml, true);
- Element root = doc.getRootElement();
- Element standardEl = root.element("standard");
- if (standardEl != null) {
- readLayoutTemplate(
- servletContextName, servletContext, layoutTemplateIds,
- standardEl, true, null, pluginPackage);
- }
- Element customEl = root.element("custom");
- if (customEl != null) {
- readLayoutTemplate(
- servletContextName, servletContext, layoutTemplateIds,
- customEl, false, null, pluginPackage);
- }
- return layoutTemplateIds;
- }
这个解析也和Liferay解析xml文件的方式一样,用的SAXParser,然后它会去判断模板类别是standard还是custom,然后用不同的参数解析:
解析方法都是readLayoutTemplate()方法,差别就在于第5个参数,一个是true,一个是false:
读取readLayoutTemplate()方法也很长,它会去获取这个xml配置文件中的所有的<layout-template>元素,然后进行遍历。
- while (itr.hasNext()) {
- com.liferay.portal.kernel.xml.Element layoutTemplate = itr.next();
- ..
先获取<layout-template>的id属性,并且对其进行非null判断,并且将这些id不是null的模板id 添加到layouttemplateIds列表中。
- String layoutTemplateId = layoutTemplate.attributeValue("id");
- if (layoutTemplateIds != null) {
- ObjectValuePair<String, Boolean> ovp =
- new ObjectValuePair<String, Boolean>(
- layoutTemplateId, standard);
- layoutTemplateIds.add(ovp);
- }
- ...
然后基于模板id来从取得模板对象或者创建一个新的:
- LayoutTemplate layoutTemplateModel = layoutTemplates.get(
- layoutTemplateId);
- if (layoutTemplateModel == null) {
- layoutTemplateModel = new LayoutTemplateImpl(layoutTemplateId);
- layoutTemplates.put(layoutTemplateId, layoutTemplateModel);
- }
然后把一些相关信息填充到新建的模板对象中,包括参数传递进来的pluginpackage信息(因为MainServlet的已经初始化了插件包,见这篇博客:http://supercharles888.blog.51cto.com/609344/906049),还有一些从模板xml配置文件中读取的其他信息:
- PluginSetting pluginSetting =
- pluginSettingLocalService.getDefaultPluginSetting();
- layoutTemplateModel.setPluginPackage(pluginPackage);
- layoutTemplateModel.setServletContext(servletContext);
- if (servletContextName != null) {
- layoutTemplateModel.setServletContextName(servletContextName);
- }
- layoutTemplateModel.setStandard(standard);
- layoutTemplateModel.setThemeId(themeId);
- layoutTemplateModel.setName(GetterUtil.getString(
- layoutTemplate.attributeValue("name"),
- layoutTemplateModel.getName()));
- layoutTemplateModel.setTemplatePath(GetterUtil.getString(
- layoutTemplate.elementText("template-path"),
- layoutTemplateModel.getTemplatePath()));
- layoutTemplateModel.setWapTemplatePath(GetterUtil.getString(
- layoutTemplate.elementText("wap-template-path"),
- layoutTemplateModel.getWapTemplatePath()));
- layoutTemplateModel.setThumbnailPath(GetterUtil.getString(
- layoutTemplate.elementText("thumbnail-path"),
- layoutTemplateModel.getThumbnailPath()));
我们从ROOT应用的liferay-layout-template.xml中拿一段,可以很清楚看到它提取了哪些信息:
- <layout-template id="max">
- <template-path>/layouttpl/standard/max.tpl</template-path>
- <wap-template-path>/layouttpl/standard/max.wap.tpl</wap-template-path>
- <thumbnail-path>/layouttpl/standard/max.png</thumbnail-path>
- </layout-template>
吧所有必要信息都填充进模板对象之后,我们就想通过一个http请求去访问模板路径对应的模板内容,:
- try {
- content = HttpUtil.URLtoString(servletContext.getResource(
- layoutTemplateModel.getTemplatePath()));
- }
- ..
然后基于themeId,模板分类(custom还是standard),模板id 这几个字符串的值来构建velocityTemplateId:
- else {
- StringBundler sb = new StringBundler(3);
- sb.append(themeId);
- if (standard) {
- sb.append(LayoutTemplateConstants.STANDARD_SEPARATOR);
- }
- else {
- sb.append(LayoutTemplateConstants.CUSTOM_SEPARATOR);
- }
- sb.append(layoutTemplateId);
- String velocityTemplateId = sb.toString();
最后把上上步获取的模板的HTML内容和上步获取的velocityTemplateId都赋值给模板对象:
- layoutTemplateModel.setContent(content);
- layoutTemplateModel.setColumns(
- _getColumns(velocityTemplateId, content));
现在,每当应用要是用某个模板对象时,它就可以很轻易的找到对应的模板代码片段内容了。