ExtJS+SpringMVC+Spring+Hibernate的一种实现(蒋锋代码分析)

今天以前我写的java环境下web应用都是这种形式:
1--自己写的@Controller中的handler方法采用Spring+Hibernate的方式读取数据,读取到我们自定义的PO中(从硬盘读取到内存中)。
2--然后采用SpringMVC的ModelAndView的方法addObject()将得到的PO或者PO的List放入ModelAndView的实例中。
3--然后handler方法中setViewName(),return。相当于返回string或者4--ModelAndView给SpringMVC,也就是交给指定的jsp页面以数据。
然后在对应的jsp页面中使用jstl的标签读取返回的数据,并将数据交给html中的标签来显示。
上述过程对应的代码如下:
首先,自己写的Controller代码,位于对应的web文件夹中
ExtJS+SpringMVC+Spring+Hibernate的一种实现(蒋锋代码分析)

@Controller
public class CheckListController extends BaseController{
    
    private CheckListService cls;
    public CheckListService getCls() {
        return cls;
    }
    @Autowired
    public void setCls(CheckListService cls) {
        this.cls = cls;
    }
    
    /**
     * 显示所有  待检单
     * @return
     */
    @RequestMapping("/checklist")
    public ModelAndView getCLs(){
        List<CheckList> lresult = cls.getCLs(); //利用Spring从硬盘获取数据,保存到PO的List中(硬盘-->内存)
        ModelAndView mav = new ModelAndView();
        mav.addObject("lresult", lresult);//将返回值放入ModelAndView的实例
        mav.setViewName("checklist/showall");   //设置被显示数据所用到的JSP页面
        return mav;
    }

}

上述代码就是Spring中Controller部分的java代码
返回之后的工作,由SpringMVC的ViewResolver来做,也就是由框架来做,定位到对应的jsp页面,把数据暴露给它。
然后我们去jsp页面看看

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
//导入jstl
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<link rel="stylesheet" href="css/view_all.css">
<title>显示待检单</title>
<style>

</style>
</head>
<body>
<!-- 左侧导航菜单 -->
<%@ include file="../pub/leftMenu.jsp" %>
    <div id="testData">
        <table border="0px" width="100%">
        <tr><th>SN</th><th>待检单</th><th>检验者</th></tr>
            <c:set var="sn" value="0" />
            <!--利用jstl获取Controller中返回并暴露的数据,然后显示数据-->
            <c:forEach var="cl_temp" items="${lresult}" varStatus="vs1">
            <c:set var="sn" value="${sn+1}" />
               <tr <c:if test="${vs1.index%2==0}" > style="background-color:#eaeaea"</c:if> >
                <td width = "5%">${sn}</td>
                <td width = "60%">${cl_temp.clName}</td>
                <td>${cl_temp.clHandler}</td>
               </tr>
            </c:forEach>
        </table>
    </div>
    
</body>
</html>

上述过程完整的表达了,之前我采用Spring+SpringMVC进行读数据库--返回给jsp---由jsp显示数据的过程。
但是,太土了。
原因有两个:
1--显示页面,就是纯粹的JSP页面这样很土,工作量也很大。我们要用的是高端大气的ExtJS所以,你的写法肯定不行。
2--这种写法实际上没有理解SpringMVC的前提下,一种十分粗略的写法。
看了蒋锋老师的代码,认真学习了SpringMVC的教材,我发现,得改,下面是蒋锋老师写的代码。先给阿托老师打个call吧这个网址:http://www.jhopesoft.com/ 可以看到他的项目、源码以及实例运行demo
quick-build-opensource项目采用ExtJS+Spring+SpringMVC+Hibernate实现
我们来看一下最基本的一个场景:login登录
说句题外话:其实正确的代码分析过程应该是导入svn然后运行,然后在eclipse中对照代码修改看效果,但是我还没有成功导入,所以就断片得看代码了(利用官网demo和下载来的war文件中的源码,实际二者本应该联动,但是这种形式分析代码是无法联动的)。
ExtJS+SpringMVC+Spring+Hibernate的一种实现(蒋锋代码分析)
登录页面如上图所示,下面我们来看的是,从显示这个页面,到输入用户名密码点击登录按钮,然后到与数据库交互验证数据的整个过程涉及到的代码。
上图显示的页面看似html实际是采用ExtJS绘制的,ExtJS自成体系,编写完代码以后需要使用Sencha编译一下,然后生成类似于下面的目录结构:
ExtJS+SpringMVC+Spring+Hibernate的一种实现(蒋锋代码分析)
实际上在eclipse中最终项目的目录结构如下所示
ExtJS+SpringMVC+Spring+Hibernate的一种实现(蒋锋代码分析)
最初访问项目的页面是index.html
ExtJS+SpringMVC+Spring+Hibernate的一种实现(蒋锋代码分析)
如上图所示,是index.html的代码,其实也真的看不出什么来。但是如果你学习过ExtJS的基础知识,你就不会看不懂了,因为ExtJS的结构是固定的,简单地说就是app.js是所有一切的入口,所以我们直接去看app.js(从上面的目录结构的照片中,其实可以找到app.js)
ExtJS+SpringMVC+Spring+Hibernate的一种实现(蒋锋代码分析)
核心代码导入app.view.Main代码,这种说法比较粗略,依据ExtJS的基础知识,目录结构也是固定的,默认都是在app目录下。
ExtJS+SpringMVC+Spring+Hibernate的一种实现(蒋锋代码分析)
ExtJS+SpringMVC+Spring+Hibernate的一种实现(蒋锋代码分析)
ExtJS+SpringMVC+Spring+Hibernate的一种实现(蒋锋代码分析)
这个login.js文件就是现实整个页面的最主要的文件
我们看看其中的有关登录按钮的代码:

                        {
                            xtype : 'button',
                            name : 'loginButton',
                            scale : 'large',
                            height : 50,
                            iconAlign : 'right',
                            iconCls : 'x-fa fa-sign-in',
                            text : '登&nbsp;&nbsp;&nbsp;&nbsp;录&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;',
                            action : 'login',
                            listeners : {
                                click : 'onLoginButton'  //点击按钮触发这个事件
                            }
                        },

上述代码用于现实红框中的内容,重点在于listeners中的click事件,这个事件定义在哪里呢?
ExtJS+SpringMVC+Spring+Hibernate的一种实现(蒋锋代码分析)
ExtJS的规范是这样的,MVVC,如下图
ExtJS+SpringMVC+Spring+Hibernate的一种实现(蒋锋代码分析)
controller部分表示这个js页面的事件相关的controller,就是下面这个文件
ExtJS+SpringMVC+Spring+Hibernate的一种实现(蒋锋代码分析)
那么可以看出来事件定义在LoginController.js这个文件中,我们看看onLoginButton对应的代码:

//就是下面这个方法
 onLoginButton : function(button, e, eOpts){
          this.validate(false);
      },
//跳转到下面这个方法
      validate : function(invalidate){
          var me = this;
          var p_form = this.lookupReference('p_form').getForm();
          if (!p_form.isValid()) return;
          var userinfo = p_form.getValues();
          userinfo.invalidate = invalidate;
          userinfo.companyid = cfg.companyid;
          var url = "login/validate.do";
          EU.RS({
                url : url,
                scope : this,
                params : userinfo,
                callback : function(result){
                    var msg = "";
                    if (result.success) {
                        this.login(userinfo, result.data);
                        return
                    }
                    switch (result.data) {
                        case "1" :
                            msg = "请输入正确的验证码!";
                            break;
                        case "2" :
                            msg = "您所输入的用户名不存在!!";
                            break;
                        case "3" :
                            msg = "密码输入错误,请重新输入!";
                            break;
                        case "4" :
                            msg = "当前用户名已被锁定,无法登录!";
                            break;
                        case "5" :
                            msg = "当前用户名已被注销,无法登录!";
                            break;
                        case "6" :
                            msg = "当前用户所在公司已被注销,无法登录!";
                            break;
                        case "7" : {
                            EU.showMsg({
                                  title : '提示信息',
                                  message : "当前用户已经在线,确定强制登录吗?",
                                  option : 1,
                                  callback : function(rt){
                                      if (rt == 'yes') me.validate(true);
                                  }
                              });
                            break;
                        }
                        default :
                            msg = "提交失败, 可能存在网络故障或其他未知原因!";
                            break;
                    }
                    if (!Ext.isEmpty(msg)) EU.toastError(msg);
                }
            });
      },

上述代码是js语言环境下方法的定义,我们在login.js中调用了这个方法
最终在上面代码中调用EU.RS()方法,这个方法定义在另一个js文件中如下图:
ExtJS+SpringMVC+Spring+Hibernate的一种实现(蒋锋代码分析)
那么上述代码究竟起什么作用呢?
我们直接去看ExtUtils.js文件的部分代码,也就是RS()方法的代码:

        /**
         * 远程访问
         * @param {} config
         */
        RS : function(config) {
            var thiz = this;
            var params = Ext.isEmpty(config.params) ? {} : config.params;
            for (var key in params) {
                var data = params[key];
                if (Ext.isArray(data)) params[key] = CU.toString(data);
            }// 转换为spring @RequestList接受的转码格式
            config.params = CU.toParams(params);// 转换为spring mvc参数接受方式
            config.async = Ext.isEmpty(config.async) ? true : config.async; // true=异步
                                                                            // false=同步
            config.scope = config.scope || thiz;
            config.dataType = config.dataType || "json";
            config.timeout = config.timeout || 30000;
            config.method = 'POST';
            var msg = Ext.isEmpty(config.msg) ? config.async : config.msg;
            config.message = config.message || '正在访问服务器, 请稍候...';
            config.target = config.target || Ext.Viewport;
            var myMask = null;
            if (msg) {
                myMask = new Ext.LoadMask({
                    msg : config.message,
                    target : config.target,
                    removeMask : true
                }); // ,style:'background-color: rgba(208, 208, 208, 0.5);'
                myMask.show();
            }
            var caller_callback = config.callback;
            var caller_errorcallback = config.errorcallback;
            var datas = null;
                    //
            //定义变量:callback
            //变量类型:function
            //变量名: callback
            //变量值:函数的代码体
            var callback = function(type, scope, success, result, response, options) {
                if (msg) myMask.hide();
                if (success) {
                    datas = result;
                    if (Ext.isFunction(caller_callback)) {
                        Ext.callback(caller_callback, config.scope, [result]);
                    }
                } else {
                    if (response.status == "999" || response.status == "998") return;
                    if (Ext.isFunction(caller_errorcallback)) {
                        Ext.callback(caller_errorcallback, config.scope, [response, options]);
                    } else {
                        thiz.toastError("访问远程服务器失败!");
                    }
                }
            }
            //cfg.crossdomain  定义在     app\utils\Config.js中,默认值为false
            if (cfg.crossdomain) {
                config.url = cfg.requestUrl + config.url;
                config.callback = function(success, result, errortype) {
                    Ext.callback(callback, this, ['jsonp', config.scope, success, result])
                }
                Ext.data.JsonP.request(config);
            } else {
                //对config,也就是LoginController。js中callback变量的第二次赋值(重新赋值)
                config.callback = function(options, success, response) {
                    var text = response.responseText;
                    //调用                             
                    Ext.callback(callback, this, ['json', config.scope, success, CU.toObject(text), response, options])
                };
                //核心语句
                Ext.Ajax.request(config);
                
            }
            return datas;
        },

var callback = function(type, scope, success, result, response, options) {}这种形式的js代码实际上定义一个变量,这个变量的名字是callback,变量的内容是一个function,js的神奇之处。
先定义callback方法,然后后面代码中再调用这个方法。
下图是Ext.callback这个标准方法的说明
ExtJS+SpringMVC+Spring+Hibernate的一种实现(蒋锋代码分析)
上述代码有三部分内容:
1---完善config这个参数(定义了一个callback方法)
2---判断crossdomain,然后从新定义LoginController中callback方法
3---执行ExtJS标准方法Ext.Ajax.request();
第三部分才是实际执行
ExtJS+SpringMVC+Spring+Hibernate的一种实现(蒋锋代码分析)
这个标准方法的含义也很简单,就是向url中发送一个request
url定义在config里,之前已经定义过了。
1和2的工作其实都是在动态的定义一个自定义callback方法(注意,是自定义的callback而不是ExtJS系统标准callback)
也就是回到LoginController.js文件中
在Login.js文件中“登录”按钮触发的事件,很简单,核心就是触发ExtJS标准的Ext.Ajax.request()方法,也就是向url发送一个request。
url是什么?防止你眼瞎,我把代码再贴一遍:

validate : function(invalidate){
          var me = this;
          var p_form = this.lookupReference('p_form').getForm();
          if (!p_form.isValid()) return;
          var userinfo = p_form.getValues();
          userinfo.invalidate = invalidate;
          userinfo.companyid = cfg.companyid;
          var url = "login/validate.do";//这就是URL
          EU.RS({
                url : url,
                scope : this,
...................
}

好吧,接下来会发生什么呢?
接下来的事情就和ExtJS无关了,因为你发送了一个request。这个request会被tomcat中web项目里配置的SpringMVC拦截下来,哈哈哈,看着眼熟了吧?
依据url,SpringMVC中你自己手写的Controller部分的java文件就会起作用了。
"login/validate.do"
这个url其实就直接,(当然我也是去eclipse工程中慢慢瞪眼睛看出来的)如下图自己看吧:
ExtJS+SpringMVC+Spring+Hibernate的一种实现(蒋锋代码分析)
上述url经过controller的定位,也就是SpringMVC中HandlerMapping部分的定位,找到这个java文件进行处理,下面的工作就全都交给了SpringMVC和Spring。
上图所示的代码,也就是Spring中Controller的代码我们来列举一下:

//省略了import以及其他方法
package com.jhopesoft.platform.controller;

@Controller
@RequestMapping("/login")
public class Login {

  @Resource
  private LoginService service;

  @SystemLogs("用户登陆")
  @RequestMapping(value = "/validate")
  @ResponseBody
  public ResultBean validate(HttpServletRequest request, HttpServletResponse response, String companyid,
      String usercode, String password, Boolean invalidate) {
      
    ResultBean result = service.login(companyid, usercode, password, invalidate);
    if (!result.isSuccess()) return result;
    UserBean bean = login(request, companyid, usercode);
    result.setData(bean);
    return result; //注意返回值类型
    
  }
  }

上述代码和过去我自己写的Spring中的Controller的java代码区别就在于返回值,他的java代码中没有出现ModelAndView,按照web--service--Dao这个目录结构的代码,返回值类型是一个自定义的PO。过去我们是将PO放到ModelAndView中,然后跳转到JSP页面中再获取其中的数据。现在不是这样了,那么这种写法究竟什么意思呢?
涉及到两部分内容:
1---Spring代码如何获取数据库中数据的;
2---返回的PO如何被解析的;

1--案例代码从数据库获取数据的过程:
这一部分是经典的Spring写法,controller目录中保存SpringMVC的java代码,service目录保存业务逻辑代码,下图基本反映了java部分所有的代码:
ExtJS+SpringMVC+Spring+Hibernate的一种实现(蒋锋代码分析)
看到Dao部分只有一个java文件(一个interface一个class)
Entity文件夹中全是PO文件,由eclipse集成的Hiberante Tools生成
哇这就是java代码。这一部分在前面博文中有所介绍。

2--返回值PO如何被解析
蒋锋老师代码中是这个意思,我还没有验证啊:PO类型的返回值实际上是一个PO的实例,里面存放着数据。返回给SpringMVC以后,由SpringMVC解析成json文件,返回给这个方法的调用者,也就是最开始的login.js文件中的,返回方法,也就是callback方法。如下图所示:
ExtJS+SpringMVC+Spring+Hibernate的一种实现(蒋锋代码分析)
那么json文件是什么形态呢?一堆括号,一堆冒号,一堆字符串。
ExtJS+SpringMVC+Spring+Hibernate的一种实现(蒋锋代码分析)
我们现在关心的问题是,PO如何被解析成json文件的。
看下面的博文:
http://blog.csdn.net/fw0124/article/details/48280083?spm=5176.100239.blogcont291249.11.FqLHtx
歌词大意就是,通过maven导入依赖

<dependency>
  <groupId>org.codehaus.jackson</groupId>
  <artifactId>jackson-core-asl</artifactId>
  <version>1.9.13</version>
</dependency>
<dependency>
  <groupId>org.codehaus.jackson</groupId>
  <artifactId>jackson-mapper-asl</artifactId>
  <version>1.9.13</version>
</dependency>

然后就可以
另外两个参考文档如下:
http://www.cnblogs.com/fangjian0423/p/springMVC-xml-json-convert.html

http://my.oschina.net/lichhao/blog/172562

上一篇:全面理解面向对象的 JavaScript


下一篇:JAVA解析XML