本文對Jfinal的啓動源碼作解釋說明。java
PS:Jfinal啓動容器可基於Tomcat/Jetty等web容器啓動,本文基於Jetty的啓動方式作啓動源碼的解讀和分析,tomcat相似。web
public static void main(String[] args) { /** * 特別注意:Eclipse 之下建議的啓動方式 */ JFinal.start("WebRoot", 80, "/", 5); }
<?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>
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全路徑類redis
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加載啓動資源apache
jfinal.init(jfinalConfig, filterConfig.getServletContext())
boolean init(JFinalConfig jfinalConfig, ServletContext servletContext) {
this.servletContext = servletContext;
this.contextPath = servletContext.getContextPath();
//B1.獲取web容器運行的跟目錄,並存儲到PathKit.webRootPath變量中
initPathUtil();tomcat
//B2.初始化constant, route, engine, plugin, interceptor, handler等信息,實際是調用jfinalConfigapp
Config.configJFinal(jfinalConfig); // start plugin, init log factory and init engine in this method
constants = Config.getConstants();
//B3.初始化映射,包含controller和intercept
initActionMapping();ui
//B4.初始化Handler
initHandler();this
//B5.初始化Render
initRender();url
//B6.初始化不知道
initOreillyCos();spa
//初始化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 子類的方法,配置用戶自定義controller
jfinalConfig.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();
//初始化TokeninitTokenManager();PS初始化Token以上完成了server初始化操做,全部請求的封裝都是基於server-容器實現,代碼僅到這一層,詳細須要跟蹤容器的實現。天道酬勤!