开源中国 2018 年度最后一场技术盛会邀你来约~错过就要等明年啦!点此立即预约

涂飞平 / inspect-jfinalJava

Watch 5 Star 4 Fork 0
Sign up for free
Explore and code with more than 2 million developers,Free private repositories !:)
Sign up
讲解JFinal源码,作为新同事的培训资料 spread retract

Cancel
Notice: Creating folder will generate an empty file .keep, because not support in Git
Loading...
README.md

#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的(详情参考JFinalinitHandler 方法)。 那真正的处理逻辑转到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如何分割,包装成一个全新的对象?通过分割,获得更多的处理机会和更好框架逻辑。

Comments ( 1 )

You need to Sign in for post a comment

Help Search