JFinal源码走读_1_jfinal初始化

      • jfinal的入口
      • 步进分析
        • jfinal核心初始化方法jfinalinit分析
          • ConfigconfigJFinaljfinalConfig分析
          • ConfigconfigJFinaljfinalConfig分析
            • startPlugins分析
            • url到action映射的核心实现
          • initHandler分析
        • initRender分析
          • renderFactoryinitconstants servletContext分析
        • initOreillyCos分析
        • initI18n分析
        • initTokenManager分析
      • 初始化小结

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.common.DemoConfig</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>jfinal</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>  

demo项目中的web.xml定义的com.jfinal.core.JFinalFilter,是jfinal的核心拦截器,拦截所有请求,是所有web请求的入口。
fiter的生命周期决定,web程序启动时,其init()方法会被web容器自动调用,所以final的初始化就是从此处开始的。

init()

public void init(FilterConfig filterConfig) throws ServletException {
       // 1.根据web.xml中配置的configClass利用反射获取实例并向上转型为JFinalConfig。
        createJFinalConfig(filterConfig.getInitParameter("configClass"));

        // 2.根据web容器传入的filterConfig对象和上一步创建的jfinalConfig对象进行jfinal的初始化,
        // jfinal对象为Jfinal类的实例,JFinal为单例且被final修饰。
        if (jfinal.init(jfinalConfig, filterConfig.getServletContext()) == false)
            throw new RuntimeException("JFinal init error!");

        // 3.获取处理器handler
        handler = jfinal.getHandler();
        // 4.获取常量,此常量在createJFinalConfig()时被填充进Config对象,Config的所有域和方法都为static,预定义的config都为final
        constants = Config.getConstants();
        // 5.获取字符编码,默认为utf-8
        encoding = constants.getEncoding();
        // 6.回调函数,默认实现为空
        jfinalConfig.afterJFinalStart();

        // 7.获取contextPath,demo项目中contextPath为空
        String contextPath = filterConfig.getServletContext().getContextPath();
        // 8.根据contextPath得到contextPathLength,暂时不明contextPathLength用处
        contextPathLength = (contextPath == null || "/".equals(contextPath) ? 0
                : contextPath.length());
    }

步进分析

  1. 根据configClass常量利用反射得到实例并向上转型为父类JFinalConfig,利用了模板方法模式的思想
  2. jfinal核心初始化方法,后续详细探究
  3. 345678步如注释

jfinal核心初始化方法jfinal.init()分析

boolean init(JFinalConfig jfinalConfig, ServletContext servletContext) {
        this.servletContext = servletContext;
        this.contextPath = servletContext.getContextPath();

        // 1.初始化PathKit的webRootPath变量,为当前项目的绝对路径
        initPathUtil();

        // 2.配置Jfinal,configJfinal方法会配置用户自定义的constants,routs,plugins,handles,interceptors(全局拦截器)
        Config.configJFinal(jfinalConfig);

        // 3.获取自定义配置之后的constants
        constants = Config.getConstants();

        // 4.initActionMapping
        initActionMapping();
        // 5.initHandler
        initHandler();
        // 6.initRender
        initRender();
        // 7.initOreillyCos
        initOreillyCos();
        // 8.initI18n
        initI18n();
        // 9.initTokenManager
        initTokenManager();

        return true;
    }

1,3:如注释
4-9:随后具体分析

Config.configJFinal(jfinalConfig)分析
static void configJFinal(JFinalConfig jfinalConfig) {
        // 配置自定义的constants
        jfinalConfig.configConstant(constants);
        // 初始化日志工厂,以及为Config和JfinalFilter的log获取实例
        initLoggerFactory();
        // 配置自定义的Route
        jfinalConfig.configRoute(routes);
        // 配置自定义的Plugin
        jfinalConfig.configPlugin(plugins);
        // 使插件初始化并开始运作,延后分析
        startPlugins(); // very important!!!
        // 配置自定义的的全局拦截器
        jfinalConfig.configInterceptor(interceptors);
        // 配置自定义的的处理器
        jfinalConfig.configHandler(handlers);
    }
Config.configJFinal(jfinalConfig)分析
static void configJFinal(JFinalConfig jfinalConfig) {
        // 配置自定义的constants
        jfinalConfig.configConstant(constants);
        // 初始化日志工厂,以及为Config和JfinalFilter的log获取实例
        initLoggerFactory();
        // 配置自定义的Route
        jfinalConfig.configRoute(routes);
        // 配置自定义的Plugin
        jfinalConfig.configPlugin(plugins);
        // 使插件初始化并开始运作,延后分析
        startPlugins(); // very important!!!
        // 配置自定义的的全局拦截器
        jfinalConfig.configInterceptor(interceptors);
        // 配置自定义的的处理器
        jfinalConfig.configHandler(handlers);
    }
startPlugins();分析
 private static void startPlugins() {
        // 获取配置的plugins集合
        List<IPlugin> pluginList = plugins.getPluginList();
        if (pluginList == null)
            return;
        // IPlugin只有两个方法分别控制插件的开始和停止,此处应用了观察者模式来统一管理plugin的生命周期
        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());
                }
                // 对于具体plugin的初始化,暂时不做深究
                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);
            }
        }
    }

buildActionMapping()分析

void buildActionMapping() {
        // mapping=new HashMap<String,Action>()
        // mapping是一个以string为key,action为value的hashmap实例
        // 清空mapping对象
        mapping.clear();
        // 获取Controller中含有的方法,即排除不被映射为action的方法名
        Set<String> excludedMethodName = buildExcludedMethodName();
        // 实例化interceptorBuilder ,构造方法无操作
        InterceptorBuilder interceptorBuilder = new InterceptorBuilder();
        // 将list转为array
        Interceptor[] defaultInters = interceptors.getInterceptorArray();
        // 将全局拦截器添加到interceptorBuilder私有的拦截器Map中
        interceptorBuilder.addToInterceptorsMap(defaultInters);

        // 1.url到action映射的核心实现
        --someCode--

1.url到action映射的核心实现分析延后

url到action映射的核心实现
  1. 先看一看mapping的真面目
private final Map<String, Action> mapping = new HashMap<String, Action>();
  1. 探一探Action
class Action {

    private final Class<? extends Controller> controllerClass;
    private final String controllerKey;
    private final String actionKey;
    private final Method method;
    private final String methodName;
    private final Interceptor[] interceptors;
    private final String viewPath;

    public Action(String controllerKey, String actionKey, Class<? extends Controller> controllerClass, Method method, String methodName, Interceptor[] interceptors, String viewPath) {
        this.controllerKey = controllerKey;
        this.actionKey = actionKey;
        this.controllerClass = controllerClass;
        this.method = method;
        this.methodName = methodName;
        this.interceptors = interceptors;
        this.viewPath = viewPath;
    }

    get set …………
}
  1. 核心实现
        // 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);
        }

至此:url到action的映射完成,每个url都有对应的action,实现了mvc中的V到C的链接
initActionMapping分析基本完成

initHandler分析

回到jfinal核心初始化方法jfinal.init()分析的initHandle的分析

private void initHandler() {

        // 利用上一步得到的actionMappong和constants来构建actionHandle的实例
        Handler actionHandler = new ActionHandler(actionMapping, constants);

        // 将配置的handle和actionHandle组成handle链 ,依照定义的顺序依次链接,actionHandle在链的末端。
        // 返回的handle为链首。
        handler = HandlerFactory.getHandler(Config.getHandlers()
                .getHandlerList(), actionHandler);
    }

initRender分析

private void initRender() {

        // 获取renderFactory,单例且被final修饰,构造方法为空
        RenderFactory renderFactory = RenderFactory.me();

        // factory初始化,分析如下
        renderFactory.init(constants, servletContext);
    }
renderFactory.init(constants, servletContext)分析
public void init(Constants constants, ServletContext servletContext) {
        this.constants = constants;
        RenderFactory.servletContext = servletContext;

        // render父类初始化encoding,devmode
        Render.init(constants.getEncoding(), constants.getDevMode());

        // 初始化默认支持的视图,各具体视图初始化延后分析,暂时值分析jsp
        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)
                // 只用过jsp,所以暂时只分析与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();
        }
    }

initOreillyCos分析

private void initOreillyCos() {
        //此处没怎么看懂,为何需要一个本地副本
        Constants ct = constants;
        //OreillyCos.isMultipartSupported(),使用懒加载方式,初始化isMultipartSupported属性
        if (OreillyCos.isMultipartSupported()) {
            // 获取配置中的上传文件存放目录
            String uploadedFileSaveDirectory = ct
                    .getUploadedFileSaveDirectory();
            if (uploadedFileSaveDirectory == null
                    || "".equals(uploadedFileSaveDirectory.trim())) {
                // 如果默认目录没有配置,则使用如下配置,此处应该放入constans中,可能是作者开发时的一点疏忽呢
                uploadedFileSaveDirectory = PathKit.getWebRootPath()
                        + File.separator + "upload" + File.separator;
                // 设置uploadedFileSaveDirectory到constant的本地副本中
                ct.setUploadedFileSaveDirectory(uploadedFileSaveDirectory);

                /* * File file = new File(uploadedFileSaveDirectory); if * (!file.exists()) file.mkdirs(); */
            }
            // 断点续传初始化
            OreillyCos.init(uploadedFileSaveDirectory, ct.getMaxPostSize(),
                    ct.getEncoding());
        }
    }

initI18n分析

private void initI18n() {
        // 国际化支持,暂不深究
        String i18nResourceBaseName = constants.getI18nResourceBaseName();
        if (i18nResourceBaseName != null) {
            I18N.init(i18nResourceBaseName, constants.getI18nDefaultLocale(),
                    constants.getI18nMaxAgeOfCookie());
        }
    }

initTokenManager分析

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

初始化小结

jfinal已flter为入口,通过被动调用的filter的init()方法,完成整个jfinal应用的初始化。
大致流程就是先读取配置(只是是写在程序中的配置,希望后期可以提供注解的方式去配置这些程序运行时配置,比如路由,完全可以在具体controller处配置,比如arp添加model到表的映射亦可以在模型处注解处理),然后初始化程序中的各个组件,(logger,plugin,actionMapping,handle,render,Oreillycos,i18n,token),最终程序初始化成功,可以对外服务。

你可能感兴趣的:(源码,javaweb,分析,jFinal)