JFinal學習筆記web
一切都從web.xml開始提及:服務器
當服務器初始化的時候會初始化web.xml裏面的相關配置信息。下面咱們來看一個重要的過濾器配置:JFinalFilter。下面是相關的配置信息app
<filter>jsp
<filter-name>jfinal</filter-name>ide
<filter-class>com.jfinal.core.JFinalFilter</filter-class>學習
<init-param> ui
<param-name>configClass</param-name> this
<param-value>demo.DemoConfig</param-value> url
</init-param>spa
</filter>
<filter-mapping>
<filter-name>jfinal</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</filter>
這就意味着全部的請求都要通過JFinalFiter過濾了
從中咱們能夠看到在web.xml之中配置了一個名爲JFinalFilter的過濾器。下面咱們來看看JFinalFiter的源碼。
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 Logger log;
private int contextPathLength;
//系統在初始化Servlet的時候自動調用該方法
public void init(FilterConfig filterConfig) throws ServletException {
//建立JFianlConfig對象
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();
//若是項目路徑爲null或者只有一個'/',就定義項目路徑的長度爲0,不然仍是其原來的長度。
contextPathLength = (contextPath == null || "/".equals(contextPath) ? 0 : contextPath.length());
}
//request和response的做用不用過多介紹了。這個FilterChain的左右主要是用來連續調用下一個Filter時候使用的,下面給出了FilterChain的做用介紹
/**
* A FilterChain is an object provided by the servlet container to the developer
* giving a view into the invocation chain of a filtered request for a resource. Filters
* use the FilterChain to invoke the next filter in the chain, or if the calling filter
* is the last filter in the chain, to invoke the resource at the end of the chain.
*
* @see Filter
* @since Servlet 2.3
**/
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
request.setCharacterEncoding(encoding);
String target = request.getRequestURI();
if (contextPathLength != 0)
//獲得其ServletPath以及相關的參數
target = target.substring(contextPathLength);
boolean[] isHandled = {false};
try {
//idHandler用來判斷該Target是否應該被相應的handler處理
//這是整個Filter的最核心方法
handler.handle(target, request, response, isHandled);
}
catch (Exception e) {
if (log.isErrorEnabled()) {
String qs = request.getQueryString();
log.error(qs == null ? target : target + "?" + qs, e);
}
}
if (isHandled[0] == false)
chain.doFilter(request, response);
}
public void destroy() {
jfinalConfig.beforeJFinalStop();
jfinal.stopPlugins();
}
private void createJFinalConfig(String configClass) {
if (configClass == null)
throw new RuntimeException("Please set configClass parameter of JFinalFilter in web.xml");
try {
Object temp = Class.forName(configClass).newInstance();
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");
} catch (InstantiationException e) {
throw new RuntimeException("Can not create instance of class: " + configClass, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Can not create instance of class: " + configClass, e);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Class not found: " + configClass + ". Please config it in web.xml", e);
}
}
static void initLogger() {
log = Logger.getLogger(JFinalFilter.class);
}
}
讓咱們重點看看這個handler的由來。
首先由handler = jfinal.getHandler();知道這個handler是由jfinal對象得來的,如今讓咱們看看jfinal的部分源碼:
public final class JFinal {
private Constants constants;
private ActionMapping actionMapping;
private Handler handler;。。。。。定義了其餘成員變量
Handler getHandler() {
return handler;
}
private static final JFinal me = new JFinal();
//初始化JFinal時候調用的方法(在上面已經提到過這一點)
boolean init(JFinalConfig jfinalConfig, ServletContext servletContext) {
this.servletContext = servletContext;
this.contextPath = servletContext.getContextPath();
initPathUtil();
Config.configJFinal(jfinalConfig); // start plugin and init logger factory in this method
constants = Config.getConstants();
initActionMapping();
initHandler();
initRender();
initOreillyCos();
initI18n();
initTokenManager();
return true;
}
private void initHandler() {
Handler actionHandler = new ActionHandler(actionMapping, constants);
handler = HandlerFactory.getHandler(Config.getHandlers().getHandlerList(), actionHandler);
}
}
由這裏咱們知道handler是由HandlerFactory的getHandler方法得來的。
讓咱們再次看看HandlerFactory的部分源碼以探個究竟:
public class HandlerFactory {
private HandlerFactory() {
}
/**
* Build handler chain
*/
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.nextHandler = result;
result = temp;
}
return result;
}
}
顯然這裏返回的是一個actionHandler爲首handler chain。
讓咱們再來看看這個:
Handler actionHandler = new ActionHandler(actionMapping, constants);
handler = HandlerFactory.getHandler(Config.getHandlers().getHandlerList(), actionHandler);
此處傳進去並非簡單的handler,而是他的字類ActionHandler,而且傳進去了有兩個參數,一個是ActionMapping類的變量,一個是constants。對於後者將就是一些常量的設置因此不進行過多介紹。讓咱們先看看ActionMapping以後再來看這個ActionHandler。
final class ActionMapping {
private static final String SLASH = "/";
private Routes routes;
private Interceptors interceptors;
private final Map<String, Action> mapping = new HashMap<String, Action>();
ActionMapping(Routes routes, Interceptors interceptors) {
this.routes = routes;
this.interceptors = interceptors;
}
在ActionMapping中定義了一個路由(routes)和一個Interceptors,這個routes類裏面主要的核心是兩個Map,內容以下(截取了部分源碼過來):
//每個訪問路徑(controllerKey)都對應有一個相應的controller,並做爲一對Entry放到map中
private final Map<String, Class<? extends Controller>> map = new HashMap<String, Class<? extends Controller>>();
//每個訪問路徑(controllerKey)都對應一個在項目中的實際存放路徑(WEB-INF/index.jsp等等),並做爲一對Entry放到viewPathMap中
private final Map<String, String> viewPathMap = new HashMap<String, String>();
所以咱們知道這個ActionHandler就是處理一些關於ActionMapping中對應的ControllerKey與Controller.class的事情。
因此如今既然這些都已經清楚了,咱們能夠看看ActionHandler的廬山真面目了。
在ActionHandler中咱們能夠看到這樣一行註釋:
/**
* handle
* 1: Action action = actionMapping.getAction(target)
* 2: new ActionInvocation(...).invoke()
* 3: render(...)
*/
這就解釋了handle方法須要作的事情了,首先是根據ActionMapping得到相應的Action,而後利用反射進行方法的調用,最後把結果映射到相應的頁面上去。這就是核心的三個步驟了,接下來讓咱們詳細的讀一下這個源碼:
final class ActionHandler extends Handler {
private final boolean devMode;
private final ActionMapping actionMapping;
private static final RenderFactory renderFactory = RenderFactory.me();
private static final Logger log = Logger.getLogger(ActionHandler.class);
public ActionHandler(ActionMapping actionMapping, Constants constants) {
this.actionMapping = actionMapping;
this.devMode = constants.getDevMode();
}
/**
* handle
* 1: Action action = actionMapping.getAction(target)
* 2: new ActionInvocation(...).invoke()
* 3: render(...)
*/
//這裏進行了核心的handle方法描述:
這裏的target爲如下格式:http://localhost:8080/ContextPath/ControllerKey/MethodName/parameters
public final void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) {
if (target.indexOf(".") != -1) {
return ;
}
isHandled[0] = true;
String[] urlPara = {null};
Action action = actionMapping.getAction(target, urlPara);
//判斷Action是否爲空
if (action == null) {
if (log.isWarnEnabled()) {
String qs = request.getQueryString();
log.warn("404 Action Not Found: " + (qs == null ? target : target + "?" + qs));
}
renderFactory.getErrorRender(404).setContext(request, response).render();
return ;
}
//Action不爲空時候:
try {
//獲得對應的Controller
Controller controller = action.getControllerClass().newInstance();
controller.init(request, response, urlPara[0]);
if (devMode) {
boolean isMultipartRequest = ActionReporter.reportCommonRequest(controller, action);
//利用反射執行相關的Action
new ActionInvocation(action, controller).invoke();
if (isMultipartRequest) ActionReporter.reportMultipartRequest(controller, action);
}
else {
new ActionInvocation(action, controller).invoke();
}
Render render = controller.getRender();
if (render instanceof ActionRender) {
String actionUrl = ((ActionRender)render).getActionUrl();
if (target.equals(actionUrl))
throw new RuntimeException("The forward action url is the same as before.");
else
handle(actionUrl, request, response, isHandled);
return ;
}
if (render == null)
render = renderFactory.getDefaultRender(action.getViewPath() + action.getMethodName());
render.setContext(request, response, action.getViewPath()).render();
}
catch (RenderException e) {
if (log.isErrorEnabled()) {
String qs = request.getQueryString();
log.error(qs == null ? target : target + "?" + qs, e);
}
}
catch (ActionException e) {
int errorCode = e.getErrorCode();
if (errorCode == 404 && log.isWarnEnabled()) {
String qs = request.getQueryString();
log.warn("404 Not Found: " + (qs == null ? target : target + "?" + qs));
}
else if (errorCode == 401 && log.isWarnEnabled()) {
String qs = request.getQueryString();
log.warn("401 Unauthorized: " + (qs == null ? target : target + "?" + qs));
}
else if (errorCode == 403 && log.isWarnEnabled()) {
String qs = request.getQueryString();
log.warn("403 Forbidden: " + (qs == null ? target : target + "?" + qs));
}
else if (log.isErrorEnabled()) {
String qs = request.getQueryString();
log.error(qs == null ? target : target + "?" + qs, e);
}
e.getErrorRender().setContext(request, response).render();
}
catch (Exception e) {
if (log.isErrorEnabled()) {
String qs = request.getQueryString();
log.error(qs == null ? target : target + "?" + qs, e);
}
renderFactory.getErrorRender(500).setContext(request, response).render();
}
}
}
到這裏,咱們簡單了看了一下JFinal的實現原理。