本文对Jfinal的启动源码做解释说明。

PS:Jfinal启动容器可基于Tomcat/Jetty等web容器启动,本文基于Jetty的启动方式做启动源码的解读和分析,tomcat类似。

    • 入口  
      JFinalConfig的继承类的Main方法为入口,实例代码继承类为:DemoConfig,Main方法如下:

      public static void main(String[] args) {
      /**
      * 特别注意:Eclipse 之下建议的启动方式
      */
      JFinal.start("WebRoot", 80, "/", 5); }

      启动时,从WebRoot-->Web-INF-->web.xml开始
      web.xml代码如下:

      <?xml version="1.0" encoding="UTF-8"?>
      <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
      <filter>
      <filter-name>jfinal</filter-name>
      <filter-class>com.jfinal.core.JFinalFilter</filter-class>
      <init-param>
      <param-name>configClass</param-name>
      <param-value>com.demo.common.DemoConfig</param-value>
      </init-param>
      </filter> <filter-mapping>
      <filter-name>jfinal</filter-name>
      <url-pattern>/*</url-pattern>
      </filter-mapping>
      </web-app>
    • JFinalFilter执行源码解读
      web容器调用web.xml中的JFinalFilter过滤器,注入:com.demo.common.DemoConfig全路径,JFinalFilter中三个重要的方法分别是:init(FilterConfig filterConfig)、doFilter(ServletRequest req, ServletResponse res, FilterChain chain)、destroy()。
      启动入口为init方法,方法截图如下
       1 public void init(FilterConfig filterConfig) throws ServletException {
      2 createJFinalConfig(filterConfig.getInitParameter("configClass"));//A.解析web.xml中configClass全路径类并初始化
      3
      4 if (jfinal.init(jfinalConfig, filterConfig.getServletContext()) == false) {//B.基于Jfinal.init加载启动资源,包含控制器、拦截器等
      5 throw new RuntimeException("JFinal init error!");
      6 }
      7
      8 handler = jfinal.getHandler();//C.获取处理器
      9 constants = Config.getConstants();//D.获取常量
      10 encoding = constants.getEncoding();//E.获取编解码器
      11 jfinalConfig.afterJFinalStart();//F.启动web容器
      12
      13 String contextPath = filterConfig.getServletContext().getContextPath();//G.获取server路径
      14 contextPathLength = (contextPath == null || "/".equals(contextPath) ? 0 : contextPath.length());//H.对路径特殊处理
      15 }

      A.解析web.xml中configClass全路径类

       createJFinalConfig(filterConfig.getInitParameter("configClass"));
      PS:上方法主要是为了实例化web.xml中configClass对象,JVM在装载class时候,基于类加载机制创建configClass对应的对象实例,代码分析如下
      private void createJFinalConfig(String configClass) {
      if (configClass == null) {
      throw new RuntimeException("Please set configClass parameter of JFinalFilter in web.xml");
      } Object temp = null;
      try {
      temp = Class.forName(configClass).newInstance();//基于类加载机制实例化
      } catch (Exception e) {
      throw new RuntimeException("Can not create instance of class: " + configClass, e);
      } if (temp instanceof JFinalConfig) {
      jfinalConfig = (JFinalConfig)temp;//强制类型转换
      } else {
      throw new RuntimeException("Can not create instance of class: " + configClass + ". Please check the config in web.xml");
      }
      //此类创建了一个对象JFinalConfig的继承类对象而已,不做深入追究
      }

      B.基于Jfinal.init加载启动资源

      jfinal.init(jfinalConfig, filterConfig.getServletContext())

      boolean init(JFinalConfig jfinalConfig, ServletContext servletContext) {
      this.servletContext = servletContext;
      this.contextPath = servletContext.getContextPath();
      //B1.获取web容器运行的跟目录,并存储到PathKit.webRootPath变量中
      initPathUtil();

      //B2.初始化constant, route, engine, plugin, interceptor, handler等信息,实际是调用jfinalConfig

      Config.configJFinal(jfinalConfig); // start plugin, init log factory and init engine in this method
      constants = Config.getConstants();
      //B3.初始化映射,包含controller和intercept
      initActionMapping();

      //B4.初始化Handler
      initHandler();

      //B5.初始化Render
      initRender();

      //B6.初始化不知道
      initOreillyCos();

      //初始化Token
      initTokenManager();

      return true;
      }

      B1.获取web容器运行的跟目录,并存储到PathKit.webRootPath变量中
      initPathUtil();

      /**
      
      * 初始化web根路径
      
      */
      
      private void initPathUtil() {
      String path = servletContext.getRealPath("/");
      PathKit.setWebRootPath(path);
      }

      B2.初始化constant, route, engine, plugin, interceptor, handler等信息

      Config.configJFinal(jfinalConfig);

      static void configJFinal(JFinalConfig jfinalConfig) {
      jfinalConfig.configConstant(constants);//调用 JFinalConfig 子类的 configConstant,自行跟踪
              initLogFactory();//B21:初始化日志工厂  基于log4j封装class的getlog方法,不做解释  
      initEngine();//B22:初始化引擎
      jfinalConfig.configRoute(routes);//调用 JFinalConfig 子类的方法,配置用户自定义controller
              jfinalConfig.configEngine(engine);//调用 JFinalConfig 子类的方法,配置引擎
              jfinalConfig.configPlugin(plugins);//调用 JFinalConfig 子类的方法,配置用户自定义的插件、redis插件、dbcp插件
              startPlugins();B23:启动插件 very important!!!
      jfinalConfig.configInterceptor(interceptors);//调用 JFinalConfig 子类的方法,配置用户自定义的拦截器
              jfinalConfig.configHandler(handlers);//调用 JFinalConfig 子类的方法,配置用户自定义hander
      }

      B21.初始化log

      /**
      *初始化log的核心是创建logFactory对象,尝试创建log4j,如果创建失败,则使用JDK默认log工厂,详情省略
      */
      static void init() {
      if (defaultLogFactory == null) {
      try {
      Class.forName("org.apache.log4j.Logger");
      Class<?> log4jLogFactoryClass = Class.forName("com.jfinal.log.Log4jLogFactory");
      defaultLogFactory = (ILogFactory)log4jLogFactoryClass.newInstance(); // return new Log4jLogFactory();
      } catch (Exception e) {
      defaultLogFactory = new JdkLogFactory();
      }
      }
      }

      B22:初始化引擎

      initEngine()
      /**
      * 设置开发模式和模板文件跟目录,这个方法是Jfinal默认调用的,如果要更新engine里面的变量的话,则JFinalConfig继承类可重写configEngine(Engine engine);
      */
      private static void initEngine() {
      engine.setDevMode(constants.getDevMode());
      engine.setBaseTemplatePath(PathKit.getWebRootPath());
      }

      B23:启动插件
         PS:类似dbcp、redis等的初始化在jfinal中被定义成了插件的方式,startPlugins中我们重点强调什么是插件、插件能干啥?

      startPlugins()中并没有启动插件,仅仅是在jfinalConfig.configPlugin(plugins)后,设置插件的开发模式
      
      private static void startPlugins() {
      //获取插件列表
      List<IPlugin> pluginList = plugins.getPluginList();
      if (pluginList == null) {
      return ;
      } for (IPlugin plugin : pluginList) {
      try {
      // process ActiveRecordPlugin devMode
      if (plugin instanceof com.jfinal.plugin.activerecord.ActiveRecordPlugin) {
      com.jfinal.plugin.activerecord.ActiveRecordPlugin arp = (com.jfinal.plugin.activerecord.ActiveRecordPlugin)plugin;
      if (arp.getDevMode() == null) {
      //基于用户定位设置插件的开发模式
      arp.setDevMode(constants.getDevMode());
      }
      }
      //启动插件,这步骤特别的重要,下个博客重点说明启动插件能干啥用。。。。xieyang@163.com/xieyang@e6yun.com
      if (plugin.start() == false) {
      String message = "Plugin start error: " + plugin.getClass().getName();
      log.error(message);
      throw new RuntimeException(message);
      }
      }
      catch (Exception e) {
      String message = "Plugin start error: " + plugin.getClass().getName() + ". \n" + e.getMessage();
      log.error(message, e);
      throw new RuntimeException(message, e);
      }
      }
      }

      B3.初始化映射,包含controller和intercept
      initActionMapping();

      PS:initActionMapping方法的本质将controller的映射和拦截器存储到ActionMapping,然后调用ActionMapping.buildActionMapping完成映射关系的初始化,下文对如何进行映射给予分析和说明
      备注:未完待续明天继续2017-07-25

      void buildActionMapping() {
      mapping.clear();
      //通过反射获取controller父类的无参数的方法名集合
      Set<String> excludedMethodName = buildExcludedMethodName();
      //拦截器管理类
      InterceptorManager interMan = InterceptorManager.me();
      //getRoutesList():获取用户自定义的controller、名字和模板路径及一个空的route,PS后续详解
      for (Routes routes : getRoutesList()) {
      //遍历routes,获取每一个route对象
      for (Route route : routes.getRouteItemList()) {
      //获取当前route的class,此class实在route.me.add的时候添加进来的
      Class<? extends Controller> controllerClass = route.getControllerClass();
      //PS:从拦截器管理器中获取当前controller的拦截器集合,具体实现方式为:获取controller的Before类注解value为Intercept子类的注解值,通过反射初始化全局单例方式的拦截器,并以集合的方式返回,读者自行研读此处代码
      Interceptor[] controllerInters = interMan.createControllerInterceptor(controllerClass);
      //判断当前controllerClass的超类是不是Controller.class
      boolean sonOfController = (controllerClass.getSuperclass() == Controller.class);
      //如果是true,则返回当前类的所有的包括public/private/protected/default修饰的方法。否则的话,返回当前类及父类的public方法
      Method[] methods = (sonOfController ? controllerClass.getDeclaredMethods() : controllerClass.getMethods());
      for (Method method : methods) {
      String methodName = method.getName();
      //当前方法不在父类的无参方法集合中或者当前方法的参数值不为零,则认为认为是非public方法,不做controller的映射,直接返回
      if (excludedMethodName.contains(methodName) || method.getParameterTypes().length != 0)
      continue ;
      //当前类的是controller的子类,但是方法是非public,则直接返回
      if (sonOfController && !Modifier.isPublic(method.getModifiers()))
      continue ;
      //PS:有点难,
      Interceptor[] actionInters = interMan.buildControllerActionInterceptor(routes.getInterceptors(), controllerInters, controllerClass, method);
      String controllerKey = route.getControllerKey(); ActionKey ak = method.getAnnotation(ActionKey.class);
      String actionKey;
      if (ak != null) {
      actionKey = ak.value().trim();
      if ("".equals(actionKey))
      throw new IllegalArgumentException(controllerClass.getName() + "." + methodName + "(): The argument of ActionKey can not be blank."); if (!actionKey.startsWith(SLASH))
      actionKey = SLASH + actionKey;
      }
      else if (methodName.equals("index")) {
      actionKey = controllerKey;
      }
      else {
      //请求的URL路径
      actionKey = controllerKey.equals(SLASH) ? SLASH + methodName : controllerKey + SLASH + methodName;
      }
      //对请求URL的详细介绍
      Action action = new Action(controllerKey, actionKey, controllerClass, method, methodName, actionInters, route.getFinalViewPath(routes.getBaseViewPath()));
      //配置映射关系
      if (mapping.put(actionKey, action) != null) {
      throw new RuntimeException(buildMsg(actionKey, controllerClass, method));
      }
      }
      }
      }
      routes.clear(); // support url = controllerKey + urlParas with "/" of controllerKey
      Action action = mapping.get("/");
      if (action != null) {
      mapping.put("", action);
      }
      }

      B4.初始化Handler

      initHandler();
        PS:主要是为了初始化链接结构

      /**
      * Build handler chain
      */
      @SuppressWarnings("deprecation")
      public static Handler getHandler(List<Handler> handlerList, Handler actionHandler) {
      Handler result = actionHandler; for (int i=handlerList.size()-1; i>=0; i--) {
      Handler temp = handlerList.get(i);
      temp.next = result;
      temp.nextHandler = result;
      result = temp;
      } return result;
      }

      B5.初始化Render
      initRender();PS:初始化模板引擎

      Render.init(constants.getEncoding(), constants.getDevMode());
      initTemplateRender();
      initFreeMarkerRender(servletContext);
      initVelocityRender(servletContext);
      initJspRender(servletContext);
      initFileRender(servletContext);

      备注:后续讲解模板引擎怎么初始化的问题

      //B6.初始化不知道
      initOreillyCos();

      //初始化Token
      initTokenManager();PS初始化Token

      以上完成了server初始化操作,所有请求的封装都是基于server-容器实现,代码仅到这一层,详细需要跟踪容器的实现。
      天道酬勤!

JFinal的启动源码解读的更多相关文章

  1. Jfinal启动源码解读

    本文对Jfinal的启动源码做解释说明. PS:Jfinal启动容器可基于Tomcat/Jetty等web容器启动,本文基于Jetty的启动方式做启动源码的解读和分析,tomcat类似. 入口  JF ...

  2. Jfinal拦截器源码解读

    本文对Jfinal拦截器源码做以下分析说明

  3. Jfinal-Plugin源码解读

    PS:cnxieyang@163.com/xieyang@e6yun.com 本文就Jfinal-plugin的源码进行分析和解读 Plugin继承及实现关系类图如下,常用的是Iplugin的三个集成 ...

  4. swoft| 源码解读系列二: 启动阶段, swoft 都干了些啥?

    date: 2018-8-01 14:22:17title: swoft| 源码解读系列二: 启动阶段, swoft 都干了些啥?description: 阅读 sowft 框架源码, 了解 sowf ...

  5. Cocos Creator 源码解读:引擎启动与主循环

    前言 预备 不知道你有没有想过,假如把游戏世界比作一辆汽车,那么这辆"汽车"是如何启动,又是如何持续运转的呢? 如题,本文的内容主要为 Cocos Creator 引擎的启动流程和 ...

  6. Jfinal控制器源码解读

    本文对Jfinal的控制器源码做以下分析. PS:控制器是所有请求跳转的基础,本文就Jfinal控制器的继承关系及初始化的方法做出解释说明. 啰嗦下:所有的请求和响应都是都是通过web容器封装,我们主 ...

  7. SDWebImage源码解读 之 SDWebImageCompat

    第三篇 前言 本篇主要解读SDWebImage的配置文件.正如compat的定义,该配置文件主要是兼容Apple的其他设备.也许我们真实的开发平台只有一个,但考虑各个平台的兼容性,对于框架有着很重要的 ...

  8. AFNetworking 3.0 源码解读(五)之 AFURLSessionManager

    本篇是AFNetworking 3.0 源码解读的第五篇了. AFNetworking 3.0 源码解读(一)之 AFNetworkReachabilityManager AFNetworking 3 ...

  9. [Hadoop源码解读](六)MapReduce篇之MapTask类

    MapTask类继承于Task类,它最主要的方法就是run(),用来执行这个Map任务. run()首先设置一个TaskReporter并启动,然后调用JobConf的getUseNewAPI()判断 ...

随机推荐

  1. OSX下git diff/merge 可视化工具 P4Merge 环境配置步骤

    先下载 step1:首先,把要运行的命令放入外部包装脚本中,创建一个merge包装脚本,名字叫做extMerge,让它带参数调用P4Merge. $ cat >> /usr/local/b ...

  2. 《FPGA全程进阶---实战演练》第三章之PCB叠层

    1.双面板 在双层板设计layout时,最好不要不成梳状结构,因为这样构成的电路,回路面积较大,但是只要对较重要的信号加以地保护,布线完成之后将空的地方敷上地铜皮,并在多个过孔将两个地连接起来,可以弥 ...

  3. e668. 在一组像素中创建缓冲图像

    This example demonstrates how to convert a byte array of pixel values that are indices to a color ta ...

  4. 鼠标捕获(setCapture,releaseCapture)的学习

    鼠标捕获(setCapture)作用是将鼠标事件捕获到当前文档的指定的对象——对指定的对象设置鼠标捕获.这个对象会为当前应用程序或整个系统接收所有鼠标事件. 所谓鼠标捕获,是指对鼠标事件(onmous ...

  5. 【Java NIO的深入研究1】缓冲区

    缓冲区 传统的流和通道的对比 流 通道 慢 快 处理简单 处理复杂 单字节的传输 一块数据的传输 - Java.io.*已经重新写过 - 是对流的模拟 单向的 双向的 可直接访问 必须通过Buffer ...

  6. window用Xmanager4.0的Xstart连接linux远程桌面

    安装包: xorg-x11-xauth xterm.x86_64 0:253-1.el6 Execute command path:/usr/bin/xterm Xstart连接Linux远程桌面有一 ...

  7. 调用外部 DLL 中的函数(2. 晚绑定)

    , b, t, );end; procedure TForm1.FormDestroy(Sender: TObject);begin  FreeLibrary(inst);  {记得释放}end; e ...

  8. ChemDraw 16最新版本发布 更效率科研的首选

    ChemDraw一直是全球领先的科学绘图软件,致力于为科学家.教师以及学生提供最新的智能应用程序.ChemDraw 16版本相较于15版本做出了较大的改进,大大缩短科研时间,提高科研效率.扩展Name ...

  9. Windows绘图中的GDI映射模式

    对Windows编程新手来说,GDI编程中最困难的部分就是映射模式(Mapping Mode). 什么是映射模式? 我们知道,GDI画图是在设备描述表这个逻辑意义上的显示平面上进行,其使用的是逻辑坐标 ...

  10. Visual Basic的未来之路

        Green首先列出了当时使用VB进行开发的四个基础指导原则:         1.VB和C#共享的通用IDE和平台构建块.         2.共享的“多范式.面向对象.命令式.强类型等”语言 ...