struts2和spring mvc,孰优孰劣?

mip33 10年前

最近我在将APDPlat升级到Java8,发现最新版本的struts2不支持Java8,同时由于之前有很多的同学希望我把APDPlat的struts2替换为spring mvc,所以我就决定试试看。本文我们看两个转换前后的例子:

1、下拉列表服务,此类比较简单,只涉及一个方法store:

使用struts2:

@Scope("prototype")  @Controller  @Namespace("/dictionary")  public class DicAction extends ExtJSSimpleAction<Dic> {      @Resource      private DicService dicService;      private String dic;      private String tree;      private boolean justCode;            /**       *        * 此类用来提供下拉列表服务,主要有两种下列类型:       * 1、普通下拉选项       * 2、树形下拉选项       * @return 不需要返回值,直接给客户端写数据       *        */      public String store(){          Dic dictionary=dicService.getDic(dic);          if(dictionary==null){              LOG.info("没有找到数据词典 "+dic);              return null;          }          if("true".equals(tree)){              String json = dicService.toStoreJson(dictionary);              Struts2Utils.renderJson(json);          }else{              List<Map<String,String>> data=new ArrayList<>();              for(DicItem item : dictionary.getDicItems()){                  Map<String,String> map=new HashMap<>();                  if(justCode){                      map.put("value", item.getCode());                  }else{                      map.put("value", item.getId().toString());                  }                  map.put("text", item.getName());                  data.add(map);              }              Struts2Utils.renderJson(data);          }          return null;      }        public void setJustCode(boolean justCode) {          this.justCode = justCode;      }        public void setTree(String tree) {          this.tree = tree;      }        public void setDic(String dic) {          this.dic = dic;      }  }

使用spring mvc:

@Scope("prototype")  @Controller  @RequestMapping("/dictionary")  public class DicAction extends ExtJSSimpleAction<Dic> {      @Resource      private DicService dicService;            /**       *        * 此类用来提供下拉列表服务,主要有两种下拉类型:       * 1、普通下拉选项       * 2、树形下拉选项       * @param dic       * @param tree       * @param justCode       * @return 返回值直接给客户端       */      @ResponseBody      @RequestMapping("/dic!store.action")      public String store(@RequestParam(required=false) String dic,                          @RequestParam(required=false) String tree,                          @RequestParam(required=false) String justCode){          Dic dictionary=dicService.getDic(dic);          if(dictionary==null){              LOG.info("没有找到数据词典 "+dic);              return "";          }          if("true".equals(tree)){              String json = dicService.toStoreJson(dictionary);              return json;          }else{              List<Map<String,String>> data=new ArrayList<>();              dictionary.getDicItems().forEach(item -> {                  Map<String,String> itemMap=new HashMap<>();                  if("true".equals(justCode)){                      itemMap.put("value", item.getCode());                  }else{                      itemMap.put("value", item.getId().toString());                  }                  itemMap.put("text", item.getName());                  data.add(itemMap);              });              String json = JSONArray.fromObject(data).toString();              return json;          }      }  }


从上面我们可以看到,struts2和spring mvc的区别非常明显,struts2使用原型,spring mvc使用单例。

单例一定比原型快吗?创建一个对象的开销可以忽略吗?这个问题需要在自己的场景中考虑,不过大多时候我们是可以忽略的。

APDPlat之前使用struts2,每一个请求都会对应一个全新的Action,所以请求的参数就可以作为Action的字段来自动注入,言下之意就是Action中的所有方法都可以共用字段,而现在换成spring mvc了,不同的方法需要各自获取请求中的参数。

对比以上代码,我个人还是认为spring mvc的方式更好一些,对于Action(spring mvc叫Controller)来说,单例、无状态是比较理想的。


2、数据字典服务,此类比较复杂,涉及的方法有create、delete、updatePart、retrieve、query、store

使用struts2:

@Scope("prototype")  @Controller  @Namespace("/dictionary")  public class DicItemAction extends ExtJSSimpleAction<DicItem> {      @Resource      private DicService dicService;      private String node;        /**       * 返回数据字典目录树       * @return        */      public String store() {          if (node == null) {              return null;          }          Dic dic=null;          if(node.trim().startsWith("root")){              dic = dicService.getRootDic();          }else{              int id=Integer.parseInt(node);              dic = dicService.getDic(id);          }                    if (dic != null) {              String json = dicService.toJson(dic);              Struts2Utils.renderJson(json);          }          return null;      }        public void setNode(String node) {          this.node = node;      }  }

使用spring mvc:

@Scope("prototype")  @Controller  @RequestMapping("/dictionary")  public class DicItemAction extends ExtJSSimpleAction<DicItem> {      @Resource      private DicService dicService;        /**       * 返回数据字典目录树       * @param node       * @return        */      @ResponseBody      @RequestMapping("/dic-item!store.action")      public String store(@RequestParam(required=false) String node) {          if (node == null) {              return "[]";          }          Dic dic=null;          if(node.trim().startsWith("root")){              dic = dicService.getRootDic();          }else{              int id=Integer.parseInt(node);              dic = dicService.getDic(id);          }                    if (dic != null) {              String json = dicService.toJson(dic);              return json;          }          return "[]";      }      @ResponseBody      @RequestMapping("/dic-item!query.action")      public String query(@RequestParam(required=false) Integer start,                          @RequestParam(required=false) Integer limit,                          @RequestParam(required=false) String propertyCriteria,                          @RequestParam(required=false) String orderCriteria,                          @RequestParam(required=false) String queryString,                          @RequestParam(required=false) String search){          super.setStart(start);          super.setLimit(limit);          super.setPropertyCriteria(propertyCriteria);          super.setOrderCriteria(orderCriteria);          super.setQueryString(queryString);          super.setSearch("true".equals(search));          return super.query();      }      @ResponseBody      @RequestMapping("/dic-item!retrieve.action")      public String retrieve(@ModelAttribute DicItem model) {          super.model = model;          return super.retrieve();      }      @ResponseBody      @RequestMapping("/dic-item!delete.action")      public String delete(@RequestParam String ids) {          super.setIds(ids);          return super.delete();      }      @ResponseBody      @RequestMapping("/dic-item!create.action")      public String create(@ModelAttribute DicItem model) {          super.model = model;          return super.create();      }      @ResponseBody      @RequestMapping("/dic-item!updatePart.action")      public String updatePart(@ModelAttribute DicItem model) {          super.model = model;          return super.updatePart();      }  }

从上面可以看到,从struts2转换为spring mvc之后,代码一下子就增加了,父类的create、delete、updatePart、retrieve、query这5个方法对于spring mvc就无效了,而且模型注入的方式也不起作用了,下面我们要解决这两个问题。


要解决第一个问题,我们首先要改变struts2的URL调用方式,在struts2中,我们是这么调用Action的方法的,!后面是Action的方法名称:

http://localhost:8080/APDPlat_Web-2.6/dictionary/dic-item!query.action

如果我们不改变调用方式,上面刚说的那5个方法就无法抽象到父类中了,改变方式也挺简单,只需要把!改成/就可以了,在父类中增加如下代码并在前端JS中将!改成/:

@ResponseBody  @RequestMapping("query.action")  public String query(@RequestParam(required=false) Integer start,                      @RequestParam(required=false) Integer limit,                      @RequestParam(required=false) String propertyCriteria,                      @RequestParam(required=false) String orderCriteria,                      @RequestParam(required=false) String queryString,                      @RequestParam(required=false) String search){      super.setStart(start);      super.setLimit(limit);      super.setPropertyCriteria(propertyCriteria);      super.setOrderCriteria(orderCriteria);      super.setQueryString(queryString);      setSearch("true".equals(search));      return query();  }    @ResponseBody  @RequestMapping("retrieve.action")  public String retrieve(@ModelAttribute T model) {      this.model = model;      return retrieve();  }    @ResponseBody  @RequestMapping("delete.action")  public String delete(@RequestParam String ids) {      super.setIds(ids);      return delete();  }    @ResponseBody  @RequestMapping("create.action")  public String create(@ModelAttribute T model) {      this.model = model;      return create();  }    @ResponseBody  @RequestMapping("updatePart.action")  public String updatePart(@ModelAttribute T model) {      this.model = model;      return updatePart();  }


关于第二个问题,在struts2中,注入Action的参数,要使用model.id这样的方式,model是Action的一个字段,而在spring mvc中,这样是不行的,需要做一个转换,在父类中增加如下代码以使spring mvc能适应struts2参数注入方式:

/**   * 前端向后端传递模型参数的时候都有model.前缀   * @param binder   */  @InitBinder  public void initBinder(WebDataBinder binder) {      binder.setFieldDefaultPrefix("model.");  }


经过上面的改进,数据字典服务 使用spring mvc的代码升级为:

@Scope("prototype")  @Controller  @RequestMapping("/dictionary/dic-item/")  public class DicItemAction extends ExtJSSimpleAction<DicItem> {      @Resource      private DicService dicService;        /**       * 返回数据字典目录树       * @param node       * @return        */      @ResponseBody      @RequestMapping("store.action")      public String store(@RequestParam(required=false) String node) {          if (node == null) {              return "[]";          }          Dic dic=null;          if(node.trim().startsWith("root")){              dic = dicService.getRootDic();          }else{              int id=Integer.parseInt(node);              dic = dicService.getDic(id);          }                    if (dic != null) {              String json = dicService.toJson(dic);              return json;          }          return "[]";      }  }

来自:http://my.oschina.net/apdplat/blog/403561