Struts2、Spring、Hibernate整合ExtJS


Struts2、Spring、Hibernate整合ExtJS
2011年01月07日
  大发现,大家在贴代码的时候。系统会自动加上一些代码或注释之类的东西,像script或style会被加上其他字符。并且有2份相同的代码,这时你可以把在线编辑器中的textarea中的代码删掉。重新贴上去就可以了,注意是重新贴上上去,不是重新插入!!这样就没有问题了,呵呵~~~
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
   SSHExtTree 百度文库提供下载:http://wenku.baidu.com/view/485e4d36f111f18583d05a ed.html
  开标题就知道是Struts、Spring、Hibernate、ExtJS的Tree实例文档,其中包括Filter功能的checkNodeTree、comboBoxCheckNodeTree、comboBoxTree这三种扩展的tree。不错,如果你不了解Struts、Spring、Hibernate、ExtJS,也不要紧。跟着我做,做出这个例子应该木有问题。
  生产环境:
  System:Windows
  WebBrowser:IE6+、Firefox3+
  JavaEE Server:tomcat5.0.2.8、tomcat6
  IDE:eclipse、MyEclipse6.1+
  Database:Ms SQLServer2000、2005
  开发依赖库版本:
  ExtJS:lib 2.1
  Struts: 2.1.4
  Hibernate:3.2
  Spring:2.0
  Email:[email protected]
  Blog:http://blog.csdn.net/IBM_hoojo
   http://hoojo.cnblogs.com/
  
如果你具备上面的环境后,下面就可以跟着我一步步的完成这个SSHExtTree的示例。
  1、 添加Hibernate支持,这里我用MyEclipse的工具直接添加jar包和链接数据库的配置文件,这一步如果你熟悉可以跳过。
  A、 首先你选择你的MyEclipse中的MyEclipse Database Explorer这个视图
  
  B、 进入视图后,点击new
  
  C、 进入new视图后,你就可以添加你的DB的链接数据库的种类、端口、地址、数据库名称
  我上面选择的是MsSQL2005
  Connect URL:jdbc:jtds:sqlserver://localhost:1433/jc2009_gdszz
  jc2009_gdszz是数据库的名称,是建科院的数据库
  用户名、密码就是你链接数据库的密码
  注意的是你还要添加你的链接数据库的driver,我这里用jdts驱动
  上面的完成后,你可以点击Test Driver就可以测试你的链接对不对。如果对的话就会出现上面的successfully!
  D、 继续Next你可以选择第三个单选按钮,就是下面的1。然后点击Add按钮选择你的数据库。这样后面链接数据库的时候就不会出现其他的数据库了;点击Finish
  
  E、 下面开始链接数据库
  
  上面的sql_2005就是我们刚才建的数据库链接了。右键sql_2005,然后点击Open connection,就可以链接到你的数据库了
  F、 然后展开数据库,选择dbo展开,选择table。你就看到你的数据库中的表了
  至此数据链接创建成功,下面我们添加Hibernate的支持。
  G、 下面切换到MyEclipse JavaEE视图,如果你喜欢Java视图也可以。这里用MyEclipse JavaEE视图。
  
  然后右键点击你建好的SSHExtTree这个WebProject
  
  选择MyEclipse,选择Add Hibernate
  H、进入添加Hibernate支持的视图后,选择Hibernate3.2;然后记得选择添加jar包到你的工程lib中
  
  I、 继续Next,选择New。我喜欢单独的Hibernate配置文件,如果你不喜欢你可以在Spring中添加配置。
  
  J、 点击Next,进入选择数据库链接的视图,当然是选择我们刚才创建的sql_2005这个视图了。
  
  记得选择你的数据库方言,数据库方言对于不同的数据库是不同的。
  K、 点击Next你就可以看到创建SessionFactory,我们用Spring就不要这个了。点击Finish;就可以看到配置文件hibernate.cfg.xml
  我们还得添加2条:
  true
  true
  这个在调试程序的时候有用,可以格式化输入sql语句
  综上所述,上面的主要完成的就是添加Hibernate的jar包和数据库链接配置文件。你也可以手动添加Hibernate的数据库配置文件和jar包。那样就不需要上面的步骤。
  2、 下面我们添加Spring的支持,请跟着我做
  A、 右键SSHExtTree项目,选择MyEclipse然后选择Add Spring
  
  B、 点击后你可以看到Spring的支持了,Spring选择2.0的版本。然后就是选择你要的jar包。
  
  C、 我们选择完后,继续下一步Next
  
  D、 继续下一步Next
  
  上面的SessionFactory Bean id就是你的applicationContext.xml这个文件中的SessionFactory的bean的id。在后面我们需要为其他使用Hibernate模版的文件件注入sessionFactory。至此Spring的支持添加完毕,现在我们需要配置Spring的Aop管理我们的事务。
  E、 首先修改applicationContext.xml为applicationContext-common.xml,然后我们添加aop、tx的命名空间。你也可以手动添加,如果你不记得Sping的aop、tx的命名空间,请跟着我做。
  F、 右键我们的Project,选择New然后选择xml basic template
  
  G、 进入页面后,直接next。选择第二项
  
  H、进入后,看下面的选择schema文件
  
  先选择aop2.0,点击next
  I、 进入视图后你可以看到
  
  选择p,点击Edit 也就是1处。然后将改成aop。然后点击Add 
  J、 进入视图后,你就可以继续选择tx这个命名空间的schame
  
  K、 点击ok后,你可以选择刚才的tx。然后点击edit。为它添加xsd
  
  将ns name复制粘贴到Location Hint中,然后在后面添加/spring-tx-2.0.xsd
  完了后,点击Finish。
  然后copy里面的内容到刚才的applicationContext-common.xml中,内容如下:
  xmlns:aop="http://www.springframework.org/schema/a op"
  xmlns:tx="http://www.springframework.org/schema/tx "
  xsi:schemaLocation="http://www.springframework.org /schema/aop 
  http://www.springframework.org/schema/aop/spring-a op-2.0.xsd 
  http://www.springframework.org/schema/tx 
  http://www.springframework.org/schema/tx/spring-tx -2.0.xsd ">
  刚才这么多步骤就是要这些文件的内容
  最后common文件的的头部就是这样的内容:   L、 下面配置事务的传播特性
  Hibernate的事务管理机制  上面的配置注入了sessionFactory,property中的name是HibernateTransactionManager中的setSessionFactory这个setter方法,后面的ref是上面的bean的引用。
  事务的传播特性  配置哪些方法参与事务的管理,add*代码addXXX开头的方法,后面的propagation是事务的传播特性、级别,最后的*代表所有方法,对于查询的方法read-only只读就可以了。查询不需要参与事务。
  配置在哪里启用事务,哪些包或类、方法参与事务管理:  上面的pointcut称作切面,expression是表达式(关于表达式更多内容可以参考spring官方文档)
  execution(* com.hhh.biz.impl.*.*(..))前面的execution是固定的,括号里面的内容是
  第一个*代表任意返回值,com.hhh.biz.impl代表包。在这一层的包被纳入到事务的管理,impl.*代表impl下面的所有的class,impl.*.*代表class中所有方法,impl.*.*(..)代表的是方法的任意个参数。这个表达的是事务管理到com.hhh.biz.impl下所有类的方法的任意返回值、任意参数
  advice-ref是上面配置的事务的传播特性,pointcut-ref是上面的切面配置至此关键的配置就完毕了。
  综上所述,上面的主要是完成用IDE添加Spring的jar包和配置文件,如果你不想用上面的方式,也可以手动添加jar包。然后将xml配置文件的的头部、命名空间等信息贴到建好的xml中也是可行的。
  3、 下面我们将手动添加Struts2.x的jar和配置Struts2.x的运行环境
  A、 你需要添加以下jar包
  commons-logging-api-1[1].1.1.jar
  freemarker-2[1].3.10.jar
  ognl-2[1].6.11.jar
  struts2-core-2.0.11.jar
  xwork-2[1].0.2.jar
  由于和Spring整合,这里还有添加strtus和spring的插件包struts2-spring-plugin-2.0.14.jar
  B、 下面开始配置struts的核心控制器
  在web.xml中添加如下配置:  这样struts2的web.xml配置就完成了
  C、 下面开始添加struts的Action配置文件,这个文件的名称就是struts.xml。不要随便更改,这个是默认的名称。如果你想用其他的名称,请在web.xml中的核心控制器中配置param参数设置配置文件的名称。
  内容如下:  这样struts的配置就完成了
  4、 整合ssh框架,首先还是web.xml配置。
  Spring提供了编码过滤器,我们在web.xml添加编码过滤器的配置。  配置加载Spring配置的路径  配置加载Spring上下文的的监听器  至此配置基本完成,下面发布到tomcat中。看看是否有误错误,没有错误请求index.jsp看看。成功了就继续;
  请求发现出现了java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderListe ner这个异常信息,发现在添加jar包的时候没有把spring-web这个选中。那就手动添加spring-web.jar
  这个jar包就ok了。启动发现没有错误,范围index.jsp也成功了。
  5、 下面通过用MyEclipse Database Explorer生成JavaBeans、hbm.xml配置文件
  我们这里写Tree,只需用一张Tree的表就可以了。
  首先看下这张表的结构,代码如下:
  
  NodeId是主键,PID引用NodeID,PID是应用的父节点。也就是说当前数据的PID指向的数据的id就是它的父元素。
  假如要查NodeID=1的子元素,那就差些PID=1的就是它的子元素的。
  A、右键击sort表格,选择reverse
  
  这里就选sort这个表,然后右键。选择Hibernate Reverse Engineering就可以生产JavaBeans、hbm.xml映射文件了。
  B、选择Browser,选择package
  
  C、选择create POJO DBTable,然后点击Finish
  
  文件就生成了
  6、 编写ExtJS的Tree需要的树形结构的JavaBeans和完成Tree数据查询接口和实现代码。
  Tree JavaBeans  public class Tree { private String id; private String text;//固定格式
   private boolean leaf;//固定格式
   private Integer nodelevel;
   private String nodeId;
   private String pid;
  //private String uiProvider = "col";
   private List children = new ArrayList();//固定格式
   //setter/getter
  
public Tree(){
  }
   public Tree(String text, String id, String nodeId, String pid, Integer nodelevel) {
   this.id = id;
   this.text = text;
   this.pid = pid;
   this.nodeId = nodeId;
   this.nodelevel = nodelevel;
   if (nodelevel  implements TreeDao { private BaseDao dao; public void setDao(BaseDao dao) { this.dao = dao; } public List getTreeListByParentId(T entity) throws Exception { String hql = "select new com.hhh.entity.Tree(categoryname, id, nodeId, pid, nodelevel) from SsSort where isshow = 1"; if (entity == null || entity.getNodeId() == null || "".equals(entity.getNodeId())) { hql += " and pid is null"; } else { hql += " and pid = '" + entity.getNodeId() + "'"; } return dao.getList(hql); } } 上面的hql查询语句是将SsSort(Ss_sort table)这个对象的查询到的数据封装在Tree这个对象中。
  现在看看服务层biz接口和实现  public interface TreeBiz { /** * 通过sort对象去查询子结点list * @createDate 2010-8-10 下午02:51:55 * @param  操作对象类型
  * @param entity 查询数据封装条件
  * @return 返回sort对象所有的list集合
  * @throws Exception
  */
   public List getTreeListByParentId(T entity) throws Exception;
  /**
  * 通过entity对象查询子对象进行递归变量查找
  * @createDate 2010-8-11 下午01:04:02
  * @param  查询对象类型
  * @param entity 查询对象
  * @throws Exception
  */
   public void getTreeByParentId2Recursion(T entity) throws Exception;
  } 看看实现的代码:  package com.hhh.biz.impl; import java.util.List; import com.hhh.biz.TreeBiz; import com.hhh.dao.TreeDao; import com.hhh.entity.Tree; public class TreeBizImpl implements TreeBiz { private TreeDao dao; public void setDao(TreeDao dao) { this.dao = dao; } public List getTreeListByParentId(T entity) throws Exception { return dao.getTreeListByParentId(entity); } /** * @see com.hhh.biz.TreeBiz#getTreeByParentId2Recursion(co m.hhh.entity.Tree) * 这是一个递归查询,如果你一下看不明白递归的代码,可以参考getSortByParentId2Leaf方法
  * 然后将循环中相同的代码改造成递归即可
  * @createDate Dec 25, 2010 7:07:26 PM
  */
  @SuppressWarnings("unchecked")
   public void getTreeByParentId2Recursion(T entity) throws Exception {
  List child = dao.getTreeListByParentId(entity);
   if (child.size() > 0) {
   for (T s : child) {
   this.getTreeByParentId2Recursion(s);
  }
  entity.setChildren((List)child);
  } else {
  entity.setLeaf( true);
  }
  }
   public void getTreeByParentId2Leaf(T entity) throws Exception {
  List child = dao.getTreeListByParentId(entity);
   if (child.size() > 0) {
   for (T s : child) {
  List child2 = dao.getTreeListByParentId(s);
   if (child2.size() > 0) {
   for (T s2 : child2) {
  List child3 = dao.getTreeListByParentId(s2);
   if (child3.size() > 0) {
   for (T s3 : child3) {
  List child4 = dao.getTreeListByParentId(s3);
   if (child4.size() == 0) {
  s3.setLeaf( true);
  } else {
  s3.setLeaf( false);
  }
  s2.getChildren().add(s3);
  }
  } else {
  s2.setLeaf( true);
  }
  s.getChildren().add(s2);
  }
  } else {
  s.setLeaf( true);
  }
  entity.getChildren().add(s);
  }
  } else {
  entity.setLeaf( true);
  }
  }
  } 上的查询getTreeByParentId2Recursion用到了递归方法,如果你对递归的代码不是很明白就看看下面的查询getTreeByParentId2Leaf代码
  7、 下面编写Action代码,action中需要用到json-lib这个jar包,这个jar的主要作用就是将Java对象序列化成json格式的字符串对象。
  需要添加如下jar包:
  ezmorph-1.0.3.jar
  json-lib-2.3-jdk15.jar
  commons-lang.jar
  commons-collections-3.2.jar
  commons-beanutils.jar  public class TreeAction extends ActionSupport { private static final longserialVersionUID = -3795544890686836966L; private TreeBiz biz; private String data; private Tree tree = new Tree(); public void setBiz(TreeBiz biz) { this.biz = biz; } @Override public String execute() throws Exception { biz.getTreeByParentId2Recursion(tree); data = JSONArray.fromObject(tree.getChildren()).toString(); returnSUCCESS; } } 上面的代码很简单,看看action中的execute代码。JSONArray.fromObject这个主要就是将一个数值序列化成json的字符串。
  看看xml配置文件的配置:  首先看看struts配置文件中的constant元素,它是设置struts的国际化编码的,然后就是package这个包,它相当于Java类在package是区分配置的空间,其实package还有一个属性就是namespace命名空间。后面的extends是继承struts-default的默认配置。
  action就是配置的class Action的配置,上面的Action就通过这里的配置。name就是我们访问action的路径,class就是Action的classpath。这里用了spring的继承,所有class和spring配置的bean的id或name对应的。result是Action跳转的视图配置。
  Spring的beans的配置:             Spring的action的配置:  这里是id=treeAction就是和上面struts配置文件中的action配置的class对应的。
  这里这样配置能够起作用是struts2-spring-plugin-2.0.14.jar这个jar包
  8、 发布启动当前工程发现有如下错误:
  org.hibernate.MappingException: Association references unmapped class: com.hhh.entity.SsSortSubject
  我们删掉SsSort.hbm.xml这个配置文件中的
  
  
  
  
  
  
  我们不需要这个配置
  如果出现这个java.lang.NoClassDefFoundError: org/objectweb/asm/CodeVisitor异常就删掉cglib这个jar包,这是cglib中需要asm这个依赖。这里我们不要cglib。
  如果发布成功,启动没有错误。然后在浏览器中输入:http://localhost:8080/ExtTree/tree.action
  看看是否输出json格式的字符串。如果输出是正常的内容,没有异常。那么后台的代码就编写成功。
  9、 下面开始编写客户端的代码,添加ext2的js库。
  编写客户端tree的代码:  Ext.namespace("Ext.hhh"); /*++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++ + 系统项目树 + + create date: 2010-12-26 + ++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++*/ Ext.hhh.SystemTree = Ext.extend(Ext.tree.TreePanel, { constructor: function () { this.hiddenPkgs = []; Ext.hhh.SystemTree.superclass.constructor.call(thi s, { title: "项目菜单",
  //xtype: "treepanel",
  //singleExpand: true, //只展开一个子结点,展开另一个就隐藏其他
  renderTo: Ext.fly("showTree"),
  iframe: true,
  height: 600,
  width: 300,
  lines: false, 
  useArrows: false,
  autoScroll: true,
  rootVisible: false,
  maskDisabled: false, 
  checkModel: "multiple", 
  onlyLeafCheckable: false, 
  loader: new Ext.tree.TreeLoader({
  dataUrl: Ext.hhh.SystemTree.TREE_DATA_URL,
  baseAttrs: { 
  uiProvider: Ext.ux.TreeCheckNodeUI //添加 uiProvider 属性
  },
  clearOnLoad: false
  /*baseParams: {
  "sort.nodeId": "100"
  },*/
  /*uiProviders: {
  "col": Ext.ux.TreeCheckNodeUI //需要在后台传递数据的时候,需要添加一个uiProviders属性,值为col;
  }*/
  }),
  root: new Ext.tree.AsyncTreeNode({
  expanded: true,
  text: "系统项目", 
  id: "100"
  }),
  listeners: {
  click: function(node) {
  //Ext.Msg.alert("Navigation Tree Click", "You clicked: " + node.attributes.nodeId + "," + node.text + ",");
  window.frames["content"].document.body.innerHTML = "name:" + node.text + ", nodeId:" + node.attributes.nodeId;
  },
  check: function(node, checked){
  //alert(node.text+" = "+ checked);//注册"check"事件
  window.frames["content"].document.body.innerHTML = "name:" + node.text + ", nodeId:" + node.attributes.nodeId;
  } 
  }
  }
  }); 上面就是一颗树,带点击和选中事件的tree。我们还可以给它额外添加些辅助功能,让它可以查询树节点,点击展开、收缩、刷新等功能。
  首先添加这些工具条,toolbar  tbar: [" ", { xtype: "textfield", enableKeyEvents: true,//启用键盘事件,默认是不开启的
  emptyText: "请输入查询内容",
  width: 205
  }, {
  tooltip: "展开所有",
  iconCls: "expandIcon",
  handler: this.onExpendAllNode,
  scope: this//设置scope后,this指向MainViewport,不设置默认指向当前对象
  }, "-", {
  tooltip: "收缩所有",
  iconCls: "collaspeIcon",
  handler: this.onCollspseAllNode,
  scope: this
  }, "-", {
  tooltip: "刷新",
  iconCls: "refreshTree",
  handler: this.onRefreshHandler,
  scope: this
  }] 完善工具条上面的handler事件的方法  onExpendAllNode: function () { this.onNodeExpandAll(); }, onCollspseAllNode: function () { this.onNodeCollapseAll(); }, onRefreshHandler: function () { this.onRefreshSystemTree(); }, onNodeExpandAll: function () { this.body.mask('Loading...', 'x-mask-loading'); var t = this; this.root.expand(true, true, function () { (function () { t.body.unmask(); }).defer(200); }); }, onNodeCollapseAll: function () { this.root.collapse(true); }, onRefreshSystemTree: function () { this.body.mask('Loading...', 'x-mask-loading'); var tb = this.body; var t = this; this.onNodeCollapseAll(); this.root.reload(function () { tb.unmask(); t.root.expand(true); }); /*this.root.reload(); setTimeout(function () { tb.unmask(); t.onNodeExpandAll(); }, 1000);*/ } 看看tree的入口函数和导入的js文件       "> My JSP 'index.jsp' starting page         --%>    
  
  
  
  
  Ext.onReady(function () {
  Ext.BLANK_IMAGE_URL = "ext2/resources/images/default/s.gif";
  Ext.QuickTips.init();
  Ext.form.Field.prototype.msgTarget = "side";
  new Ext.hhh.SystemTree();
  });
  
  
  
  
  
  
   启动项目,在浏览器中请求: 可以看到如下界面:
  
  在查询的地方输入检测,就可以查询到相关带检测的项目:
  
  点击一个节点就可以得到它的id或text
  
   下拉列表选择节点树
  
效果图:
  有了上面写的tree,现在写comboboxCheckNodeTree。需要用到扩展的comboBox的js文件。Ext.ux.ComboBoxCheckTree.js这个是插件,进扩展的comboBox的js库。直接引入即可。下面编写comboBox的js:  /** * @function 选择树节点combobox
  * @createDate: Sep 5, 2010 3:22:35 PM * @class Ext.hoo.tree.ComboBoxCheckNodeTree * @extends Ext.ux.ComboBoxCheckTree */ Ext.ns("Ext.hoo.tree"); Ext.hoo.tree.ComboBoxCheckNodeTree = Ext.extend(Ext.ux.ComboBoxCheckTree, { constructor: function () { Ext.hoo.tree.ComboBoxCheckNodeTree.superclass.cons tructor.call( this, { renderTo: Ext.get('show'), //width: 300, height: 150, // define which node's value will be submited //all 所有节点
  //folder 父节点
  //leaf 叶子节点
  //selectValueModel: 'leaf', 默认叶子节点
  tree: {
  xtype: 'treepanel',
  height: 100,
  checkModel: 'cascade', //cascade 级联选择模式
  onlyLeafCheckable: false,//可以选择所有的节点,true叶子
  animate: true,
  rootVisible: true,
  autoScroll: true,
  loader: new Ext.tree.TreeLoader({
  dataUrl: Ext.hoo.tree.ComboBoxCheckNodeTree.TREE_DATA_URL,
  baseAttrs: { 
  uiProvider: Ext.ux.TreeCheckNodeUI//Ext.tree.TreeNodeUI 
  } 
  }),
  root: new Ext.tree.AsyncTreeNode({
  expanded: true,
  text: "系统项目", 
  id: "100"
  })
  }
  });
  //this.on("change", this.onFieldChange, this);
   this.on("blur", this.onFieldChange, this);
  },
  onFieldChange: function (field) {
  alert(field.getValue() + "###" + this.getValue() + "#######" + this.getRawValue());
  }
  });
  Ext.hoo.tree.ComboBoxCheckNodeTree.TREE_DATA_URL = "tree.action";
  Ext.onReady( function () {
  Ext.BLANK_IMAGE_URL = "ext2/resources/images/default/s.gif";
   new Ext.hoo.tree.ComboBoxCheckNodeTree();
  }); 代码比较简单,上面常用的属性也作注释说明。这个comboBoxTree可以用作多选比较好,当然单选也可以。
   下拉列表树
  
效果图:
  不带checkbox的tree,一般用于单选模式。看代码:  /** * @function 带checkbox的tree的combobox
  * @createDate: Sep 5, 2010 3:22:35 PM * @class Ext.hoo.tree.ComboBoTree * @extends Ext.ux.ComboBoxCheckTree */ Ext.ns("Ext.hoo.tree"); Ext.hoo.tree.ComboBoTree = Ext.extend(Ext.ux.ComboBoxTree, { constructor: function () { Ext.hoo.tree.ComboBoTree.superclass.constructor.ca ll( this, { renderTo: Ext.get('basicTree'), //width: 300, height: 150, //all:所有结点都可选中
  //exceptRoot:除根结点,其它结点都可选(默认)
  //folder:只有目录(非叶子和非根结点)可选
  //leaf:只有叶子结点可选
  selectNodeModel: "leaf",
  //hiddenId: "comboHideId",
  hiddenName: "comboHideName",
  tree: {
  xtype: 'treepanel',
  height: 100,
  animate: true,
  rootVisible: true,
  autoScroll: true,
  loader: new Ext.tree.TreeLoader({
  dataUrl: Ext.hoo.tree.ComboBoTree.TREE_DATA_URL
  }),
  root: new Ext.tree.AsyncTreeNode({
  expanded: true,
  text: "系统项目", 
  id: "100"
  })
  },
  listeners: {
  select: function (field) {
  alert(field.getValue() + "###" + this.getValue() + "#######" + this.getRawValue() + "###" + this.getNode().text);
  }
  }
  });
  }
  });
  Ext.hoo.tree.ComboBoTree.TREE_DATA_URL = "tree.action";
  Ext.onReady( function () {
  Ext.BLANK_IMAGE_URL = "ext2/resources/images/default/s.gif";
   new Ext.hoo.tree.ComboBoTree();
  });

猜你喜欢

转载自rzz85rzz.iteye.com/blog/1361860