JFinal的启动源码解读
本文对Jfinal的启动源码做解释说明。
PS:Jfinal启动容器可基于Tomcat/Jetty等web容器启动,本文基于Jetty的启动方式做启动源码的解读和分析,tomcat类似。
- 入口
JFinalConfig的继承类的Main方法为入口,实例代码继承类为:DemoConfig,Main方法如下:
public static void main(String[] args) {
/**
* 特别注意:Eclipse 之下建议的启动方式
*/
JFinal.start("WebRoot", 80, "/", 5); }
启动时,从WebRoot-->Web-INF-->web.xml开始
web.xml代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<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">
<filter>
<filter-name>jfinal</filter-name>
<filter-class>com.jfinal.core.JFinalFilter</filter-class>
<init-param>
<param-name>configClass</param-name>
<param-value>com.demo.common.DemoConfig</param-value>
</init-param>
</filter> <filter-mapping>
<filter-name>jfinal</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</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全路径类
createJFinalConfig(filterConfig.getInitParameter("configClass"));
PS:上方法主要是为了实例化web.xml中configClass对象,JVM在装载class时候,基于类加载机制创建configClass对应的对象实例,代码分析如下
private void createJFinalConfig(String configClass) {
if (configClass == null) {
throw new RuntimeException("Please set configClass parameter of JFinalFilter in web.xml");
} Object temp = null;
try {
temp = Class.forName(configClass).newInstance();//基于类加载机制实例化
} catch (Exception e) {
throw new RuntimeException("Can not create instance of class: " + configClass, e);
} if (temp instanceof JFinalConfig) {
jfinalConfig = (JFinalConfig)temp;//强制类型转换
} else {
throw new RuntimeException("Can not create instance of class: " + configClass + ". Please check the config in web.xml");
}
//此类创建了一个对象JFinalConfig的继承类对象而已,不做深入追究
}
B.基于Jfinal.init加载启动资源
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();
/** * 初始化web根路径 */ private void initPathUtil() {
String path = servletContext.getRealPath("/");
PathKit.setWebRootPath(path);
}
B2.初始化constant, route, engine, plugin, interceptor, handler等信息
Config.configJFinal(jfinalConfig);

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

B21.初始化log

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

/**
* 设置开发模式和模板文件跟目录,这个方法是Jfinal默认调用的,如果要更新engine里面的变量的话,则JFinalConfig继承类可重写configEngine(Engine engine);
*/
private static void initEngine() {
engine.setDevMode(constants.getDevMode());
engine.setBaseTemplatePath(PathKit.getWebRootPath());
}
B23:启动插件
PS:类似dbcp、redis等的初始化在jfinal中被定义成了插件的方式,startPlugins中我们重点强调什么是插件、插件能干啥?startPlugins()中并没有启动插件,仅仅是在jfinalConfig.configPlugin(plugins)后,设置插件的开发模式

private static void startPlugins() {
//获取插件列表
List<IPlugin> pluginList = plugins.getPluginList();
if (pluginList == null) {
return ;
} for (IPlugin plugin : pluginList) {
try {
// process ActiveRecordPlugin devMode
if (plugin instanceof com.jfinal.plugin.activerecord.ActiveRecordPlugin) {
com.jfinal.plugin.activerecord.ActiveRecordPlugin arp = (com.jfinal.plugin.activerecord.ActiveRecordPlugin)plugin;
if (arp.getDevMode() == null) {
//基于用户定位设置插件的开发模式
arp.setDevMode(constants.getDevMode());
}
}
//启动插件,这步骤特别的重要,下个博客重点说明启动插件能干啥用。。。。xieyang@163.com/xieyang@e6yun.com
if (plugin.start() == false) {
String message = "Plugin start error: " + plugin.getClass().getName();
log.error(message);
throw new RuntimeException(message);
}
}
catch (Exception e) {
String message = "Plugin start error: " + plugin.getClass().getName() + ". \n" + e.getMessage();
log.error(message, e);
throw new RuntimeException(message, e);
}
}
}
B3.初始化映射,包含controller和intercept
initActionMapping();PS:initActionMapping方法的本质将controller的映射和拦截器存储到ActionMapping,然后调用ActionMapping.buildActionMapping完成映射关系的初始化,下文对如何进行映射给予分析和说明
备注:未完待续明天继续2017-07-25
void buildActionMapping() {
mapping.clear();
//通过反射获取controller父类的无参数的方法名集合
Set<String> excludedMethodName = buildExcludedMethodName();
//拦截器管理类
InterceptorManager interMan = InterceptorManager.me();
//getRoutesList():获取用户自定义的controller、名字和模板路径及一个空的route,PS后续详解
for (Routes routes : getRoutesList()) {
//遍历routes,获取每一个route对象
for (Route route : routes.getRouteItemList()) {
//获取当前route的class,此class实在route.me.add的时候添加进来的
Class<? extends Controller> controllerClass = route.getControllerClass();
//PS:从拦截器管理器中获取当前controller的拦截器集合,具体实现方式为:获取controller的Before类注解value为Intercept子类的注解值,通过反射初始化全局单例方式的拦截器,并以集合的方式返回,读者自行研读此处代码
Interceptor[] controllerInters = interMan.createControllerInterceptor(controllerClass);
//判断当前controllerClass的超类是不是Controller.class
boolean sonOfController = (controllerClass.getSuperclass() == Controller.class);
//如果是true,则返回当前类的所有的包括public/private/protected/default修饰的方法。否则的话,返回当前类及父类的public方法
Method[] methods = (sonOfController ? controllerClass.getDeclaredMethods() : controllerClass.getMethods());
for (Method method : methods) {
String methodName = method.getName();
//当前方法不在父类的无参方法集合中或者当前方法的参数值不为零,则认为认为是非public方法,不做controller的映射,直接返回
if (excludedMethodName.contains(methodName) || method.getParameterTypes().length != 0)
continue ;
//当前类的是controller的子类,但是方法是非public,则直接返回
if (sonOfController && !Modifier.isPublic(method.getModifiers()))
continue ;
//PS:有点难,
Interceptor[] actionInters = interMan.buildControllerActionInterceptor(routes.getInterceptors(), controllerInters, controllerClass, method);
String controllerKey = route.getControllerKey(); ActionKey ak = method.getAnnotation(ActionKey.class);
String actionKey;
if (ak != null) {
actionKey = ak.value().trim();
if ("".equals(actionKey))
throw new IllegalArgumentException(controllerClass.getName() + "." + methodName + "(): The argument of ActionKey can not be blank."); if (!actionKey.startsWith(SLASH))
actionKey = SLASH + actionKey;
}
else if (methodName.equals("index")) {
actionKey = controllerKey;
}
else {
//请求的URL路径
actionKey = controllerKey.equals(SLASH) ? SLASH + methodName : controllerKey + SLASH + methodName;
}
//对请求URL的详细介绍
Action action = new Action(controllerKey, actionKey, controllerClass, method, methodName, actionInters, route.getFinalViewPath(routes.getBaseViewPath()));
//配置映射关系
if (mapping.put(actionKey, action) != null) {
throw new RuntimeException(buildMsg(actionKey, controllerClass, method));
}
}
}
}
routes.clear(); // support url = controllerKey + urlParas with "/" of controllerKey
Action action = mapping.get("/");
if (action != null) {
mapping.put("", action);
}
}
B4.初始化Handler
initHandler();
PS:主要是为了初始化链接结构
/**
* Build handler chain
*/
@SuppressWarnings("deprecation")
public static Handler getHandler(List<Handler> handlerList, Handler actionHandler) {
Handler result = actionHandler; for (int i=handlerList.size()-1; i>=0; i--) {
Handler temp = handlerList.get(i);
temp.next = result;
temp.nextHandler = result;
result = temp;
} return result;
}
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的启动源码解读的更多相关文章
- Jfinal启动源码解读
本文对Jfinal的启动源码做解释说明. PS:Jfinal启动容器可基于Tomcat/Jetty等web容器启动,本文基于Jetty的启动方式做启动源码的解读和分析,tomcat类似. 入口 JF ...
- Jfinal拦截器源码解读
本文对Jfinal拦截器源码做以下分析说明
- Jfinal-Plugin源码解读
PS:cnxieyang@163.com/xieyang@e6yun.com 本文就Jfinal-plugin的源码进行分析和解读 Plugin继承及实现关系类图如下,常用的是Iplugin的三个集成 ...
- swoft| 源码解读系列二: 启动阶段, swoft 都干了些啥?
date: 2018-8-01 14:22:17title: swoft| 源码解读系列二: 启动阶段, swoft 都干了些啥?description: 阅读 sowft 框架源码, 了解 sowf ...
- Cocos Creator 源码解读:引擎启动与主循环
前言 预备 不知道你有没有想过,假如把游戏世界比作一辆汽车,那么这辆"汽车"是如何启动,又是如何持续运转的呢? 如题,本文的内容主要为 Cocos Creator 引擎的启动流程和 ...
- Jfinal控制器源码解读
本文对Jfinal的控制器源码做以下分析. PS:控制器是所有请求跳转的基础,本文就Jfinal控制器的继承关系及初始化的方法做出解释说明. 啰嗦下:所有的请求和响应都是都是通过web容器封装,我们主 ...
- SDWebImage源码解读 之 SDWebImageCompat
第三篇 前言 本篇主要解读SDWebImage的配置文件.正如compat的定义,该配置文件主要是兼容Apple的其他设备.也许我们真实的开发平台只有一个,但考虑各个平台的兼容性,对于框架有着很重要的 ...
- AFNetworking 3.0 源码解读(五)之 AFURLSessionManager
本篇是AFNetworking 3.0 源码解读的第五篇了. AFNetworking 3.0 源码解读(一)之 AFNetworkReachabilityManager AFNetworking 3 ...
- [Hadoop源码解读](六)MapReduce篇之MapTask类
MapTask类继承于Task类,它最主要的方法就是run(),用来执行这个Map任务. run()首先设置一个TaskReporter并启动,然后调用JobConf的getUseNewAPI()判断 ...
随机推荐
- .net 高级写法总结
1.处理HTTP非正常的请求参数: [1] 获取相应的流转为string [2] request 的只读属性设置为可编辑,类似form [3] 转换为json对象 [4] 重设只读属性 //判断请求类 ...
- 关于树莓派 BOOBS 安装之后的初级操作
以安装OpenCV 3.1.0为例 上一篇我们利用Raspberry 官方提供的工具 BOOBS安装了Raspbian 操作系统,下面让我们看一下如何简单的配置raspbian操作系统. 从树莓派官方 ...
- 【转】【Linux】Linux 命令行快捷键
Linux 命令行快捷键 涉及在linux命令行下进行快速移动光标.命令编辑.编辑后执行历史命令.Bang(!)命令.控制命令等.让basher更有效率. 常用 ctrl+左右键:在单词之间跳转 ct ...
- Navicat连接Oracle11g 错误的解决办法
一.换成32位的Navicat!!! 二.去Oracle官网下载你要连接数据版本的client. 注意:需要下载两个文件,以11.2.0.4.0版本为例,需要下载 instantclient-sqlp ...
- Java多例模式
多例模式又划分为有上限多例模式和无上限多例模式两种,没上限的多例模式和直接 new 一个对象没什么差别,此处不做记录. 有上限多例模式:实际上是单例模式的推广,如果它的上限是1,那么就成了单例模式了. ...
- unity3d绘画手册-------地形各个参数解释
关于Unity3D是什么.我就不多做解释了.由于工作原因,该系列原创教程不定期更新.每月必然有更新.谢谢各位. Unity地形:: 新建地形: <ignore_js_op> 如图在菜单中新 ...
- PNG透明兼容IE6的几种方法(转)
png 透明针对 IE6 一直是件挺麻烦的事情,使用的方法也是各有不同,大多的原理是用 IE 的滤镜来解决的. 语法: filter:progid:DXImageTransform.Microsoft ...
- 统计 fastq 文件 q20 , GC 含量的软件
二代测序的分析过程中,经常需要统计原始下机数据的数据量,看数据量是否符合要求:另外还需要统计q20,q30,GC含量等反应测序质量的指标: 在kseq.h 的基础上稍加改造,就可以实现从fastq 文 ...
- uniqid,md5,microtime
<?php header("content-type:text/html;charset=utf-8"); $str = uniqid(md5(microtime(true) ...
- vector deque list
vector ,deque 和 list 顺序性容器: 向量 vector : 是一个线性顺序结构.相当于数组,但其大小可以不预先指定,并且自动扩展.它可以像数组一样被操作,由于它的特性我们完全可 ...