Portlet 生命周期

init() =〉 render() =〉 processAction() =〉 processEvent() =〉 serveResource() =〉destroy()

  • init()

    在Liferay容器部署portlet时,启动portlet实例化

    init有两个写法:

    •  public void init() throws PortletException
    •  public void init(PortletConfig config) throws PortletException

      PortletConfig对象用于读取portlet配置(定义在portlet.xml)

  • render()

    HTML的UI输出,是最常用到的方法了,一个例子

@Override
public void render(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
_log.info(" 做些你自己定义的事情");
super.render(request, response);
}

    值得注意的是:

    • 在render()的一般最后一行,需要写这句:super.render(request,response),如果不写会不能正常工作,会造成GenericPortlet继承链的断裂(render() 在GenericPortlet中被重写);
    • Portlet不能直接访问ServletRequest和ServletResponse;
    • RenderRequest和RenderResponse是接口,继承于PortletRequest和PortletResponse;
    • 如果一个页面有多个Portlets,当每次页面刷新,所有Portlets实例的render()就会被全部调用一次;
    • 有趣的是,Portlet规范并没有一个排序的机制,去安排这些Portlets的render()顺序,这证明了Portlet的独立性,如果要定制开发时序的加载,那必须自己去实现一个GenericPortlet的子类,或者直接扩展MVCPortlet,增加一个加载队列。
  • processAction()

    action 处理,后面再详细介绍,这里只要知道ActionRequest也是一个接口继承

    

  • processEvent()

    监听时间处理

  • serveResource()

    通过resource URL处理资源

  • destroy()

    portlet卸载时的处理

    

Portlet 容器负责的工作

  • 装载portlet类
  • 创建portlet实例
  • 初始化
  • 向portlet实例发送用户请求(request)
  • 销毁实例(当容器卸载portlet)

Portlet和Servlet的区别

见:

Liferay7 BPM门户开发之15: Liferay开发体系简介

Portlet类的层次

  • GenericPortlet实现了Portlet接口;
  • LiferayPortlet是GenericPortlet的子类,并且提供了一些额外方法;
  • MVCPortlet继承与LiferayPortlet.提供了用于MVC架构的一些方法;
  • UserCustomPortlet (用户定义portlet)继承与MVCPorrtlet;

URLs 传递

  • Portlet在开发调试时不如servlet中那样方便直接,无法定义静态指定地址url,而是通过以下几种方式:

    •   Render URL:call render method,用于界面控制
    •   Action URL:call action method,用于服务调用
    •   Resource URL:call serve resource method,用于访问资源

Render 处理

首先,介绍Render URL,有四种方式:

方式1、Portlet Tag (portlet:renderURL)

view.jsp

<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>

<%@page import="com.liferay.portal.kernel.portlet.LiferayPortletMode"%>
<%@page import="com.liferay.portal.kernel.portlet.LiferayWindowState"%>

<portlet:renderURL var="renderUrl"
windowState="<%=LiferayWindowState.NORMAL.toString() %>" copyCurrentRenderParameters="true" portletMode="<%=LiferayPortletMode.VIEW.toString()%>">
<portlet:param name="param" value="XXXXXX"/>
</portlet:renderURL> <a href="${renderUrl}">RenderURL Created by Portlet Tag</a>
render()
private static final Log _log = LogFactoryUtil.getLog(YourPortlet.class.getName());

 @Override
public void render(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
_log.info(" This is render method");
String data = request.getParameter("param");
String data1= ParamUtil.getString(request, "param","");
super.render(request, response);
}

最终会实际产生以下URL:

http://localhost:8080/web/portletTest/test?

  • p_p_id=renderURLbyportletTag_WAR_renderURLbyportlet
  • &p_p_lifecycle=0
  • &p_p_state=normal
  • &p_p_mode=view
  • &p_p_col_id=column-1
  • &p_p_col_count=1
  • &_renderURLbyportletTag_WAR_renderURLbyportlet_param=XXXXXX

p_p_id 是portlet Id

p_p_lifecycle是生命周期的当前阶段,定义是:0 – render;1 – action;2 – serve resource;

p_p_col_id是多列布局中的当前页第几列;

p_p_col_count是当前页的布局总列数;

最后一个就是用户定自定url参数了,由jsp传递;

方式2、PortletURLFactoryUtil 方式

这种是服务端直接控制,jsp基本不用写什么逻辑,后台控比较喜欢用

render()
@Override
public void render(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
_log.info(" This is render method of RenderURLByJavaAPIPortlet");
String data = request.getParameter("param");
String data1= ParamUtil.getString(request, "param",""); ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(WebKeys.THEME_DISPLAY);
PortletURL renderUrl = PortletURLFactoryUtil.create(request, themeDisplay.getPortletDisplay().getId(), themeDisplay.getPlid(), PortletRequest.RENDER_PHASE);
renderUrl.setWindowState(LiferayWindowState.NORMAL);
renderUrl.setPortletMode(LiferayPortletMode.VIEW);
renderUrl.setParameter("param", "This parameter comes from Render URL generated with Java API");
request.setAttribute("renderUrlByJavaAPI", renderUrl.toString());  super.render(request, response);
}

其中,plid 是page layout id

在jsp中,只用一行代码:

<a href="${renderUrlByJavaAPI}">Render Url created by Java API</a>

还有一种灵活方式,即可以由jsp来直接输出

<%@ taglib uri="http://liferay.com/tld/theme" prefix="liferay-theme" %>
<liferay-theme:defineObjects/> <%
PortletURL renderUrlFromJSP = renderResponse.createRenderURL();
renderUrlFromJSP.setParameter("param1", "This portletULR is created with API in JSP");
renderUrlFromJSP.setWindowState(LiferayWindowState.NORMAL);
renderUrlFromJSP.setPortletMode(LiferayPortletMode.VIEW); %>
<a href="<%=renderUrlFromJSP%>">Render Url created by JSP</a>

方式3:Liferay Tag (liferay-portlet:renderURL)

 view.jsp

<%@ taglib uri="http://liferay.com/tld/portlet" prefix="liferay-portlet" %>
<liferay-portlet:renderURL var="openPortletURL" copyCurrentRenderParameters="true" portletMode="<%=LiferayPortletMode.VIEW.toString() %>"
windowState="<%=LiferayWindowState.NORMAL.toString()%>">
<liferay-portlet:param name="param" value="This is from Liferay TAG"/>
</liferay-portlet:renderURL> <a href="${openPortletURL}">Render Url created by Liferay TAG in JSP</a>

render() 同第一种写法(Portlet Tag方式)

方式4:JavaScript (by AUI)

view.jsp

<%@page import="com.liferay.portal.kernel.portlet.LiferayWindowState"%>
<%@page import="com.liferay.portal.kernel.portlet.LiferayPortletMode"%>
<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>
<%@ taglib uri="http://liferay.com/tld/aui" prefix="aui" %>
<%@ taglib uri="http://liferay.com/tld/theme" prefix="liferay-theme" %> <liferay-theme:defineObjects/>
<portlet:defineObjects /> <a id="renderURLWithJS" href=""> This render URL link is created with Javascript</a> <aui:script>
AUI().use('liferay-portlet-url', function(A) {
var renderUrl1 = Liferay.PortletURL.createRenderURL();
renderUrl1.setWindowState("<%=LiferayWindowState.NORMAL.toString() %>");
renderUrl1.setParameter("param","This value comes from Javascript");
renderUrl1.setPortletMode("<%=LiferayPortletMode.VIEW %>");
renderUrl1.setPortletId("<%=themeDisplay.getPortletDisplay().getId() %>"); A.one("#renderURLWithJS").set('href',renderUrl1.toString());
alert("renderUrl1 is ->"+renderUrl1.toString());
alert(A.one("#renderURLWithJS").attr("href")); }); </aui:script> 这种比较不常用

render() 同第一种写法(Portlet Tag方式)

action 处理

在涉及页面处理业务逻辑,或其他Portlet交互,或者Form提交Action时,就需要我们定义Action方法,actionURL 就是用来传递Action的id

它是和jsp一对一对应的,比如jsp中:

view.jsp

<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>

<portlet:defineObjects />

User Name is : <b> ${userName}</b>

<portlet:actionURL name="actionMethod1" var="sampleActionMethodURL">
</portlet:actionURL> <form action="${sampleActionMethodURL}" method="post">
UserName :-<input type="text" name="userName"><br>
<input type="submit" value="Submit">
</form> <portlet:actionURL name="addName" var="addNameUrl"></portlet:actionURL> <a href="${addNameUrl}">Add Name</a>

前提还要定义portlet.xml

portlet.xml首先要定义

<portlet-name>custom-liferaymvc</portlet-name>
<display-name>Custom Liferaymvc</display-name>
<portlet-class>com.companyname.portlet.CustomMVCPortlet</portlet-class>
<init-param>
<name>view-jsp</name>
<value>/jsp/view.jsp</value>
</init-param>

编写一个继承MVCPortlet的类

@ProcessAction(name="addName")
public void addName(ActionRequest actionRequest,
ActionResponse actionResponse) throws IOException, PortletException, PortalException, SystemException{
actionRequest.setAttribute("userName", "Wangxin");
} @ProcessAction(name="actionMethod1")
public void sampleActionMethod(ActionRequest request, ActionResponse response)
throws IOException, PortletException, PortalException, SystemException{
_log.info("This is sampleActionMethod ...");
String userName = ParamUtil.get(actionRequest, "userName", StringPool.BLANK);
actionRequest.setAttribute("userName", userName);
}

如果跟踪一下,会是自动产生以下url:

http://localhost:8080/web/home?p_auth=e6cvA8tX
&p_p_id=testactionmethod_WAR_testactionmethodportlet
&p_p_lifecycle=1
&p_p_state=normal
&p_p_mode=view
&p_p_col_id=column-1
&_testactionmethod_WAR_testactionmethodportlet_javax.portlet.action=actionMethod1

另外PortletURLFactoryUtil 是一种后台产生action url的常用方式

java code:

    @Override
public void render(RenderRequest request, RenderResponse response)
throws PortletException, IOException { ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(WebKeys.THEME_DISPLAY);
PortletURL actionUrl = PortletURLFactoryUtil.create(request, themeDisplay.getPortletDisplay().getId(), themeDisplay.getPlid(), PortletRequest.ACTION_PHASE);
actionUrl.setWindowState(LiferayWindowState.NORMAL);
actionUrl.setPortletMode(LiferayPortletMode.VIEW);
actionUrl.setParameter("javax.portlet.action", "actionMethodByJavaApi");
actionUrl.setParameter("sampleParam", "This parameter comes from Action URL generated with Java API");
request.setAttribute("actionUrlByJavaAPI", actionUrl.toString());
super.render(request, response);
} @ProcessAction(name="actionMethodByJavaApi")
public void actionMethodByJavaApi(ActionRequest request, ActionResponse response)
throws IOException, PortletException, PortalException, SystemException{
String sampleParam = ParamUtil.get(request, "sampleParam", "defaultValue");
_log.info("Sample Param is ::"+sampleParam);
} view.jsp <%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>
<portlet:defineObjects />
<a href="${actionUrlByJavaAPI}">Action Url created by Java API in Portlet Class</a>
<br>

还有两种方式就不介绍了:
liferay-portlet:actionURL 和 JavaScript (AUI module – Liferay.PortletURL)

一个portlet交互的流程图

resource 处理

Render & action是最早被定义的规范,见portlet specification 1.0 (JSR-168).
resource serving 被定义于portlet specification 2.0 (JSR-286)
一句话概括serve resource做的事情:动态产生输出到客户端,负责向客户端发送动态内容
比如:下载文件,发送一个excle报表,发送xml、json文件......,这些在Render 周期是无法实现的

serve resource过程

这里有比较好的介绍:
http://www.oracle.com/technetwork/java/jsr286-2-141964.html

7.0版的Command设计模式

在7.0版,整个一套处理设计为注解方式了,摒弃了满屏混乱的XML配置,更加清晰直观,代码结构更好

有三种命令接口:

  • MVCActionCommand
  • MVCRenderCommand
  • MVCResourceCommand

其中

BaseMVCActionCommand(class)实现=> MVCActionCommand(interface)

定制Portlet

@Component(
immediate = true,
property = {
"com.liferay.portlet.css-class-wrapper=portlet-hello-world",
"com.liferay.portlet.display-category=category.sample",
"com.liferay.portlet.icon=/icons/hello_world.png",
"com.liferay.portlet.preferences-owned-by-group=true",
"com.liferay.portlet.private-request-attributes=false",
"com.liferay.portlet.private-session-attributes=false",
"com.liferay.portlet.remoteable=true",
"com.liferay.portlet.render-weight=50",
"com.liferay.portlet.use-default-template=true",
"javax.portlet.display-name=Hello World",
"javax.portlet.expiration-cache=0",
"javax.portlet.init-param.always-display-default-configuration-icons=true",
"javax.portlet.name=" + HelloWorldPortletKeys.HELLO_WORLD,
"javax.portlet.resource-bundle=content.Language",
"javax.portlet.security-role-ref=guest,power-user,user",
"javax.portlet.supports.mime-type=text/html"
},
service = Portlet.class
)
public class HelloWorldPortlet extends MVCPortlet {
......
}

action command

比如一个action URL用于编辑实体,指向edit_entry.jsp

<portlet:actionURL name="/blogs/edit_entry" var="editEntryURL" />

action URL被触发后,匹配的action类将会被处理,可以有两种方式实现action command,一是实现接口MVCActionCommand,二是直接继承BaseMVCActionCommand类,liferay官方是推荐第二种,因为已经实现了很多有用的方法。

你的XXXXMVCActionCommand 类必需有@Componet注解,是为了OSGI容器反射得到类,例子:

@Component(
immediate = true,
property = {
"javax.portlet.name=Your_portlet_name",
"mvc.command.name=/blogs/edit_entry"
},
service = MVCActionCommand.class
)
public class YourMVCActionCommand extends BaseMVCActionCommand {
// your action
}

比如一个增加实体的方法

public void addGuestbook(ActionRequest request, ActionResponse response)
throws PortalException, SystemException { ServiceContext serviceContext = ServiceContextFactory.getInstance(
Guestbook.class.getName(), request); String name = ParamUtil.getString(request, "name"); try {
_guestbookService.addGuestbook(serviceContext.getUserId(),
name, serviceContext); SessionMessages.add(request, "guestbookAdded"); } catch (Exception e) {
SessionErrors.add(request, e.getClass().getName()); response.setRenderParameter("mvcPath",
"/html/guestbook/edit_guestbook.jsp");
} }

MVC render command

需要定义三件事情:

  • 实现MVCRenderCommand interface;
  • 在视图创建portlet render URL;
  • Component注解定义MVCRenderCommand service;

一个最简单的render

@Component(
immediate = true,
property = {
"javax.portlet.name=" + HelloWorldPortletKeys.HELLO_WORLD,
"javax.portlet.name=" + HelloWorldPortletKeys.HELLO_OTHER_WORLD,
"mvc.command.name=/hello/edit_entry"
},
service = MVCRenderCommand.class
)
public class EditEntryMVCRenderCommand implements MVCRenderCommand { @Override
public String render(
RenderRequest renderRequest, RenderResponse renderResponse)
throws PortletException { try {
Blog entry = ActionUtil.getEntry(renderRequest);
}
catch (Exception e) {
if (e instanceof NoSuchEntryException ||
e instanceof PrincipalException) { SessionErrors.add(renderRequest, e.getClass()); return "/hello/error.jsp";
}
else {
throw new PortletException(e);
}
} return "/hello/edit_entry.jsp";
}
}

javax.portlet.name被定义了2个,这代表它可以在2个portlet中使用。

URL:

<portlet:renderURL var="editEntryURL">
<portlet:param name="mvcRenderCommandName" value="/hello/edit_entry" />
<portlet:param name="entryId" value="<%= String.valueOf(entry.getEntryId()) %>" />
</portlet:renderURL>

一个较详细的例子:

@Override
public void render(RenderRequest renderRequest,
RenderResponse renderResponse) throws PortletException, IOException { try {
ServiceContext serviceContext = ServiceContextFactory.getInstance(
Guestbook.class.getName(), renderRequest); long groupId = serviceContext.getScopeGroupId(); long guestbookId = ParamUtil.getLong(renderRequest, "guestbookId"); List<Guestbook> guestbooks = _guestbookService
.getGuestbooks(groupId); if (guestbooks.size() == ) {
Guestbook guestbook = _guestbookService.addGuestbook(
serviceContext.getUserId(), "Main", serviceContext); guestbookId = guestbook.getGuestbookId(); } if (!(guestbookId > )) {
guestbookId = guestbooks.get().getGuestbookId();
} renderRequest.setAttribute("guestbookId", guestbookId); } catch (Exception e) { throw new PortletException(e);
} super.render(renderRequest, renderResponse); }

MVC serveResource command

@Component(
property = {
"javax.portlet.name=" + LoginPortletKeys.FAST_LOGIN,
"javax.portlet.name=" + LoginPortletKeys.LOGIN,
"mvc.command.name=/login/captcha"
},
service = MVCResourceCommand.class
)
public class CaptchaMVCResourceCommand implements MVCResourceCommand { @Override
public boolean serveResource(
ResourceRequest resourceRequest, ResourceResponse resourceResponse) { try {
CaptchaUtil.serveImage(resourceRequest, resourceResponse); return false;
}
catch (Exception e) {
_log.error(e, e); return true;
}
} private static final Log _log = LogFactoryUtil.getLog(
CaptchaMVCResourceCommand.class);
}

目前7.0的文档还太少。

Liferay7 BPM门户开发之17: Portlet 生命周期的更多相关文章

  1. Liferay7 BPM门户开发之33: Portlet之间通信的3种方式(session、IPC Render Parameter、IPC Event、Cookies)

    文章介绍了5种方式,4种是比较常用的: Portlet session IPC Public Render Parameters IPC Event Cookies 参考地址: https://web ...

  2. Liferay7 BPM门户开发之28: Portlet文件上传,及实体类同步更新上传

    抓住核心 . Liferay文件上传的核心就是使用UploadPortletRequest类 继承关系java.lang.Object extended byjavax.servlet.Servlet ...

  3. Liferay7 BPM门户开发之10: 通用流程实现从Servlet到Portlet(Part1)

    开发目的: 实现通用流程自动化处理(即实现不需要hardcode代码的bpm统一处理后台,仅需要写少量前端html form代码和拖拽设计BPM定义) 既可独立运行或可依托于Liferay或依托其它门 ...

  4. Liferay7 BPM门户开发之37: Liferay7下的OSGi Hook集成开发

    hook开发是Liferay客制扩展的一种方式,比插件灵活,即可以扩展liferay门户,也能对原有特性进行更改,Liferay有许多内置的服务,比如用hook甚至可以覆盖Liferay服务. 可作为 ...

  5. Liferay7 BPM门户开发之36: 使用Portlet filters过滤器做切面AOP

    使用Portlet filters过滤器做切面AOP Portlet Filters定义于JSR286 Java Portlet Specification 2.0 Portlet Filters是为 ...

  6. Liferay7 BPM门户开发之15: Liferay开发体系简介

    Liferay SDK 开发体系 主要分6种: Portlet Hook Theme Layout Templates Web Modules Ext Portlet :类似于servlet的web组 ...

  7. Liferay7 BPM门户开发之12:acitiviti和liferay用户权限体系集成

    写到第12章才出现Liferay的内容,希望可以厚积薄发. 我们的目标是不使用不维护Activiti的用户组织架构,只维护Liferay的体系,这样的好处是非常明显的,即不用做组织架构的同步工作. 原 ...

  8. Liferay7 BPM门户开发之27: MVC Portlet插件工程开发

    官网上的教材说实话实在精简不清晰. https://dev.liferay.com/develop/tutorials/-/knowledge_base/7-0/creating-an-mvc-por ...

  9. Liferay7 BPM门户开发之44: 集成Activiti展示流程列表

    处理依赖关系 集成Activiti之前,必须搞清楚其中的依赖关系,才能在Gradle里进行配置. 依赖关系: 例如,其中activiti-engine依赖于activiti-bpmn-converte ...

随机推荐

  1. Java虚拟机 内存区域划分

    (图片来自https://www.cnblogs.com/whgk/p/6138522.html) 先从线程私有区开始介绍 虚拟机栈 Java虚拟机栈是由一个个栈帧组成的,当一个方法被调用时,代表这个 ...

  2. [leetcode]40. Combination Sum II组合之和之二

    Given a collection of candidate numbers (candidates) and a target number (target), find all unique c ...

  3. iview表格高度自适应只需要三步即可

    1. 需要增加到table表格里的 highlight-row :height="tableHeight" ref="table" 2.在return 定义一个 ...

  4. linux主机名设置

    有时会报错: 代理抛出异常错误: java.net.MalformedURLException: Local host name unknown: java.net.UnknownHostExcept ...

  5. C#,如何程序使用正则表达式如何使用匹配的位置的结果修改匹配到的值

    程序代码使用正则表达式如何修改匹配到的值: 代码一: using System; using System.Text.RegularExpressions; public class Example ...

  6. 【转】Cron表达式详解

    Cron表达式是一个字符串,字符串以5或6个空格隔开,分为6或7个域,每一个域代表一个含义,Cron有如下两种语法格式: (1) Seconds Minutes Hours DayofMonth Mo ...

  7. 代码之髓读后感——容器&并发

    容器 单个地址存放单个数据,但是如果有多个数据,而这些数据互相关联,则我们更希望的是将他们能够更好的在内存中组织在一起.于是便出现了容器的概念. 在不同的语言中,容器的名称不同,性质各异.比如,C 语 ...

  8. 用nodejs搭建类似于C++的服务器后台.类似网易pomelo

    实际的情况,用nodejs跑业务,非常的快,只要用好其无阻塞和回调这两点,处理速度真的是杠杠的. 从年初开始,我用nodejs搭建了类似C++的服务器后台,也想和做同样的事情的朋友分享,本服务平台因为 ...

  9. AX_List

    List list = new List(Types::Class);  CustTable custTable;  while select custTable  {      list.addEn ...

  10. 2019.02.27 bzoj4556: [Tjoi2016&Heoi2016]字符串(二分答案+sam+线段树合并)

    传送门 题意:给一个字符串SSS. 有mmm次询问,每次给四个参数a,b,c,da,b,c,da,b,c,d,问s[a...b]s[a...b]s[a...b]的所有子串和s[x...y]s[x... ...