首先要從 web 容器進行初始化
<?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.fw.config.MppConfig</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>jfinal</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
從 web.xml 可以看出,容器初始化的時候會加載 JFinalFilter 這個類並且調用其 init 方法。
public final class JFinalFilter implements Filter {
private Handler handler;
private String encoding;
private JFinalConfig jfinalConfig;
private Constants constants;
private static final JFinal jfinal = JFinal.me();
private static Log log;
private int contextPathLength;
public void init(FilterConfig filterConfig) throws ServletException {
createJFinalConfig(filterConfig.getInitParameter("configClass"));
if (jfinal.init(jfinalConfig, filterConfig.getServletContext()) == false)
throw new RuntimeException("JFinal init error!");
handler = jfinal.getHandler();
constants = Config.getConstants();
encoding = constants.getEncoding();
jfinalConfig.afterJFinalStart();
String contextPath = filterConfig.getServletContext().getContextPath();
contextPathLength = (contextPath == null || "/".equals(contextPath) ? 0 : contextPath.length());
}
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");
}
// ...
}
init 方法中的參數正是 web.xml 中的 JFinalFilter 的初始化參數,即 com.fw.config.MppConfig,它繼承於 JFinalConfig,在 createJFinalConfig 中利用該參數使用反射機制得到 JFinalConfig 的實例。
jfinal.init
boolean init(JFinalConfig jfinalConfig, ServletContext servletContext) {
this.servletContext = servletContext;
this.contextPath = servletContext.getContextPath();
initPathUtil();
Config.configJFinal(jfinalConfig);
constants = Config.getConstants();
initActionMapping();
initHandler();
initRender();
initOreillyCos();
initTokenManager();
return true;
}
第一、initPathUtil,初始化 Path工具類的 webRootPath(即項目根路徑)。
private void initPathUtil() {
String path = servletContext.getRealPath("/");
PathKit.setWebRootPath(path);
}
第二、Config.configJFinal 加載 JFinalConfig 實例,進行一些配置。
static void configJFinal(JFinalConfig jfinalConfig) {
jfinalConfig.configConstant(constants); initLogFactory();
jfinalConfig.configRoute(routes);
jfinalConfig.configPlugin(plugins); startPlugins(); // very important!!!
jfinalConfig.configInterceptor(interceptors);
jfinalConfig.configHandler(handlers);
}
配置常量,初始化 Log 工具類,配置路由,配置插件,開啟插件,配置攔截器,配置 handler。
下面看看我項目中的 JFinalConfig 實例。
public class MppConfig extends JFinalConfig {第三、初始化 ActionMapping、Handler、Render等。
initActionMapping(); initHandler(); initRender(); initOreillyCos(); initTokenManager();
private void initActionMapping() {
actionMapping = new ActionMapping(Config.getRoutes(), Config.getInterceptors());
actionMapping.buildActionMapping();
Config.getRoutes().clear();
}
第一、創建 ActionMapping,映射所有訪問路徑和其對應的Action,填充得到一個 HashMap。下面是創建 ActionMapping 代碼和注釋,涉及到其他類的源碼請自行查看。
final class ActionMapping {
private static final String SLASH = "/";
private Routes routes;
// ActionMapping 映射
private final Map<String, Action> mapping = new HashMap<String, Action>();
// 構造方法,routes 參數傳進來
ActionMapping(Routes routes, Interceptors interceptors) {
this.routes = routes;
}
private Set<String> buildExcludedMethodName() {
Set<String> excludedMethodName = new HashSet<String>();
Method[] methods = Controller.class.getMethods();
for (Method m : methods) {
if (m.getParameterTypes().length == 0)
excludedMethodName.add(m.getName());
}
return excludedMethodName;
}
void buildActionMapping() {
// 初始化,我將要向裡面塞東西了,要清空一下
mapping.clear();
// 這個方法返回的是 Controller接口的所有方法集合。
Set<String> excludedMethodName = buildExcludedMethodName();
// 得到 InterceptorManager 的實例
InterceptorManager interMan = InterceptorManager.me();
// 遍歷一個 Entry 集合。Entry 的 key 為訪問路徑,value 為其對應的 Controller。
for (Entry<String, Class<? extends Controller>> entry : routes.getEntrySet()) {
// 得到訪問路徑對應的 Controller class
Class<? extends Controller> controllerClass = entry.getValue();
// 如果 Controller class沒有攔截器注解,則返回一個空數組。反之返回這個類所有攔截器組成的數組
Interceptor[] controllerInters = interMan.createControllerInterceptor(controllerClass);
boolean sonOfController = (controllerClass.getSuperclass() == Controller.class); // 這裡必定為 true
// getDeclaredMethods 得到這個類的所有方法以及其接口的所有方法,不包括繼承的方法
Method[] methods = (sonOfController ? controllerClass.getDeclaredMethods() : controllerClass.getMethods());
for (Method method : methods) {
String methodName = method.getName();
//若這個方法是繼承自 Controller的方法 或 該方法有參數,過濾掉
if (excludedMethodName.contains(methodName) || method.getParameterTypes().length != 0)
continue ;
// 若這個方法不是 public 方法,過濾掉
if (sonOfController && !Modifier.isPublic(method.getModifiers()))
continue ;
// 想進行到這裡,這個方法必須滿足:不是繼承自 Controller、不能有參數、必須是 public 方法
// 得到包含所有攔截器的數組(包括全局的攔截器,類級別的攔截器、方法級別的攔截器)
Interceptor[] actionInters = interMan.buildControllerActionInterceptor(controllerInters, controllerClass, method);
String controllerKey = entry.getKey();
ActionKey ak = method.getAnnotation(ActionKey.class);
String actionKey;
// ActionKey 不為空的話為設置自定義的訪問路徑(說明有方法被注有 @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;
}
// ActionKey為空,methodName 為 index的情況下:actionKey = controllerKey
else if (methodName.equals("index")) {
actionKey = controllerKey;
}
// ActionKey為空,methodName 不為 index的情況下:actionKey = controllerKey +"/" + methodName
else {
actionKey = controllerKey.equals(SLASH) ? SLASH + methodName : controllerKey + SLASH + methodName;
}
// 組合裝配成一個 Action
Action action = new Action(controllerKey, actionKey, controllerClass, method, methodName, actionInters, routes.getViewPath(controllerKey));
// 填充 HashMap(訪問路徑為 key,Action 為 value)
if (mapping.put(actionKey, action) != null)
throw new RuntimeException(buildMsg(actionKey, controllerClass, method));
}
}
// support url = controllerKey + urlParas with "/" of controllerKey
Action action = mapping.get("/");
if (action != null)
mapping.put("", action);
}
// ...
}
View Code
第二、Config.routes 已經用過了,以後也不會再用上了,清空路由的所有信息。
private void initHandler() {
Handler actionHandler = new ActionHandler(actionMapping, constants);
handler = HandlerFactory.getHandler(Config.getHandlers().getHandlerList(), actionHandler);
}
第一、創建一個 ActionHandler 實例 actionHandler。
第二、HandlerFactory.getHandler 創建一個 Handler 鏈,鏈尾是 actionHandler,並且返回鏈首。
public class HandlerFactory {
private HandlerFactory() {}
/**
* 創建一個 handler 鏈條,最後返回的 result 是 handler 鏈的頭部,鏈尾是 ActionHandler
*/
@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;
}
}
private void initRender() {
RenderFactory.me().init(constants, servletContext);
}
繼續跟~
public class RenderFactory {private void initOreillyCos() {
OreillyCos.init(constants.getBaseUploadPath(), constants.getMaxPostSize(), constants.getEncoding());
}
很簡單,附上相關代碼:
public class OreillyCos {
private void initTokenManager() {
ITokenCache tokenCache = constants.getTokenCache();
if (tokenCache != null)
TokenManager.init(tokenCache);
}
先給個代碼吧:
public class TokenManager {
private static ITokenCache tokenCache;
private static Random random = new Random();
private TokenManager() {
}
public static void init(ITokenCache tokenCache) {
if (tokenCache == null)
return;
TokenManager.tokenCache = tokenCache;
long halfTimeOut = Const.MIN_SECONDS_OF_TOKEN_TIME_OUT * 1000 / 2; // Token最小過期時間的一半時間作為任務運行的間隔時間
new Timer().schedule(new TimerTask() {public void run() {removeTimeOutToken();}},
halfTimeOut,
halfTimeOut);
}
// ...
}
----- End -----
Jfinal學習之路---Controller使用 http://www.linuxidc.com/Linux/2014-07/104323.htm
JFinal開發8個常見問題 http://www.linuxidc.com/Linux/2015-02/113421.htm
JFinal的詳細介紹:請點這裡
JFinal的下載地址:請點這裡