JFinal 启动、请求源码简析(下篇)

4、JFinal doFilter解析

1.作者用了一个boolean类型的数组来做标记位,标记是否需要doFilter这个请求,有人就会有疑问,为什么不直接用一个boolean类型的变量来做标记,而要用一个数组。《Java虚拟机规范》有一段对boolean类型的描述:“虽然定义了boolean这种数据类型,但是只对它提供了非常有限的支持。在Java虚拟机中没有任何供boolean值专用的字节码指令,Java语言表达式所操作的boolean值,在编译之后都使用Java虚拟机中的int数据类型来代替,而boolean数组将会被编码成Java虚拟机的byte数组,每个元素boolean元素占8位“,《Java虚拟机规范》给出了boolean类型4个字节,和boolean数组1个字节的定义。这里可以看作是作者对内存的一个小的优化。
		HttpServletRequest request = (HttpServletRequest)req;
		HttpServletResponse response = (HttpServletResponse)res;
		request.setCharacterEncoding(encoding);
		
		String target = request.getRequestURI();
		if (contextPathLength != 0)
			target = target.substring(contextPathLength);
		
		boolean[] isHandled = {false};
		try {
			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);
	
1.1  handler.handle方法分析
 Hander链表结构init的时候已经初始化好,这里通过方法回调一直执行Hander如果找到viewPath则返回,否则执行最后的ActionHander
public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) {
		if (target.startsWith(visitPath)) {
			isHandled[0] = true;
			
			if (target.equals(visitPath) && !target.endsWith("/index.html")) {
				HandlerKit.redirect(target += "/index.html", request, response, isHandled);
				return ;
			}
			
			try {
				servlet.service(request, response);
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}
		else {
			nextHandler.handle(target, request, response, isHandled);
		}
	}
这里对Hander进行处理,如果不满足条件则会走到ActionHander里。
以:http://localhost/TestJFinal/blog/find/?id=2 请求为例,看看ActionHander的handle方法具体做了什么事情。

	/**
	 * handle
	 * 1: Action action = actionMapping.getAction(target)
	 * 2: new Invocation(...).invoke()
	 * 3: render(...)
	 */
	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); //根据请求的url获取对应的Action(上面提到的Acition对象(Action@7bbecc3c))
		
		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 ;
		}
		
		try {
			Controller controller = action.getControllerClass().newInstance(); //获取controller实例
			controller.init(request, response, urlPara[0]); //赋值Controller的参数
			
			if (devMode) {
				boolean isMultipartRequest = ActionReporter.reportCommonRequest(controller, action); //这里会打印请求信息
				new Invocation(action, controller).invoke();
				if (isMultipartRequest) ActionReporter.reportMultipartRequest(controller, action);
			}
			else {
				new Invocation(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, action.getViewPath()).render();
		}
		catch (Throwable t) {
			if (log.isErrorEnabled()) {
				String qs = request.getQueryString();
				log.error(qs == null ? target : target + "?" + qs, t);
			}
			renderFactory.getErrorRender(500).setContext(request, response, action.getViewPath()).render();
		}
	}
1.1.1  reportCommonRequest(controller, action)
如果是开发模式,会打印信息,如下:
JFinal action report -------- 2016-10-11 20:00:33 ------------------------------
Controller  : com.demo.jfinal.controller.blog.BlogController.(BlogController.java:1)
Method      : find
Interceptor : com.jfinal.plugin.activerecord.tx.TxByMethods.(TxByMethods.java:1)
Parameter   : id=2  
--------------------------------------------------------------------------------
可以看出通过反射,已经把对象的所有信息都获取到了(从对应Action里面获取)。

1.1.2  new Invocation(action, controller).invoke();
 Invocation is used to invoke the interceptors and the target method。
public void invoke() {
		if (index < inters.length) {
			inters[index++].intercept(this); //逐一执行拦截器方法
		}
		else if (index++ == inters.length) {	// index++ ensure invoke action only one time 拦截器执行完后,执行method方法
			try {
				// Invoke the action
				if (action != null) {
					returnValue = action.getMethod().invoke(target, args);
				}
				// Invoke the method
				else {
					// if (!Modifier.isAbstract(method.getModifiers()))
						// returnValue = methodProxy.invokeSuper(target, args);
					if (useInjectTarget)
						returnValue = methodProxy.invoke(target, args);
					else
						returnValue = methodProxy.invokeSuper(target, args);
				}
			}
			catch (InvocationTargetException e) {
				Throwable t = e.getTargetException();
				throw t instanceof RuntimeException ? (RuntimeException)t : new RuntimeException(e);
			}
			catch (RuntimeException e) {
				throw e;
			}
			catch (Throwable t) {
				throw new RuntimeException(t);
			}
		}
	}

当执行这段代码时:
if (action != null) {
			returnValue = action.getMethod().invoke(target, args);
}
会调用BlogControllder的find方法:
public void find() {
			Blog blog = BlogService.blogService.findBlogById(getParaToInt(BlogConst.ID));
			renderJson(ControllerCommon.ctrCommon.returnJsonToClient(blog)); //执行完成后,就会把JSON对象封装给Controller,最后通过反射调用render方法,返回结果
		}
public void renderJson(Object object) {
		render = renderFactory.getJsonRender(object);
	}
这个时候就把BlogController的数据为:

1.1.3 render.setContext(request, response, action.getViewPath()).render();
最后执行该方法,看看源码是怎么设计的:

public void render() {
		if (jsonText == null)
			buildJsonText();
		
		PrintWriter writer = null;
		try {
			response.setHeader("Pragma", "no-cache");	// HTTP/1.0 caches might not implement Cache-Control and might only implement Pragma: no-cache
			response.setHeader("Cache-Control", "no-cache");
			response.setDateHeader("Expires", 0);
			
			response.setContentType(forIE ? contentTypeForIE : contentType);
			writer = response.getWriter();
	        writer.write(jsonText);
	        writer.flush();
		} catch (IOException e) {
			throw new RenderException(e);
		}
		finally {
			if (writer != null)
				writer.close();
		}
	}
是不是很熟悉,这个时候终于把数据返回了。
2. 返回结果:
这段代码就不会执行了!
if (isHandled[0] == false)
			chain.doFilter(request, response);
返回结果如下:

5、总结

本文对JFinal的初始化和处理请求进行简单的解析,希望能够帮助到大家,同时也提高了自己阅读源码的能力。
相关阅读: JFinal 启动、请求源码简析(上篇)

本文来自网易实践者社区,经作者田躲躲授权发布。