• jfinal 源码学习


    源由

    最近闲来无事,顺带看了下jfinal的源码,以下均为自己的个人理解,如有错误请指定;

    jfinal 使用

    • 在web.xml中配置JfinalFilter 并定义JfinalConfig的类
    • 自定义JfinalConfig 继承抽象类JfinalConfig并实现抽象方法
    1. public abstract void configConstant(Constants me);
      public abstract void configRoute(Routes me);
      public abstract void configEngine(Engine me);
      public abstract void configPlugin(Plugins me);
      public abstract void configInterceptor(Interceptors me);
      public abstract void configHandler(Handlers me);

    由上面的几个方法可以知道,我们一会就要跟上面的几个类打交道了;
    Constants : 主要定义项目的常量,比如说是否是开发模式了,上传下载的根路径,默认的视图类型、上传文件的最大长度、编码等等;
    Routes: 定义访问路径与controller的路由关系,可以新加Routes,如me.add(Routes routes),便于分模块;基本的me.add(controllerKey,controllerClass,viewPath),如果视图为空,则默认为controllerKey;
    Engine : 指定模板引擎,是3.0新增的功能;
    Plugins : 添加插件,如数据库插件、连接池插件、缓存插件等,也可以实现自己的插件;这里的plugins可以看作是一个工具类,将多个插件保存在Plugins中的列表中;
    Interceptors : 添加全局的拦截器,自定义的拦截器需要实现intercept(Invocation inv)的方法,在inv中可以获取controller中的相关内容,具体可以参考ActionHandler中的handle方法;
    Handlers: 添加多个处理器,这里可以看作是一个工具类,只是把多个处理器保存到列表中;

    初始化逻辑

    JfinalFilter

    我们先看filter初始化的方法,代码量很少

    1.         createJFinalConfig(filterConfig.getInitParameter("configClass"));
              if (jfinal.init(jfinalConfig, filterConfig.getServletContext()) == false) {
                  throw new RuntimeException("JFinal init error!");
              }
              handler = jfinal.getHandler();
              constants = Config.getConstants();
              encoding = constants.getEncoding();
              jfinalConfig.afterJFinalStart();
              String contextPath = filterConfig.getServletContext().getContextPath();
              contextPathLength = (contextPath == null || "/".equals(contextPath) ? 0 : contextPath.length());

    我们一个一个过:
    createJFinalConfig : 这个就是在web.xml jfinalFilter中定义的参数:configClass,指向自定义JfinalConfig,会尝试根据字符串加载类并生成实例,赋值给该filter中的jfinalConfig;
    jfinal.init() : 初化jfinal ,大部分的逻辑都在这里
    handler = jfinal.getHandler() : 这一句依赖jfinal的初始化,获取Handler,大家可以会问这里为什么只有一个Handler,我定义的多个处理器哪去了? 想要了解可以看下Handler的类定义,里面有一个next的变量,类型也是Handler,没错,是一个链表,在上一步jfinal初始化过程中将Handlers中保存的多个handler组装成了一个链表,这里返回头,并且在尾部加了一个ActionHandler,用来初始化controller,想了解初始化controller的过程,请参考ActionHandler中的handle的方法;
    Config.getConstants(): 在jfinal的初给化方法里,有初始化Config里的变量;

    jfinal

    从过滤器来看,大部分的初始化码在jfinal.init(jfinalConfig,servletContext)方法中;
    代码如下:

      1. // 设置变量,不用理会
      2. this.servletContext = servletContext;
      3. this.contextPath = servletContext.getContextPath();
      4. // 根据servletContext变量来初始化PathKit工具类的webRootPath;
      5. initPathUtil();
      6. Config.configJFinal(jfinalConfig); // start plugin, init log factory and init engine in this method
      7. constants = Config.getConstants();
      8. initActionMapping();
      9. initHandler();
      10. initRender();
      11. initOreillyCos();
      12. initTokenManager();
      13. return true;

    Config.configJFinal(jfinalConfig): 将jfinalConfig中定义的内容全配置到Config类中,后面的参数变量均在Config类中定义,很容易看懂,代码如下:

    1.         jfinalConfig.configConstant(constants);            initLogFactory();    initEngine();
              jfinalConfig.configRoute(routes);
              jfinalConfig.configEngine(engine);
              jfinalConfig.configPlugin(plugins);                startPlugins();        // very important!!!
              jfinalConfig.configInterceptor(interceptors);
              jfinalConfig.configHandler(handlers);

    initActionMapping(): 初始化ActionMapping,在这里完成路由及拦截器的功能,过程如下:
    遍历Config.routes()中的路由关系,根据路由关系中定义的contrllerKey,controllerClass,viewPaht,根据controllerClass利用反射获取Before()的注解,获取controller级别的拦截器集合;通过反射获取所有的方法,再获取方法上面Before()注解来获取方法级别的拦截器集合;还可以获取routes中的路由级别的拦截器集合及Interceptormanager获取全局的拦截器集合;
    然后将cotrollerKey,actionKey,conrollerClass,method,methodName,ins[],viewPath 构建为Action,再放入到以actionKey为key,action为value的map中;至此,actionMapping初始给完成,直接可以在jinal中通过actionKey来获取所有的Action对象;

    initHandler(): 初始化处理器
    使用HandlerFactory类将handlers中的list中的所有处理器构建为Handler的链表,并在链表尾部加入ActionHandler来完成核心功能,其中包括req/resp赋值,ins[]的执行,具体参考该类的handle方法;

    initRender(); // 初始化render
    initOreillyCos(); //初始化上传相关
    initTokenManager(); //初始化token缓存,如果在Constants中没有设置tokenCache,则跳过;

    至此 ,所有初始化完成 ;

    业务请求逻辑

    jfinal doFilter方法

    看完之后发现核心的代码就一句:

    1. handler.handle(target, request, response, isHandled);

    如果有自定义处理器,在自己的逻辑处理完成之后,记得加一句: next.handle()来执行下一个控制器;
    到最后必然会执行到ActionHandler的handle方法;

    ActionHandler handle方法

    核心的代码也就几句:

    1. //获取controller对象
      Controller controller = action.getControllerClass().newInstance();
      //初始化controller
      controller.init(request, response, urlPara[0]);
      newInvocation(action, controller).invoke();
      Render render = controller.getRender();
      render.render();

    inv.invoke(): 执行 当该action有拦截器时,先执行拦截器,ints[i++].interceptor(this);在拦截器内部完成拦截后还执行this.invoke(),一直到所有拦截器都执行完成为止;
    当所有的拦截器都完成之后,this.invoke()实质上就去执行方法:action.getMethod().invoke(controllerInstance,args);
    执行完成之后就去获取Render去做相应的渲染了;

    //TODO 虽然大致明白了加载过程,但为什么要这么用还需要细细体会;
    //TODO 看下渲染的相关过程
    //TODO 看下activeRecord的思想;

  • 相关阅读:
    五 Servlet 技术
    二进制、八进制、十进制、十六进制之间怎样互相转换?
    HTML中怎样添加地图?
    特殊集合
    集合arraylist
    数组

    gif 命令大全
    for 循环与嵌套
    分支语句(switch case)
  • 原文地址:https://www.cnblogs.com/417xiaoliu/p/7084972.html
Copyright © 2020-2023  润新知