JFinal源码走读_2_运行时初探

本文详细剖析了JFinal框架的运行机制,从过滤器接管请求开始,深入探讨了处理器链处理流程、ActionHandler处理机制、ActionInvocation的工作原理及渲染过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

运行时入口

由于filter拦截了所有的请求url,所以所有访问jfinal webapp 的请求都会被JfinalFilter的doFilter处理。故运行时入口为JFinalFilter的doFilter方法。

public void doFilter(ServletRequest req, ServletResponse res,
            FilterChain chain) throws IOException, ServletException {

        // 获取request,response和为request设置编码
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        request.setCharacterEncoding(encoding);

        // 获取requestUrl,url:port/webappname/xx,/webappname/xx即requestUrl
        String target = request.getRequestURI();
        // contextPath为/webappname,而action中的actionkey是不包含contextpath,所以此处需将requestUrl中的contextPath这一段截掉
        // 此时对于初始化时contextPathLength的作用就明了了,截取之后的只为/xx
        if (contextPathLength != 0)
            target = target.substring(contextPathLength);

        // 声明hander处理的标识量
        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);
            }
        }

        // 此句涉及到handle链的结构和handler的具体处理,初始化阶段时,handler链的末端是actionHandler,而只有调用acitonHandler的handle方法之后,isHandled[0]才会被赋值为true
        // handler.handle方法内部会递归调用下一个handler的handle方法,而有些处理可能调用不到最末端的actionHandle的handle方法,此时isHandled[0]为false
        // 虽然demo中没有配置额外的handler,但是如ServerNameRedirect301Handler,UrlSkipHandler都是在满足一定条件才会继续递归往下调用的
        if (isHandled[0] == false)
            // jfinal程序中可以配置多个filter,由此句可见端倪
            chain.doFilter(request, response);
    }

handler.handle(target, request, response, isHandled)的探究

由于暂时没有其他的handle配置,所以暂时只分析ActionHandler的handle方法实现

public final void handle(String target, HttpServletRequest request,
            HttpServletResponse response, boolean[] isHandled) {
        // target含有‘.’ 则不进行处理
        if (target.indexOf('.') != -1) {
            return;
        }
        // 使标识量为true,说明handler链上的所有handler都已经处理过了
        isHandled[0] = true;

        String[] urlPara = { null };

        // 获取target对应的action,即actionKey对应的action
        Action action = actionMapping.getAction(target, urlPara);

        // 没有获取到对应的action,记录异常,并渲染404页面,处理完成,响应返回客户端
        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实例,感觉此处应有改善空间,对于低并发来说,没什么压力,高并发的话,开销不应该考虑么?
            // 以空间换效率应该是可行的,由actionHandle维护一个Controller缓存池
            Controller controller = action.getControllerClass().newInstance();
            // 初始化 简单赋值
            controller.init(request, response, urlPara[0]);

            // 根据devMode决定是否在控制台打印处理信息
            if (devMode) {
                boolean isMultipartRequest = ActionReporter
                        .reportCommonRequest(controller, action);
                new ActionInvocation(action, controller).invoke();
                if (isMultipartRequest)
                    ActionReporter.reportMultipartRequest(controller, action);
            } else {
                // 1.核心代码,延后分析
                new ActionInvocation(action, controller).invoke();
            }

            // 获取render,render的具体值是由上一步得到的,因为每个action最终都需要渲染,最后一步执行的方法确定了render的值,对于某些不涉及到渲染操作的action,比如对数据库进行存储的,会返回null。
            Render render = controller.getRender();

            // 暂时没碰到ActionRender,碰到了再回头分析
            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;
            }

            // 对于某些不涉及到渲染操作的action,比如对数据库进行存储的,会返回null,此时将render赋值为jfinal的缺省设置
            if (render == null)
                render = renderFactory.getDefaultRender(action.getViewPath()
                        + action.getMethodName());
            // 为render设置上下文环境并渲染
            // 至此actionhandler的整个处理流程走完
            // jspRender的render方法延后分析
            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 (Throwable t) {
            if (log.isErrorEnabled()) {
                String qs = request.getQueryString();
                log.error(qs == null ? target : target + "?" + qs, t);
            }
            renderFactory.getErrorRender(500).setContext(request, response)
                    .render();
        }
    }

new ActionInvocation(action, controller).invoke();的探究

1.新对象

ActionInvocation(Action action, Controller controller) {
        this.controller = controller;
        this.inters = action.getInterceptors();
        this.action = action;
    }

2.invoke()方法

public void invoke() {
        // inters为此action的拦截器栈,依次执行此action的拦截器,由初始化过程可知,拦截器顺序依次为 全局,controller,method
         // 此方法是个递归方法 拦截器01_before->拦截器02_before->xx_before->action.getMethod.invoke(controller,NULL_ARGS)->xx_after->拦截器02_after->拦截器01_after
        if (index < inters.length)
            inters[index++].intercept(this);
        else if (index++ == inters.length)    // index++ ensure invoke action only one time
            // 拦截器执行完,则反射执行action对应的method方法
            try {
                action.getMethod().invoke(controller, NULL_ARGS);
            }
            // 异常处理
            catch (InvocationTargetException e) {
                Throwable cause = e.getTargetException();
                if (cause instanceof RuntimeException)
                    throw (RuntimeException)cause;
                throw new RuntimeException(e);
            }
            catch (RuntimeException e) {
                throw e;
            }
            catch (Exception e) {
                throw new RuntimeException(e);
            }
    }

render.setContext(request, response, action.getViewPath()).render()的探究

只分析jspRender的render方法

public void render() {
        // 在 jsp 页面使用如下指令则无需再指字符集, 否则是重复指定了,与页面指定的不一致时还会出乱码
        // <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
        // response.setContentType(contentType);
        // response.setCharacterEncoding(encoding);

        try {
            // 暂时不清楚activeRecord是何许神圣,暂不分析
            if (isSupportActiveRecord)
                supportActiveRecord(request);

            // jspRender利用基础的分发器转发请求,至此一个请求到相应的流程走完,即V->C->V
            request.getRequestDispatcher(view).forward(request, response);
        } catch (Exception e) {
            throw new RenderException(e);
        }
    }

运行时小结

运行时从filter接管->处理器链处理->actionHandle处理->具体action处理->render 结束,此中包含了actionRender,由于暂时没有碰到actionRender,暂不分析

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值