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

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

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

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

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

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <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">
    3. <filter>
    4. <filter-name>jfinal</filter-name>
    5. <filter-class>com.jfinal.core.JFinalFilter</filter-class>
    6. <init-param>
    7. <param-name>configClass</param-name>
    8. <param-value>com.demo.common.DemoConfig</param-value>
    9. </init-param>
    10. </filter>
    11.  
    12. <filter-mapping>
    13. <filter-name>jfinal</filter-name>
    14. <url-pattern>/*</url-pattern>
    15. </filter-mapping>
    16. </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全路径类

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

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

    1. 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();

    1. /**
    2.  
    3. * 初始化web根路径
    4.  
    5. */
    6.  
    7. private void initPathUtil() {
    8. String path = servletContext.getRealPath("/");
    9. PathKit.setWebRootPath(path);
    10. }

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

    Config.configJFinal(jfinalConfig);

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

    B21.初始化log

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

    B22:初始化引擎

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

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

    1. startPlugins()中并没有启动插件,仅仅是在jfinalConfig.configPlugin(plugins)后,设置插件的开发模式
    1. private static void startPlugins() { //获取插件列表
    2. List<IPlugin> pluginList = plugins.getPluginList();
    3. if (pluginList == null) {
    4. return ;
    5. }
    6.  
    7. for (IPlugin plugin : pluginList) {
    8. try {
    9. // process ActiveRecordPlugin devMode
    10. if (plugin instanceof com.jfinal.plugin.activerecord.ActiveRecordPlugin) {
    11. com.jfinal.plugin.activerecord.ActiveRecordPlugin arp = (com.jfinal.plugin.activerecord.ActiveRecordPlugin)plugin;
    12. if (arp.getDevMode() == null) { //基于用户定位设置插件的开发模式
    13. arp.setDevMode(constants.getDevMode());
    14. }
    15. }
    16. //启动插件,这步骤特别的重要,下个博客重点说明启动插件能干啥用。。。。xieyang@163.com/xieyang@e6yun.com
    17. if (plugin.start() == false) {
    18. String message = "Plugin start error: " + plugin.getClass().getName();
    19. log.error(message);
    20. throw new RuntimeException(message);
    21. }
    22. }
    23. catch (Exception e) {
    24. String message = "Plugin start error: " + plugin.getClass().getName() + ". \n" + e.getMessage();
    25. log.error(message, e);
    26. throw new RuntimeException(message, e);
    27. }
    28. }
    29. }

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

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

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

    B4.初始化Handler

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

    1. /**
    2. * Build handler chain
    3. */
    4. @SuppressWarnings("deprecation")
    5. public static Handler getHandler(List<Handler> handlerList, Handler actionHandler) {
    6. Handler result = actionHandler;
    7.  
    8. for (int i=handlerList.size()-1; i>=0; i--) {
    9. Handler temp = handlerList.get(i);
    10. temp.next = result;
    11. temp.nextHandler = result;
    12. result = temp;
    13. }
    14.  
    15. return result;
    16. }

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

  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. SDWebImage源码解读 之 SDWebImageCompat

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

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

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

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

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

  9. 【原】Spark中Job的提交源码解读

    版权声明:本文为原创文章,未经允许不得转载. Spark程序程序job的运行是通过actions算子触发的,每一个action算子其实是一个runJob方法的运行,详见文章 SparkContex源码 ...

随机推荐

  1. qml demo分析(externaldraganddrop-拖拽)

    一.效果展示 客户端程序拖拽是一个很常见的需求,对于QWidget程序来说,需要重写如图1这么几个方法,通过重写这几个方法的逻辑,我们就可以控制鼠标拖拽的逻辑,糟糕的是QDrag执行exec后是一个阻 ...

  2. Mybatis-----优化配置文件,基于注解CR

    这篇主要写配置文件的优化,例如  jdbc.properties 配置文件  ,引入数据库的文件,例如driver,url,username,password 等,然后在 SqlMapConfig.x ...

  3. 小白的Python之路 day4 装饰器高潮

    首先装饰器实现的条件: 高阶函数+嵌套函数 =>装饰器 1.首先,我们先定义一个高级函数,去装饰test1函数,得不到我们想要的操作方式 import time #定义高阶函数 def deco ...

  4. ES6 数组的扩展

    1. Array.from() Array.from()将类数组(array-like)对象与可遍历的对象转化为数组并返回. 下面是一个类数组 let arr = { '0':'a', '1':'b' ...

  5. 5.Nginx作为web缓存服务器

    Nginx作为web缓存服务器 从0.7.48版本开始,Nginx支持类似Squid的缓存功能.Nginx的web缓存服务主要由proxy_cache相关命令集合fastcgi_cache相关命令集构 ...

  6. IIS加载JSON文件 错误 404

    问题描述 在发布项目的时候,有一些文件是json文件,在网页中进行加载,但是在IIS7发布的时候,json文件居然是404,无法找到,在URL上输入地址也一样. 错误原因 IIS内部机制,不支持直接访 ...

  7. 关于php中,记录日志中,将数组转为json信息记录日志时遇到的问题总结

    1 中文编码化,无法看到具体的中文,如:你好  =>  \u4F60\u597D 解决方案:可以使用 json_encode($arr,JSON_UNESCAPED_UNICODE) 转义中文[ ...

  8. SQL Server(解决问题)已成功与服务器建立连接,但是在登录过程中发生错误。(provider: Shared Memory Provider, error:0 - 管道的另一端上无任何进程。

    http://blog.csdn.net/github_35160620/article/details/52676416 如果你在使用新创建的 SQL Server 用户名和密码 对数据库进行连接的 ...

  9. Linux设置PHP环境变量

    区分 环境变量从时间上可分为临时性和永久性,这里只说明永久性的设置 操作 PHP 安装目录 找到PHP的安装目录:我这里是/phpstudy/server/php 其bin目录为:/phpstudy/ ...

  10. Life in Changsha 第一次scrum冲刺

    第一次冲刺任务 基于大局的全面性功能框架定位,要求能实现用户基于自己的需求进行的一系列操作. 用户故事 用户打开“生活在长大”的界面 程序首页展示校园服务,论坛等相关信息 用户选择某个功能 程序界面跳 ...