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的各组件的初始化,初始化完成个组件后即可对外提供服务