本文对Jfinal的控制器源码做以下分析。

PS:控制器是所有请求跳转的基础,本文就Jfinal控制器的继承关系及初始化的方法做出解释说明。

啰嗦下:所有的请求和响应都是都是通过web容器封装,我们主要跟踪JFinal的Controller是如何被初始化、如何处理业务逻辑,后续需要研究容器的加载机制,进而晋升到高级甚至技术专家的程度。

源码分析总结:慢慢随着源码的深入,渐渐的对源码不会那那么的反感,刚开始读一定要静下心来,一步一脚印,别人用好几年甚至几十年写的代码,对于一个初中级开发来说,很难理解的其设计的核心,我们只有一边又一边的反复读取,才能理解作者设计的思想,无形之中提高自己的代码水平和代码架构;总之,天道酬勤、持之以恒。

  • JFinal中Controller的继承关系
    JFinal中自定义的控制器通过继承JFinal提供的Controller(抽象的)即可,该类中有四个重要成员变量需要我们关注

    public abstract class Controller {
    //Web请求
    private HttpServletRequest request;
    //响应
    private HttpServletResponse response;
    //Url参数
    private String urlPara;
    //Url参数数组
    private String[] urlParaArray;
    }

    PS:后续我们重点跟踪request、response、urlPara是如何被初始化,我们猜想:这些变量都是Jfinal框架本身从web容器中获取。

    留个疑问:自定义的Controller是 单例吗?

  • 自定义Controller的写法
    如上所示,自定义的Controller通过继承抽象类Controller即可,实例代码如下
public class BlogController extends Controller {

    static BlogService service = new BlogService();

    public void index() {
setAttr("blogPage", service.paginate(getParaToInt(0, 1), 10));
render("blog.html");
}
}

将Controller绑定给容器
通过JFinalConfig实现的ConfigRoute方式将自定义的Controller绑定给容器,供请求时候调用,如下

     /**
* 配置路由
*/
public void configRoute(Routes me) {
me.add("/", IndexController.class, "/index"); // 第三个参数为该Controller的视图存放路径
me.add("/blog", BlogController.class); // 第三个参数省略时默认与第一个参数值相同,在此即为 "/blog"
}

PS:为何不弄个注解,让JFinal自动扫描自定义的控制器呢?按照常规思想,实现注解,省略了配置代码多好

如果不弄注解:我将所有的Controller能够统一管理,能够快速找到我自定义的控制器有哪些,鄙人猜测,这应该是作者不整个Controller上的注解的原因吧,但我觉得这样的注解还是有必要的,毕竟每个人的使用方法不一样,框架本身应该要满足庞大客户群体的需求,该注解可自定添加,重新JFinal加载Controller的逻辑代码即可

  • Web过滤器
    通过重新Servlet.Filter的过滤方法,获取请求的Request和Response和相关参数,交由用户自定义的控制去实现相关的逻辑。
    过滤器的代码如下

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    //从过滤器中获取请求
    HttpServletRequest request = (HttpServletRequest)req;
    //从过滤器中获取响应
    HttpServletResponse response = (HttpServletResponse)res;
    //设置请求的编码
    request.setCharacterEncoding(encoding);
    //获取请求的URI
    String target = request.getRequestURI();
    if (contextPathLength != 0) {
    //从URI中去除根目录
    target = target.substring(contextPathLength);
    } boolean[] isHandled = {false};
    try {
    //处理器这块需要重点关注,经过分析处理器的实现为:ActionHandler
    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);
    }
    }

    接下里我们分析ActionHandler中handle方法的逻辑

        public final void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) {
    if (target.indexOf('.') != -1) {
    return ;
    } isHandled[0] = true;
    String[] urlPara = {null};
    //基于URI和参数获取请求的action
    Action action = actionMapping.getAction(target, urlPara); if (action == null) {
    if (log.isWarnEnabled()) {
    String qs = request.getQueryString();
    //Action为null的情况下,记录查询字符串的日志
    log.warn("404 Action Not Found: " + (qs == null ? target : target + "?" + qs));
    }
    renderManager.getRenderFactory().getErrorRender(404).setContext(request, response).render();
    return ;
    } try {
    //从Action中获取当前请求对应的Controller的对象,这个地方每次都是实例化一次,所以JFinal的Controller不是单例
    Controller controller = action.getControllerClass().newInstance();
    //controller调用初始化方法,赋值请求、响应和url参数
    controller.init(request, response, urlPara[0]); if (devMode) {
    if (ActionReporter.isReportAfterInvocation(request)) {
    new Invocation(action, controller).invoke();
    ActionReporter.report(target, controller, action);
    } else {
    ActionReporter.report(target, controller, action);
    new Invocation(action, controller).invoke();
    }
    }
    else {
    //反射调用,仔细跟
    new Invocation(action, controller).invoke();
    } Render render = controller.getRender();
    if (render instanceof ForwardActionRender) {
    String actionUrl = ((ForwardActionRender)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 = renderManager.getRenderFactory().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();
    String msg = null;
    if (errorCode == 404) {
    msg = "404 Not Found: ";
    } else if (errorCode == 401) {
    msg = "401 Unauthorized: ";
    } else if (errorCode == 403) {
    msg = "403 Forbidden: ";
    } if (msg != null) {
    if (log.isWarnEnabled()) {
    String qs = request.getQueryString();
    log.warn(msg + (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 (Exception e) {
    if (log.isErrorEnabled()) {
    String qs = request.getQueryString();
    log.error(qs == null ? target : target + "?" + qs, e);
    }
    renderManager.getRenderFactory().getErrorRender(500).setContext(request, response, action.getViewPath()).render();
    }
    }

    以上代码通过反射代理的方式调用了Controller中的方法,最终renderManager实现跳转,Render是Jfinal对应web响应的一个封装,这种设计思想在实际开发中很有必要,简洁代码,使用方便,下文给出Render实现类

    PS:Render这块是对响应进行了封装,具体更多的东西还需要自行分析和总机。

天道酬勤,努力每一天,也欢迎您的指正,共同进步,请联系cnxieyang@163.com

Jfinal控制器源码解读的更多相关文章

  1. Jfinal启动源码解读

    本文对Jfinal的启动源码做解释说明. PS:Jfinal启动容器可基于Tomcat/Jetty等web容器启动,本文基于Jetty的启动方式做启动源码的解读和分析,tomcat类似. 入口  JF ...

  2. Jfinal-Plugin源码解读

    PS:cnxieyang@163.com/xieyang@e6yun.com 本文就Jfinal-plugin的源码进行分析和解读 Plugin继承及实现关系类图如下,常用的是Iplugin的三个集成 ...

  3. JFinal的启动源码解读

    本文对Jfinal的启动源码做解释说明. PS:Jfinal启动容器可基于Tomcat/Jetty等web容器启动,本文基于Jetty的启动方式做启动源码的解读和分析,tomcat类似. 入口  JF ...

  4. AFNetworking 3.0 源码解读 总结(干货)(上)

    养成记笔记的习惯,对于一个软件工程师来说,我觉得很重要.记得在知乎上看到过一个问题,说是人类最大的缺点是什么?我个人觉得记忆算是一个缺点.它就像时间一样,会自己消散. 前言 终于写完了 AFNetwo ...

  5. AFNetworking 3.0 源码解读 总结

    终于写完了 AFNetworking 的源码解读.这一过程耗时数天.当我回过头又重头到尾的读了一篇,又有所收获.不禁让我想起了当初上学时的种种情景.我们应该对知识进行反复的记忆和理解.下边是我总结的 ...

  6. Alamofire源码解读系列(六)之Task代理(TaskDelegate)

    本篇介绍Task代理(TaskDelegate.swift) 前言 我相信可能有80%的同学使用AFNetworking或者Alamofire处理网络事件,并且这两个框架都提供了丰富的功能,我也相信很 ...

  7. Alamofire源码解读系列(七)之网络监控(NetworkReachabilityManager)

    Alamofire源码解读系列(七)之网络监控(NetworkReachabilityManager) 本篇主要讲解iOS开发中的网络监控 前言 在开发中,有时候我们需要获取这些信息: 手机是否联网 ...

  8. Spark Streaming源码解读之流数据不断接收和全生命周期彻底研究和思考

    本节的主要内容: 一.数据接受架构和设计模式 二.接受数据的源码解读 Spark Streaming不断持续的接收数据,具有Receiver的Spark 应用程序的考虑. Receiver和Drive ...

  9. DRF(1) - REST、DRF(View源码解读、APIView源码解读)

    一.REST 1.什么是编程? 数据结构和算法的结合. 2.什么是REST? 首先回顾我们曾经做过的图书管理系统,我们是这样设计url的,如下: /books/ /get_all_books/ 访问所 ...

随机推荐

  1. do {...} while (0) 在宏定义中的作用

    如果你是一名C程序员,你肯定很熟悉宏,它们非常强大,如果正确使用可以让你的工作事半功倍.然而,如果你在定义宏时很随意没有认真检查,那么它们可能使你发狂,浪费N多时间.在很多的C程序中,你可能会看到许多 ...

  2. ES6常用语法

    ECMAScript 6(以下简称ES6)是JavaScript语言的下一代标准.因为当前版本的ES6是在2015年发布的,所以又称ECMAScript 2015. 也就是说,ES6就是ES2015. ...

  3. open-falcon(v0.2)部署手册(源码编译)

    今天安装falcon-plus,下面为用基础环境配置. centos 6.8  alisql5.6.32   redis-3.2.8 cmake-3.9.1 bison-3.0 openssl-1.0 ...

  4. bzoj 2427: [HAOI2010]软件安装

    Description 现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi.我们希望从中选择一些软件安装到一台磁盘容量为M计算机上,使得这些软件的价值尽可能大(即Vi的和 ...

  5. php date函数

    PHP星期几获取代码: 1 date("l"); 2 //data就可以获取英文的星期比如Sunday 3 date("w"); 4 //这个可以获取数字星期比 ...

  6. 重启网络服务时 Bringing up interface eth0

    重启网络服务时报错:  Bringing up interface eth0: Error:Connection activation failed:Device not managed by Net ...

  7. bat检测文件大小并邮件报警

    rem 获取当前日期 set TimeName=%date:~0,4%%date:~5,2%%date:~8,2% rem 获取文件名 set file=%TimeName%   rem 获取文件大小 ...

  8. 发布 Google Chrome插件教程

    换个视角,世界不一样.嘘~~~ 如果你会使用js的话,那么你就可以自己动手写一个chrome插件,而且非常容易.google是一个全球化的平台,想想自己的程序被世界人民所使用,是不是很激动? 注册开发 ...

  9. Python函数篇(7)-正则表达式

    1.正则表达式   正则表达式为高级的文本模式匹配,抽取,与/或文本形式的搜索和替换功能提供了基础,简单的来说,正则表达式是由一些字符和特殊符号组成的字符串.Python通过标准库中的re模块来支持正 ...

  10. Java订单功能模块设计与实现

    在商城项目中,之前我们介绍了购物车功能模块的实现,商品加入到购物车之后,就是到购物车结算,然后显示购物车的商品列表,点击去结算,然后到了未提交前的订单列表, 点击提交订单后,生成此订单,返回订单的订单 ...