# inspect-jfinal
**Repository Path**: tufeiping/inspect-jfinal
## Basic Information
- **Project Name**: inspect-jfinal
- **Description**: 讲解JFinal源码,作为新同事的培训资料
- **Primary Language**: Java
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 5
- **Forks**: 0
- **Created**: 2016-01-16
- **Last Updated**: 2021-06-30
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
#JFinal 源码解析
## 0.前言
这是一个简单的培训提纲,主要为公司内部员工讲解一下JFinal开发框架。没有大量贴代码,只是提纲挈领,我在讲解的过程中会结合源码进行说明。
>JFinal 是基于 Java 语言的极速 WEB + ORM 框架,其核心设计目标是开发迅速、代码量少、学习简单、功能强大、轻量级、易扩展、Restful
一个极速,简单,小巧的框架。今天我们只将框架部分,不讲ActiveRecord部分
## 1.从哪里开始?
------
最好的方法是:如何做,就如何看!
在JFinal的使用中,最基础的配置就是web.xml中过滤器的配置,注意这行:
> <filter-class>com.jfinal.core.JFinalFilter</filter-class>
> <init-param>
> <param-name>configClass</param-name>
> <param-value>uf.audit.tax.util.Config</param-value>
> </init-param>
其中,JFinalFilter就是入口了,而且还有一个参数,configClass,顾名思义,就是配置信息
那我们就开始分析JFinalFilter,看看它是如何完成http请求的(servlet)
## 2.JFinalFilter
------
JFinal也是属于Servlet规范,它接管了Servlet中的Filter,并且,它不再使用servlet,这个从web.xml配置中就可以看出来。
在JFinalFilter中,init方法(这些方法都是Servlet API标准)中会获取我们配置文件中的configClass参数。
获取到configClass后会调用createJFinalConfig函数,这个函数会创建我们给出的configClass类实例并赋值给jfinalConfig变量。
创建后,会调用jfinal.init来完成jfinalConfig的配置信息填充。(填充的过程是使用全局,单例的Config对象来接收我们jfinalConfig对象的各个配置信息)
由于属性都在Config单例中,所以后续需要属性的时候,都可以使用Config.getConstains之类的方法来获取。
如何填充配置信息?(参考我们编写JFinalConfig的代码以及下面的代码)
>static void configJFinal(JFinalConfig jfinalConfig) {
> jfinalConfig.configConstants(constains);
> jfinalConfig.configRoute(routes); //将所有的服务信息保存到routes里面
> //....
>}
注意,插件机制也是在这里开始的` startPlugins `,停止部分在JFinalFilter的`destroy`方法里面。
JFinal完成配置信息工作后,开始处理诸如:actionMapping,Handler,Render之类的工作了
>initActionMapping();
>initHandler();
>initRender();
正常的JFinal开发,都需要将URL与Action做绑定(Mapping映射),或者我们使用过的注解方式。
URL和Action的绑定操作,核心函数是 ActionMapping 对象的 **buildActionMapping** 函数。
这个函数是核心函数,大家要仔细阅读,因为所有JFinal与服务相关的特性,都是在这个函数中实现。
函数的大致流程是:
遍历routes,获取其中的类信息,利用反射方式,获取所有的方法,根据方法签名(public void func())来确定是有效的action,这里URL的配置方式是:controllerKey + "/" + actionKey。 其中controllerKey就是route中的key了,actionKey就是方法名。
上面提到的服务相关特性?什么特性?
Interceptor!! 也就是JFinal中实现AOP的核心技术。
所有我们设置的Intercept和AOP方法,都会连同服务的其他信息打包到Action对象中。
然后,将URL和Action对象保存到一个mapping中。
## 3.一个请求如何到达Action?
------
当一个请求按照进入Tomcat/Jetty等符合Servlet规范的Server中,服务器会将参数和请求打包,然后按照Servlet规范,交到Filter/Servlet中,以便应用完成逻辑处理然后通过response携带内容返回。
之前已经说明,JFinal废弃了Servlet服务,仅仅使用了规范中的Filter,还是看JFinalFilter的代码,每次访问请求按照规范,都会经过Filter的doFilter函数。
其核心就是一句代码:
>handler.handle(target, request, response, isHandled)
这里的handler是在JFinal初始化的时候内置的ActionHandler类,它主要是处理Action的(详情参考JFinal`initHandler` 方法)。
那真正的处理逻辑转到ActionHandler的`handle`方法了。
>Action action = actionMappings.getAction(target, urlPara);//这里的actionMappings来自Config对象
>Controller controller = action.getControllerClass().newInstance();//通过我们保存的Action对象来获取Controller类,并生成其对象
>controller.init(request, response, urlPara[0]);//将request,response对象保存到controller中
>new Invocation(action, controller).invoke();//这里是核心
>Render render = controller.getRender();
>//...一些跳转处理,forward操作,会递归调用handle方法继续处理
>render.setContext(request, response, action.getViewPath()).render();//渲染界面
这里需要注意的是Invocation对象,为什么不在拿到controller对象的情况下直接调用其对应方法呢?
这里需要再次提及Config中的configInterceptors函数,我们定义的的拦截器,就是在这里发挥作用的。
这个函数需要慢慢体会:
>if (index < inters.length) {
> inters[index++].intercept(this);//intercept是Interceptor接口唯一的方法
>} //这里大家体会一下,拦截器链条的方式,这里的代码还能回来吗?
>else if (index++ == inters.length) {
> if (action != null) {
> returnValue = action.getMethod().invoke(target, args);//这个时候才会到我们写的方法体
> }
>}
到达我们的方法体,完成我们的处理后,我们一版会调用render来输出结果。
不管controller的有多少个method,每个mothod的render又不尽相同(比如render/renderJson),但某个时刻,只能有一个有效的render,这个render就是controller的render变量。
render是一个包含render纯虚函数的类,各种不同的render至少要实现render方法(不同的render都是对response进行最后一步处理)。
## 4. AOP机制
------
拦截器大家都明白了原理吧?那么,提到的AOP呢?
其实,在上面代码中,已经可以看出AOP的端倪了。
>inters[index++].intercept(this);//注意这个this
这个this就是之前代码中new Invocation出来的Invocation对象,通过这个参数,我们在Interceptor中可以得到方法的签名,方法的参数,也可以决定如何进行必要的处理然后再调用方法本身,这就达到了AOP的目的。
我们看看Invocation有哪些好用的方法:
>getArg(int);//获取参数
>getArgs();//获取所有参数
>getTarget();//获取被处理的Controller
>getMethod();//获取被处理的方法(action)
>getMethodName();//获取被处理的方法名
>getController();
>getReturnValue();
>getActionKey();
>getViewPath();
如果我们要完成一个AOP操作,仅仅需要实现一个Interceptor接口,然后在里面编写类似以下的代码即可:
> public void intercept(Invocation inv) {
> String actionKey = inv.getActionKey();
> //调用方法前的处理
> inv.invoke();//这步你甚至可以根据实际情况决定是否调用
> String return = inv.getReturnValue();
> //调用方法后的处理
> }
最老套的应用就是:加入一个操作记录Log功能,在不修改controller的代码情况下,完成这个功能。
当然,很多时候,我们不喜欢写Inerceptor,AOP很多时候用注解来实现。
**注** 注解我们也会使用Interceptor来完成其中的工作。
## 5. 终止化
------
这个比较简单,直接查看JFinalFilter的`destroy`方法,这个方法代码:
>jfinalConfig.beforeJFinalStop();
>jfinal.stopPlugins();
它会调用plugin的stop进行终止化操作
## 6. 其他
------
这里补充Handler部分,顾名思义:处理器。
JFinal有默认的`ActionHandler`,为什么要引入这个机制呢?
默认的ActionHandler主要是处理Action的,也就是我们使用的JFinal中Controller.action机制,如果我们需要处理其他形式的服务呢?比如:原生的Servlet呢?
> Handler handler = actionHandler;//默认的ActionHandler
> for (int i = handlerList.size() - 1; i >= 0; i--) {
//构建单向链表
> Handler temp = handlerList.get(i);
> temp.next = result;
> result = temp;
> }
> return result;
在doFilter方法中,对于handler的使用如下:
>boolean[] isHandled = {false};
>handler.handle(target, request, response, isHandled);
>if (isHandled[0] == false) chain.doFilter(request, response);
如果isHandled[0]为false,表示未做处理(因为默认就是false,这里注意,声明成数组对象,仅仅是为了参数好传递--引用传递而非值传递),就交给Servlet默认的处理流程处理,这个在静态资源处理中会用到,静态资源我们会交给Tomcat等Server自行处理。
之前的代码中,我曾经写过一个Handler用于处理Servlet,后来重构代码后,将Servlet转换为JFinal的Controller来处理,所以我们系统中目前没有其他的Handler了。
*分析完代码后,理解一下,JFinal如何将一个我们写的完整的Controller如何分割,包装成一个全新的对象?通过分割,获得更多的处理机会和更好框架逻辑。*