公司网站运营维护单位,建设一个展示商品的网站,宣传网站怎么做的,小程序模板多少钱引入#xff1a;大家在Portlet 开发中经常用到portlet:resourceURL#xff0c;而大体上都会去调用相应的serveResource()方法#xff0c;这个过程虽然大家都清楚#xff0c;但是能弄明白这个过程细节的#xff0c;我相信全世界不超过100人#xff0c;至少我去年就… 引入大家在Portlet 开发中经常用到portlet:resourceURL而大体上都会去调用相应的serveResource()方法这个过程虽然大家都清楚但是能弄明白这个过程细节的我相信全世界不超过100人至少我去年就这个疑惑问了我们客户的liferay专家她不能解释。后来去年团队里Danny问过我这个问题我当时研究了一阵也走不通所以一直搁置了。而现在当我花了前面几天时间去研究了下liferay部署war包的细节后我突然发现这个问题我完全明白了。调试分析其实根据上文http://supercharles888.blog.51cto.com/609344/1286976的结论在我们部署war包应用时候对应的我们在war包中的xml文件并不是机械的复制到了webapps下面应用的部署目录而是对于其中的xml文件进行了拆分和加内容。从上述文字结论我们知道web.xml被添加了很多额外内容然后被拆分为2个文件1个是portal-web.xml文件它包含了所有的除(Invoker Filter以外的过滤器的定义另外一个是web.xml它添加了不少内容而最重要的是它会在web.xml中添加一段PortletServlet的定义。所以我们到服务器的webapps上面看下我们的应用部署目录下的web.xml发现它不再是原来的那个web.xml了它有PortletServlet的定义(这里出于security考虑我吧包名最前面部分去掉了)所以这个Portlet相当于一个桥接它把本来隶属于Portal的一个个的Portlet地位提升上去提升到一个一个Servlet,这样他们就可以独立的负责响应各种请求了。而这个Servlet的mapping是现在当我们页面上有个Search按钮。点击会触发如下的portlet:resourceURL我们可以看到这个portlet:resourceURL标记会被liferay-portlet.tld所识别所以最终处理这个标记的类是ResourceURLTag和ResourceURLTei.它会最终被处理类转为一个请求url这个请求 url是撇开细节最后因为它的请求url满足/logearchportlet/*这个url模式(你肯定会问这个http://172.29.175.236:8080/web/guest/log-search?......这个url明显不匹配PortletServlet的模式/logsearchportlet/*嘛因为PortletServlet的模式如下图所示那么我们的请求url是如何进的这个portlet呢?关于这点我想了整整2天才想明白我在后面的精华疑点解答中会提到)所以它最终会走到PortletServlet方法中。首先在第64行中从HttpServletRequest获得portletId:然后在第66-70行分别从HttpServletRequest/Response中获取 PortletRequest和 PortletResponse对象然后在第72行获取当前请求对应的请求的生命周期阶段LIFECYCLE_PHASE:(疑点2这里为什么portletRequest,portletResponse,lifecycle信息都在HttpServletRequest中何时设置上去的关于这个问题参见精华疑点解答然后从第78-90行从portletRequest中PortletSession对象并把PortalSession关联到PortletSession中。然后第40行调用PortletUtilFilter.doFilter()方法它会根据当前lifecycle的值来判断吧PortletRequest转为何种请求类型因为我们从调试信息中看出当前lifecycle是“RESOURCE_PHASE所以它会吧PortletRequest转为ResourceRequest.然后在第71行继续调用filterChain的doFilter方法。这次它会去先把我们的portlet转为ResourceServingPortlet,这里是我们的LogSearchPortlet,再调用我们的LogSearchPortlet的serveResource方法而所有的我们在构造portlet:resourceURL时候附带的参数都会被封装在ResourceRequest从以下截图中可以看出portlet:resourceURL中的所有参数都会被添加到ResourceRequest中一个不少而我们在portlet中的代码已经实现了serveResource方法,所以就可以正确的调用执行了。精华疑点解答1我们的请求urlhttp://172.29.175.236:8080/web-guest/logsearch?.....是如何进入我们PortletServlet的url-pattern /logsearchportlet/*的。这个问题很复杂但是我们可以猜想肯定在请求送达PortletServlet之前进行了若干预处理。我们知道过滤器总是在Servlet之前执行的而我们的请求刚好可以符合 Invoker Filter的url-mapping .而这个InvokerFilter如果熟悉它的代码会发现它其实会去按照每个Filter链上Filter的定义依次去调用各个Filter的doFilter方法当然了这些Filter根据我们以前的研究内容都是定义在liferay-web.xml中。看到第一次请求是/web/guest/logsearch,果然和我们的匹配然后它会走一些filter,最后调用invokerFilterChain.doFilter(servletRequest,servletResponse).第二次我们进入这个方法时候请求就变了变为/c/portal/layout.这里省略很多不重要步骤因为我们在struts-config.xml中定义了/c/portal/layout的action-mapping,如下所以会走到LayoutAction的execute()方法中它会在第244行调用重载的processLayout()方法然后在第663-665行它会去调用processPortletRequest方法跳过漫长的一段代码和我们研究重点无关的代码最终它在porcessPortletRequest的第899行判断lifecycle是”RESOURCE_PHASE,所以进入这个分支再跳过N多不相关行看到最后它会通过ServletRequest,ServletResponse 构造ResourceRequestImpl和ResourceResponseImpl对象并且第936行通过ResourceRequestImpl创建并且封装一个ServiceContext对象看右边的调试信息可以看到我们的请求url是封装在这个ServiceContext对象的。(_currentURL属性然后我们把这个ServiceContext对象加到ThreadLocal列表中。最后在第941行调用InvokerPortlet的serveResource方法它会最终调用InvokerPortletImpl的invoke()方法而我们访问invoke()方法时候谜底终于揭开了都给我睁大眼睛看清楚了原来它会在第610行通过PortletConfigImpl获取Portlet的名字我们获得是logsearchportlet,然后把它拼接到后面的/invoke字符串就得到了这个path 为/logsearchportlet/invoke,然后它创建一个RequestDispatcher对象用于转发请求最后如下图所示它会吧请求转发到path指定的/logsearchportlet/invoke, 而这个请求url显然是匹配/logsearchportlet/*的所以就可以正确的进入到 PortletServlet了于是这个问题得到圆满解决。精华疑点解答2在PortletServlet服务于当前请求中的service方法中为什么portletRequest,portletResponse,lifecycle信息都在HttpServletRequest中当我们分析完刚才整个过程中时这个问题也迎刃而解了参见InvokerPortletImpl的invoke()方法的第623行到第626行在创建好RequestDipatcher对象后但是还没转发请求到/logsearchportlet/invoke 之前它会去先获得HttpServletRequest对象并且依次吧JAVAX_PORTLET_PORTLET,LIFECYCLE_PHASE,PORTLET_SERVLET_FILTER_CHAIN存入这样在PortletServlet的service()方法中就可以正确取出这些信息并且处理了。总结结束这文章时候我真是非常开心其实这个问题我已经想了半年没想通不过今天终于想通了。本来这个问题是我们团队一个叫Danny的问我了我当时研究了没解决后来我在Liferay官网上挂了几个月没人能解答真开心我还是靠自己的实力解决了。(1)页面上用portlet:resourceURL对应的请求url最终会被映射到PortletServlet中进行处理这个目的是吧 Portlet的对于请求处理能力的地位提升到Servlet级别因为它现在可以接受 HttpServletRequest类型的请求了这个PortletServlet会先从HttpServletRequest中获得portletId,portletRequest,portletResponse和lifecycle信息然后根据lifecycle阶段信息相应的吧PortletRequest转为何种请求类型比如如果lifecycle是RESOURCE_PHASE,那么它会吧portletRequest转为ResourceRequest它包含了portlet:resourceURL中所有附带参数。然后在doFilter方法中它吧我们的portlet转为ResourceServingPortlet 并且调用serveResource()方法于是可以就可以正确的调用我们在portlet应用层面定义的serveResource()方法了。(2)这个PortletServlet并不是开始就定义在我们的项目打的war包中的而是在部署war包到liferay部署目录后liferay框架自己添加的一段代码具体细节见上一篇文章http://supercharles888.blog.51cto.com/609344/1286976(3)但是最重要的一点是我们的页面的portlet:actionURL并不直接对应到PortletServlet的url-mapping中这也是困扰我半年多的问题。其实它是先走到InvokerFilter中然后在执行/c/portal/layout时候它会走到struts框架然后经过一系列漫长的调用最终在InvokerPortletImpl的invoke()方法中得到解决了它会生成一个新path类似 /portlet-name/invoke 然后把所有portlet相关信息包括portlet,lifecycle,filterchain添加到HttpServletRequest对象中并且新建一个RequestDispatcher吧当前请求转发到刚才的新path中这样就可以让请求去匹配PortletServlet的url-pattern并且进入PortletSevlet了。 转载于:https://blog.51cto.com/supercharles888/1287188