文档章节

Jfinal源码解析系列一

yiguangtia
 yiguangtia
发布于 2015/07/16 12:26
字数 1515
阅读 48
收藏 0

1 Jfinal的初始化入口

<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.DemoConfig</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>jfinal</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

其中JFinalFilter是过滤器负责对所有的请求进行拦截。DemoConfig是系统的配置,在初始化进行加载。

当web容器启动时,JfinalFilter会启动init()进行系统初始化。

public void init(FilterConfig filterConfig) throws ServletException {
   createJFinalConfig(filterConfig.getInitParameter("configClass"));//1获取DemoConfig
   
   if (jfinal.init(jfinalConfig, filterConfig.getServletContext()) == false)//2JFinal初始化
      throw new RuntimeException("JFinal init error!");
   
   handler = jfinal.getHandler();//3获取handler
   constants = Config.getConstants();//4获取常量
   encoding = constants.getEncoding();//5获取编码
   jfinalConfig.afterJFinalStart();//6回调函数 默认为空
   
   String contextPath = filterConfig.getServletContext().getContextPath();//7ContextPath的路径
   contextPathLength = (contextPath == null || "/".equals(contextPath) ? 0 : contextPath.length());//
}

以下对jfinal初始化进行详细讲解。

1 createJfFinalConfig类的详细讲解

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;//向上转成/jFinalConfig
   else
      throw new RuntimeException("Can not create instance of class: " + configClass + ". Please check the config in web.xml");
}

2 JFinal init()方法

boolean init(JFinalConfig jfinalConfig, ServletContext servletContext) {
   this.servletContext = servletContext;
   this.contextPath = servletContext.getContextPath();
   
   initPathUtil();//2.1初始化PathKit工具并获取WebRoot
   
   Config.configJFinal(jfinalConfig); //2.2 start plugin and init logger factory in this method
   constants = Config.getConstants();//2.3获取常量
   
   initActionMapping();//2.4 Mapping
   initHandler();//2.5 init handler
   initRender();//2.6 init render
   initOreillyCos();//2.7 init OreillyCos
   initTokenManager();//2.8 init Token
   
   return true;
}

2.2 系统配置初始化。

static void configJFinal(JFinalConfig jfinalConfig) {
		jfinalConfig.configConstant(constants);	//2.2.1 配置自定义的constants			        initLoggerFactory();// 2.2.2配置日志工厂
		jfinalConfig.configRoute(routes);// 2.2.3配置自定义的Routes
		jfinalConfig.configPlugin(plugins);// 2.2.4配置自定义的Plugins
		startPlugins();	// very important!!!//2.2.5开启plugins
		jfinalConfig.configInterceptor(interceptors);// 2.2.6配置自定义的拦截器
		jfinalConfig.configHandler(handlers);// 2.2.7配置自定义的handler
	}

2.2.5

() {
   List<IPlugin> pluginList = .getPluginList()(pluginList == )
      (IPlugin plugin : pluginList) {
      {
         (plugin com.jfinal.plugin.activerecord.ActiveRecordPlugin) {
            com.jfinal.plugin.activerecord.ActiveRecordPlugin arp = (com.jfinal.plugin.activerecord.ActiveRecordPlugin)plugin(arp.getDevMode() == )
               arp.setDevMode(.getDevMode())}
         
         (plugin.start() == ) {
            String message = + plugin.getClass().getName().error(message)RuntimeException(message)}
      }
      (Exception e) {
         String message = + plugin.getClass().getName() + + e.getMessage().error(messagee)RuntimeException(messagee)}
   }
}

2.4 ActionMappig 初始化

private void initActionMapping() {
		actionMapping = new ActionMapping(Config.getRoutes(), Config.getInterceptors());
		actionMapping.buildActionMapping();
	}

其中buildActionMapping函数

// 1.url到action映射的核心实现
        // Map的entrySet()方法返回一个实现Map.Entry接口的对象集合。集合中每个对象都是底层Map中一个特定的键/值对
        // 获取自定义配置的routs中的对象集合,key: String,value: ? extents Controller.class
        // 配置实例 me.add("/blog", BlogController.class);
        for (Entry<String, Class<? extends Controller>> entry : routes
                .getEntrySet()) {

            // 获取当前迭代的集合的value
            Class<? extends Controller> controllerClass = entry.getValue();

            // 创建当前Controller的拦截器,处理@before注解
            Interceptor[] controllerInters = interceptorBuilder
                    .buildControllerInterceptors(controllerClass);

            // 获取当前controller的所有方法
            Method[] methods = controllerClass.getMethods();

            // 迭代所有方法
            for (Method method : methods) {

                // 通过method对象获取当前method的name
                String methodName = method.getName();

                // 判断当前method是否是父级Congtroller的通用方法且该方法不能含有参数
                if (!excludedMethodName.contains(methodName)
                        && method.getParameterTypes().length == 0) {

                    // 创建当前Method的拦截器,处理@before注解
                    Interceptor[] methodInters = interceptorBuilder
                            .buildMethodInterceptors(method);
                    // 创建此method对应的action的拦截器,此拦截器由全局、controller、method级别的拦截器依次拦截组成的拦截器栈
                    Interceptor[] actionInters = interceptorBuilder
                            .buildActionInterceptors(defaultInters,
                                    controllerInters, controllerClass,
                                    methodInters, method);

                    // 定义 controllerKey 为 entry.getKey(),为添加路由时的第一个参数,如"/","/blog"
                    String controllerKey = entry.getKey();

                    // 获取当前方法的@ActionKey注解,demo项目没有ActionKey注解
                    ActionKey ak = method.getAnnotation(ActionKey.class);
                    // ak != null
                    // 的情况暂时不考虑,作者相比也是不提倡这种做法的,actionkey应该是借鉴springmvc的注解的产物,有点使方法本身的命名失去描述的感觉
                    if (ak != null) {
                        String 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;

                        if (mapping.containsKey(actionKey)) {
                            warnning(actionKey, controllerClass, method);
                            continue;
                        }

                        Action action = new Action(controllerKey, actionKey,
                                controllerClass, method, methodName,
                                actionInters, routes.getViewPath(controllerKey));
                        mapping.put(actionKey, action);

                        // 处理默认的访问方式,即url/的方式,单独处理index方法
                    } else if (methodName.equals("index")) {

                        // method为index时,actionKey为controllerKey
                        String actionKey = controllerKey;

                        // action冒泡了,使用七个参数来实例化具体的action
                        // routes.getViewPath(controllerKey) 返回的当前controller对应的视图路径,具体实现会在routs分析中探讨
                        Action action = new Action(controllerKey, actionKey,
                                controllerClass, method, methodName,
                                actionInters, routes.getViewPath(controllerKey));
                        // 添加一个映射进mapping,如actionKey对应的值不为空,那么将用新值替换旧值,且方法返回值为之前的旧值,否则返回null
                        action = mapping.put(actionKey, action);

                        // 如果action不为空,则需记录警告日志
                        if (action != null) {
                            warnning(action.getActionKey(),
                                    action.getControllerClass(),
                                    action.getMethod());
                        }

                        // 处理controller中的其他方法,如demo中的BlogController中的add save edit等
                    } else {

                        // 根据controllerkey的'/','/XX'的两种情况分别生成actionKey
                        String actionKey = controllerKey.equals(SLASH) ? SLASH
                                + methodName : controllerKey + SLASH
                                + methodName;

                        // 判断mapping是否已拥有当前actionKey对应的映射,如有,记录警告日志,跳出本轮循环,防止后配置的覆盖先前配置的
                        // 此处处理方式与对index的处理不同,何故?暂时不解
                        if (mapping.containsKey(actionKey)) {
                            warnning(actionKey, controllerClass, method);
                            continue;
                        }

                        // 与处理index方法并无二致
                        Action action = new Action(controllerKey, actionKey,
                                controllerClass, method, methodName,
                                actionInters, routes.getViewPath(controllerKey));
                        mapping.put(actionKey, action);
                    }
                }
            }
        }

        // 添加诸如http://localhost:20000/webappName 的支持,即末尾不加'/'的支持
        Action actoin = mapping.get("/");
        if (actoin != null) {
            mapping.put("", actoin);
        }

完成Action和url的对应映射

2.5 初始化handler

private void initHandler() {
		Handler actionHandler = new ActionHandler(actionMapping, constants);//根据2获得的actionMapping构造handler
		handler = HandlerFactory.getHandler(Config.getHandlers().getHandlerList(), actionHandler);
	}//构造handler chain ,依照定义的顺序依次链接,actionHandle在链的末端

2.6 initRender

private void initRender() {
RenderFactory renderFactory = RenderFactory.me();//2.6.1获取RenderFactory 单例被final修饰
renderFactory.init(constants, servletContext);//2.6.2
}

   2.6.2 renderFactory 初始化

public void init(Constants constants, ServletContext servletContext) {
this.constants = constants;
RenderFactory.servletContext = servletContext;
// init Render
Render.init(constants.getEncoding(), constants.getDevMode());
initFreeMarkerRender(servletContext);
initVelocityRender(servletContext);
initJspRender(servletContext);
initFileRender(servletContext);
// create mainRenderFactory
if (mainRenderFactory == null) {
ViewType defaultViewType = constants.getViewType();
if (defaultViewType == ViewType.FREE_MARKER)
mainRenderFactory = new FreeMarkerRenderFactory();
else if (defaultViewType == ViewType.JSP)
mainRenderFactory = new JspRenderFactory();
else if (defaultViewType == ViewType.VELOCITY)
mainRenderFactory = new VelocityRenderFactory();
else
throw new RuntimeException("View Type can not be null.");
}
// create errorRenderFactory
if (errorRenderFactory == null) {
errorRenderFactory = new ErrorRenderFactory();
}
}

2.7 uploda File init

private void initOreillyCos() {
OreillyCos.init(constants.getUploadedFileSaveDirectory(), constants.getMaxPostSize(), constants.getEncoding());//2.7.1 init 文件上传工具
}

2.7.1 

public static void init(String saveDirectory, int maxPostSize, String encoding) {
try {
Class.forName("com.oreilly.servlet.MultipartRequest");//获取MultiparRequest工具
doInit(saveDirectory, maxPostSize, encoding);//初始化上传的相关参数,具体实现以后再继续解剖
} catch (ClassNotFoundException e) {
}
}

2.8 init token

/*暂时不清楚token的作用*/
private void initTokenManager() {
ITokenCache tokenCache = constants.getTokenCache();
if (tokenCache != null)
TokenManager.init(tokenCache);
}

最后做个小结:在配置文件中配置app统一入口,获取app的相关配置后进行JFinal的各组件的初始化,初始化完成个组件后即可对外提供服务


© 著作权归作者所有

yiguangtia
粉丝 5
博文 39
码字总数 29778
作品 0
深圳
私信 提问
加载中

评论(2)

糊搞
支持呀,并强烈要求 @JFinal 将文章中的注释copy到源码中
糊搞
支持呀,并强烈要求@JFinal将文章中的注释copy到源码中
建议以依赖的方式,而非copy的方式使用jfinal代码

Springblade 的技术组合是 spring+springmvc+beetl+beetlsql+shiro,刚看了一下源码,其中大量直接 copy 的 jfinal 源码,例如 render 模块、json 模块相当于整模块原封不动地 copy 使用。项...

JFinal
2016/09/05
1K
14
JFinal Weixin 1.9 发布,微信极速 SDK

JFinal Weixin 1.9 正式发布!离上一次 JFinal weixin 1.8 发布,已经过去 10 个月。在过去的 10 个月时间里 JFinal Weixin 紧随微信公众平台的演化,不断增加了新的 API,同时也在不断完善原...

JFinal
2017/05/22
10.1K
54
JFinal 1.5 发布,JAVA极速WEB+ORM框架

JFinal 爱好者一直都在问 JFinal 何时再次升级?JFinal 1.5 何时发布?以往升级都保持在每月近两次的频率,为何本次五个月过去了新版本还不出?由于作者暂时阔别码坛已有半年时间,一直无暇顾...

JFinal
2013/10/08
7.2K
101
JFinal2.0开启Server后启动其他插件

最近同事在搞JFinal动态添加数据源配置,多亏了JFinal2.0的出现,才很方便的解决了这个问题。 我看他代码时,无意中发现插件还需要调用start()方法,于是就思考在总配置类里面配置插件之后是...

夜辰
2015/07/04
0
0
手把手教你新建 jfinal项目 (一)

申明:个人对java web了解不多,水平不足,有搞笑的地方请勿喷。 一、开工了 1、首先上官网下载 jfinal 的jar包和文档 我们要下载带源码的,这样用的时候还能看看源码。 2、使用MyEclipse进行...

胡萝卜炒肉
2014/05/27
0
0

没有更多内容

加载失败,请刷新页面

加载更多

前嗅教程:如何获取精准客源,提高销量

经常有人问嗅嗅,我是XX行业的,大数据能帮我做什么? • 可以给我带来客源吗? • 可以提高我的销量吗? • 可以增加我的利润吗? 今天嗅嗅就以生鲜供货为例,为大家讲一讲外卖平台那些事~...

forespider
29分钟前
1
0
浮窗插件

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>移动窗口</title> <style> body { margin: 0; padding: 0; width: 100%; height: 1000px; background: #eee; } /*示......

流年那么伤
33分钟前
2
0
关于 Jenkins master 共享 JENKINS_HOME 目录的实验

本文首发于:Jenkins 中文社区 作者:翟志军 审校:王冬辉,linuxsuren Jenkins master 的高可用是个老大难的问题。和很多人一样,笔者也想过两个 Jenkins master 共享同一个 JENKINS_HOME 的...

Jenkins中文社区
40分钟前
3
0
【重构】Spring Cloud OAuth 无Token调用源码封装

背景 重构-改善既有代码的设计,重构的目的是是软件更容易被理解和修改。 书接上回Spring Security OAuth 微服务内部Token传递的源码解析,本篇主要无token 调用过程中,代码的不断完善及其重...

冷冷gg
46分钟前
49
0
watchOS更新后 Apple Watch 4心电图功能已开始支持欧洲用户

苹果在发布 Apple Watch 4 系列时也发布了 ECG(心电图)功能,但这项功能仅适用于在美版 Apple Watch。对于其他地区的用户来说,访问该功能的唯一途径是在美国购买该设备。不过当 watchOS ...

linuxCool
55分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部