jfinal源码研究之启动

从.NET转投Java阵营之后,鄙人也算是花费了大量的时间和精力对Java世界里的明星开源框架诸如Spring,Tomcat,Mybatis,Druid,Log4j2等进行了比较深入的了解。


在这个研究过程中,除了不断感慨这些框架的设计精妙以及广泛的普适性外,渐渐地也对它们的复杂度开始感到些许厌倦。

1. 概述

首先还是强调下,本人不敢说Spring或Mybatis这些明星框架有什么不好。只是任何工具都有其适用场景,Spring等框架有着极为广泛的受众,带来强大的社区活跃性和生命力的同时,也不可避免得为了满足层出不穷和千奇百怪的需求,不断得增加中间层,这样在增加了灵活性和包容性的同时,其框架的复杂性也不可避免得膨胀起来。

但是,我们真的需要这么多功能。Spring必须做到你可以不要,但我必须要有。但作为使用者的我们,却只是在乎有限的几个功能点。 本文将要讨论的jfinal正是以此为立足点。贴一段jfinal的宣传语,相信也是最终让很多人选择它的理由: ”JFinal 是基于 Java 语言的极速 WEB + ORM 框架,其核心设计目标是开发迅速、代码量少、学习简单、功能强大、轻量级、易扩展、Restful。在拥有Java语言所有优势的同时再拥有ruby、python、php等动态语言的开发效率!为您节约更多时间,去陪恋人、家人和朋友 :)“。

2. 配置

jfinal的配置极其简单,只需要在web.xml中配置如下一段XML:

<filter>
    <filter-name>jfinal</filter-name>
    <filter-class>com.jfinal.core.JFinalFilter</filter-class>
    <init-param>
       <!-- 以下这个类就是我们添加自定义配置的位置; 除此之外就没有其他XML配置需求-->
      <param-name>configClass</param-name>
      <param-value>com.sp.wechat.conf.WechatConfig</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>jfinal</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

3. 细节

正如标题所示,本文我们关注的是jfinal框架是如何启动的。在看到上面的web.xml配置之后,相信各位基本都能猜到核心启动代码应该就在Servlet规范中的com.jfinal.core.JFinalFilter.init方法中。

public void init(FilterConfig filterConfig) throws ServletException {
    // 确保用户配置了自定义的jfinal配置类
    if (jfinalConfig == null) {
        createJFinalConfig(filterConfig.getInitParameter("configClass"));
    }
    // 初始化jfinal框架的核心组件; 也是本文的重心所在
    jfinal.init(jfinalConfig, filterConfig.getServletContext());
    // 获取contextPath的实际长度; 用于后期接收请求时, 快速截取出实际的请求参数
    String contextPath = filterConfig.getServletContext().getContextPath();
    contextPathLength = (contextPath == null || "/".equals(contextPath) ? 0 : contextPath.length());

    // 保留一份Constants配置
    constants = Config.getConstants();
    // 获取配置的字符编码
    encoding = constants.getEncoding();
    // 回调启动成功的响应函数, 并入用户的自定义逻辑
    jfinalConfig.afterJFinalStart();

    // 获取配置完毕的核心组件handler, 开始接受请求
    handler = jfinal.getHandler();      // 开始接受请求
}

4. JFinal.init

这个方法的职责是在JFinal启动时,初始化jfinal框架的核心组件。

// JFinal.init
void init(JFinalConfig jfinalConfig, ServletContext servletContext) {
    this.servletContext = servletContext;
    this.contextPath = servletContext.getContextPath();

    // 初始化工具类PathKit, 以便之后的使用; 
    // 截止3.4版本, 本方法主要是为PathKit设置WebRootPath
    initPathKit();

    Config.configJFinal(jfinalConfig);  // start plugin, init log factory and init engine in this method
    // 保留一份Constants配置
    constants = Config.getConstants();

    // ActionMapping是个全局单例模式, 其中存放着用户所有自定义配置的Route——即客户端请求的url路径与处理该请求的Action之间的匹配关系
    // 其中的关键字段 mapping (Map<String,Action>类型, key就是actionKey, 客户端请求的url路径与该actionKey匹配的, 对应的Value就是处理该请求的Action)
    initActionMapping();
    // Handler, 在这里将Handler组装成链式结构, 并且ActionHandler为链条的最后一环, 即除非链条被主动断开, 那么在handler链条中, 最后处理请求的肯定是这个ActionHandler
    // 用户配置的Handler, 在链条中, 先配置的先执行, 后配置的后执行
    // jfinal框架中, 也正是ActionHandler在调取相应的自定义Action的
    initHandler();
    // Render
    initRender();
    // OreillyCos 文件上传功能
    initOreillyCos();
    // TokenManager 页面token功能,防止安全漏洞, 确保页面是传递给客户端的那个
    initTokenManager();
}
4.1 Config.configJFinal
  1. 本方法按照一定的顺序回调开发者的自定义配置项constant, plugin, route, engine, interceptor, handler。将用户的自定义配置加载到JFinal内部的相应配置实体Constants,Routes,Plugins,Interceptors,Handlers中。
  2. 所以访问修饰符为package的Config类即可JFinal配置项的集散地,在经过Config.configJFinal调用时机之后,我们就可以通过Config里的各种静态getXxxx方法获取用户的各种自定义配置。
//----------------- Config.configJFinal
/*
 * Config order: constant, plugin, route, engine, interceptor, handler
 */
static void configJFinal(JFinalConfig jfinalConfig) {
    // 回调用户自定义的配置Constant
    jfinalConfig.configConstant(constants);         
    // 初始化日志系统
    initLogFactory();   
    // 初始化Engine, 截止3.4版本, 本方法做了两件事:
    //  1. 设置devMode
    //  2. 设置default base template path
    initEngine();

    // ----------- 以下会连续调用五次configPluginWithOrder, 确保plugin能在合适的时机初始化且只初始化一次。
    // 开发者可以通过在自定义配置constans.setConfigPluginOrder来自定义Plugin的启动时机。
    // 默认configPlugin(..) 将在 configRoute(...) 调用之后被调用
    configPluginWithOrder(1, jfinalConfig);
    // 回调用户自定义的配置Route
    jfinalConfig.configRoute(routes);

    configPluginWithOrder(2, jfinalConfig);
    // 回调用户自定义的配置Engine
    jfinalConfig.configEngine(engine);

    configPluginWithOrder(3, jfinalConfig);
    // 回调用户自定义的配置Interceptor
    jfinalConfig.configInterceptor(interceptors);

    configPluginWithOrder(4, jfinalConfig);
    // 回调用户自定义的配置Handler
    jfinalConfig.configHandler(handlers);

    configPluginWithOrder(5, jfinalConfig);
}
4.2 JFinal.initActionMapping
// JFinal.initActionMapping
private void initActionMapping() {
    // 构建全局的单例ActionMapping
    actionMapping = new ActionMapping(Config.getRoutes());
    // jFinal正是在下面的这个方法中将用户自定义的Route信息解析为Action级别的URL-Action的键值对的。
    // 注意这里还会一并考虑Interceptor的分配。
    // jFinal最终还是以Action为主体,而非我之前预想的Controller。
    actionMapping.buildActionMapping();
    Config.getRoutes().clear();
}
4.3 JFinal.initHandler
// JFinal.initHandler
private void initHandler() {
    // 确保一个回调用户自定义Action的ActionHandler的存在。
    ActionHandler actionHandler = Config.getHandlers().getActionHandler();
    if (actionHandler == null) {
        actionHandler = new ActionHandler();
    }

    actionHandler.init(actionMapping, constants);
    handler = HandlerFactory.getHandler(Config.getHandlers().getHandlerList(), actionHandler);
}

5. 最后

随着阅历的增加,相比较所谓的聪明和智商,现在的我只会相信一件事:那就是”坚持“。哪怕一开始你做得惨不忍睹,但愿意持续不断地改进,愿意为之不断付出时间和精力的坚持,才是唯一让我相信它能成功的理由。正如鄙人在博客的标题栏里的那句“人们真正注意到你的时候,不是第一眼看到你站在那里,而是发现过了这么久你居然还在那里。”。jfinal的作者这么多年的勤勉和坚持,才是造就jfinal如今成就的唯一原因。

  1. Office Site
  2. SpringMVC技巧之通用Controller

猜你喜欢

转载自blog.csdn.net/lqzkcx3/article/details/80963796