mcl77361 发表于 2014-12-2 00:42:47

P3插件式应用开发框架——初探

本帖最后由 mcl77361 于 2014-12-4 21:30 编辑

一、开篇杂谈
       接触“JEECG快速开发框架”已有一年有余,在此期间也使用该框架进行了两个系统的开发,个人感觉开发效率提高不少。上周有幸接触到P3插件式应用开发框架(以下简称P3),兴趣使然进行了一些研究和探索,在此抛砖引玉,留个印记。
二、WEB框架概述
      P3作为一个Web开发框架,它的实现原理是什么,插件式设计思路又是如何呢?
      首先,让我们来看看一个web请求的处理过程的简图。如图:web.png
      从简图我们可以看出,基于Servlet容器的WEB框架实现有两种方式。一种通过实现Filter接口,另外一种实现Servlet。通过统一拦截浏览器的请求再分发给具体业务处理类,从而实现响应,产生HTML代码,返回给浏览器。如:Struts2.x/StrutsPrepareAndExecuteFilter(实现Filter接口)。SpringMVC/DispatcherServlet(实现Servlet)。
   而P3是采用何种方式呢?实现Filter接口。有图有真相。P3Framework-filter.png
三、P3总体结构
       P3是Filter实现的可扩展的插件式开发框架。
       3.1 Filter实现是如何的?
       该框架的核心引擎是实现Filter接口。如图:Framework-filter.png
       在Filter初始化(init方法)中应用Spring对“应用配置文件”进行加载解析。
       在Filter拦截方法(doFilter)中通过RequestProcess调度在“应用配置文件”注册进来的请求处理器进行请求的处理。请求处理器负责请求的解析和具体业务处理对象(如Action)的生成、调用和页面渲染。如图:requestProcess.png
       3.2如何实现插件式扩展
       是对2.1提到的请求处理器进行扩展,实现插件式注入。每个插件实现需实现请求处理器接口如图:process.png
       3.3处理链逻辑如何
       按注册“请求处理器”的顺序进行链条式调用,只有某个请求处理器认为完全处理,链条中断,不再往下传递。如:
                           for (Process handler : processHandlers) {
                              if (handler.execUrlPattern(url)) {
                                                if (logger.isDebugEnabled())
                                                      logger.debug("\t" + handler.getClass().getName()+ " process");
                                                if (handler.process(request, response)) {
                                                      if (logger.isDebugEnabled())
                                                                logger.debug("processChain finish");
                                                      return true;
                                                }
                                        }
                            }
      3.4页面渲染如何处理?
      public boolean process(HttpServletRequest request, HttpServletResponse response) throws IOException;
      从“请求处理器”接口我们可以看出,页面渲染在处理器中通过response进行响应,具体应用如何渲染引擎如:freemarket/jsp/velocity,则由“请求处理器”具体实现者来承担(实现)。由此我们可以判断该框架是可以支持多种渲染引擎的。
四、“请求处理器”实现实例分析[ActionProcessHandler/ResourceProcessHandler]
         所有的具体请求处理器都必须实现Process接口。该接口定义了以下方法。
       (1) setUrlPattern(String urlPattern) //用于设置该请求处理器,要处理的url匹配规则,为外界设置提供入口。
       (2) boolean execUrlPattern(StringBuffer url) //用于决定某个url请求是否能被该请求处理器处理(类似Mapping)。
       (3) boolean process(HttpServletRequestrequest,HttpServletResponse response)//处理请求,返回true整个处理链中断。否则往下继续传递。
       为了更便捷的实现该接口,框架提供了一个默认抽象类实现了(1)(2)。我们只需要继承该抽象类并实现process方法即可。
       public abstract class ProcessHandler implementsProcess
       4.1 ActionProcessHandler实现
      public class ActionProcessHandler extends ProcessHandler
      public boolean process(HttpServletRequestrequest,HttpServletResponse response) throws IOException {
          returninvoke(request, response, UrlRole.parse(request));
       }       UrlRole.parse负责对请求的url进行解析得出具体的Action类的全限定名和方法,详细的解析逻辑请查看UrlRole类。       private invoke(HttpServletRequestrequest,HttpServletResponse response, UrlRole urlRole){       Class<?> actionClass = Class.forName(urlRole.getClassName());      AbstractActionaction = (AbstractAction)actionClass.newInstance();      Method actionMethod= actionClass.getMethod(urlRole.getMethodName());      action.request= request;      action.response= response;      Object rtn = actionMethod.invoke(action);      booleanresult = true;      if (rtn !=null) {          result = outputToResponse(request, response,rtn.toString());      }      returnresult;      从以上代码逻辑中不难看出,框架(根据解析出来的全限定类名和方法)采用反射机制实现方法的调用。      如在该请求处理器中实现具体业务处理Action类,需注意以下几点:      (1)包名需满足以下规则:com.buss.xxx.yyy.action【详情请参考UrlRole类】      (2)响应的方法为无参方法。      (3)方法返回值为HTML代码或浏览器可接受的数据形式。      4.2 ResourceProcessHandler实现      实现静态资源的响应输出。      public boolean process(HttpServletRequestrequest, HttpServletResponse response) {      try {         String url = request.getRequestURI();          url =url.replaceFirst(request.getContextPath(), "");          InputStream is = this.getClass().getResourceAsStream("/content" +url);       if (is != null) {zip(request,response,is);}      从红色标识部分我们可以看出要加载的静态资源必须在classpath下的content目录下。应用该请求处理同时还可达到压缩输 出的效果。五、框架中常用渲染引擎的初始化及应用
       框架中对渲染引擎的支持以工具类的形式提供。即提供静态方法调用接口。每个渲染引擎工具类以实现InitializingBean接口已达到引擎初始化目的。具体分析如下:
      5.1 Velocity
      public class VelocityHelper implements InitializingBean
      Velocity页面渲染引擎工具类,提供两个有用的接口,如下:
      (1) public static String merge(String template, VelocityContext velocityContext)
      (2) public static String merge(String template)
      5.2 Freemarket
      public class FreemarkerViewHelper implements InitializingBean
      Freemarket页面渲染引擎工具类,提供多个有用的接口,如下:
      (1) parseTemplate(String tplName, String encoding,Map<String, Object> paras)
      (2) parseTemplate(String tplName, Map<String, Object> paras)
六、请求同步处理问题
       框架中实现了对request、response对象的同步处理。
       具体实现为: SynchronizationHelper。
       框架中引入同步机制,是解决同一个请求在多个线程中处理的并发问题,还是预先设计,个人对该功能的设计思路不是很          清晰,希望有机会能和群主沟通交流。个人感觉此处引入同步机制,有一个优点。提供了一个便捷的手段在请求链上下文          中获取request、response对象。
       框架实现同步的原理为:运用线程变量ThreadLocal实现。【注:一般的JDBC事务管理实现也是基于该机制实现】。


mcl77361 发表于 2014-12-3 22:46:33

六、个人一点建议
(1) ActionProcessHandler中Action对象生成的优化
Class<?> actionClass = Class.forName(urlRole.getClassName());
框架中目前对Action对象的生成采用反射机制生成。无法实现Service层注入问题。
建议:使用SpringHelper.getBean实现Action对象的获取。(注:解析机制和bean的id需重新考虑和解析)
(2) ActionProcessHandler中无参方法的优化
框架中目前的响应方法都是无参方法,需要获取客户端传来的参数,只能通过
SynchronizationHelper.getCurrentRequest或AbstractAction.request。
建议:引入机制解决带参方法和参数的自动转型填充的问题。
(3)ResourceProcessHandler资源加载路径相对固定
该请求处理器加载的资源路径为:classpath下的content目录下资源。无法动态配置。
建议:提供接口设置加载路径,同时,引入URLClassloader加载指定路径下的资源。
页: [1]
查看完整版本: P3插件式应用开发框架——初探