文档章节

struts2和spring mvc,孰优孰劣?

杨尚川
 杨尚川
发布于 2015/04/19 17:40
字数 1465
阅读 11916
收藏 115

最近我在将APDPlat升级到Java8,由于之前有很多的同学希望我把APDPlat的struts2替换为spring mvc,所以我就决定试试看。

本次我把APDPlat的struts2改造为spring mvc的目标是:99.99%不改动JS、HTML、JSP等前端代码,只改JAVA代码!所以大家要先理解我的目标,然后再来看我的做法。

本文我们看两个转换前后的例子:

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 "[]";
    }
}


© 著作权归作者所有

杨尚川

杨尚川

粉丝 1103
博文 220
码字总数 1624053
作品 12
东城
架构师
私信 提问
加载中

评论(58)

护士的小黄瓜
护士的小黄瓜
springmvc推荐的市注解的方式,第一搞得代码乱,一个方法上面写那么多注解,也许是我没习惯注解的原因。但是如果要做细粒度的权限控制的话,假设有100个类,每个类里面有5个方法,那就需要都打开这100个类,并把每个类里面的方法映射的uri给记录下来,然后在做控制,想想都够蛋疼的。
limaofeng
limaofeng
恰恰相反,如果struts2没有标签我就觉得它没有优势了。如果不用标签或者项目是一个完全的数据服务接口
鸭子不会叫
鸭子不会叫

引用来自“李文军”的评论

说实话,spring mvc比struts已经前进了一步,但是我试用了下,发现还是挺繁琐的,首先配置太多,没有一个开启即用的默认标准配置,再次在功能上很多也不简洁实用
可以考虑用Spring Boot
鸭子不会叫
鸭子不会叫
可以把@Controller换成@RestController,不是可以省去@ResponseBody么?
s
s3051024
http://struts.apache.org/docs/java-8-support-plugin.html

其實已經支援了java 8了
依旧安然
真没看出来Spring MVC 和Struts2 明显的优异
两者都差不多 其实思路都是一样的
上面说的Spring抽出create、delete、updatePart、retrieve、query方法
Struts2也是这样啊 注解方式 一个类里面也可以映射多个请求
杨尚川
杨尚川 博主

引用来自“YANG_YAWEI”的评论

struts2 把请求参数映射到 action 的属性 ,这个绝对要比 springmvc 要方便得多,当有很多请求参数的时候,springmvc 最好的方式是用一个对象把这些参数都作为对象的属性,也就变相和 struts1 的 ActionForm 差不多了
其实我还是很希望 struts 能再出新版本 , 把 2 的 bug 解决掉 , 同时也能更好的支持 rest。springmvc 也并没有简化多少,配置一样很多很繁琐
假设我要调用一个类的10个方法,用struts2我能简洁到什么程度呢?我只需要在类上面加一个注解@Namespace("/dictionary")即可。但是如果我用spring mvc,我不但需要在类上面加一个注解@RequestMapping("/dictionary/dic/"),还必须为10个方法都加注解@RequestMapping("store.action")...。而且,如果方法增加到20个,struts2不用做任何新的注解,spring mvc却需要增加10个新的注解。是我不会用spring mvc还是它真的这么不智能?struts2的类注解中比spring mvc少了/dic是因为,struts2能从类名中获得这个值。
YANG_YAWEI
YANG_YAWEI
struts2 把请求参数映射到 action 的属性 ,这个绝对要比 springmvc 要方便得多,当有很多请求参数的时候,springmvc 最好的方式是用一个对象把这些参数都作为对象的属性,也就变相和 struts1 的 ActionForm 差不多了
其实我还是很希望 struts 能再出新版本 , 把 2 的 bug 解决掉 , 同时也能更好的支持 rest。springmvc 也并没有简化多少,配置一样很多很繁琐
苦思冥想
苦思冥想
797979
杨尚川
杨尚川 博主

引用来自“大飞哥2099”的评论

就这样了? 比较结果呢?
结果是我从struts2迁移到spring mvc啊,文章一开始我就说了
【Spring Boot 实战开发】第3讲 Kotlin扩展函数

Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程。在 Java 开发领域的诸多著名框架:Spring 框架及其衍生框架、做缓存Redis、消息...

程序员诗人
2018/04/20
0
0
【Spring Boot 实战开发】第2讲 Kotlin类型系统与空安全

Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程。在 Java 开发领域的诸多著名框架:Spring 框架及其衍生框架、做缓存Redis、消息...

程序员诗人
2018/04/19
0
0
【Spring Boot 开发实战】第2讲 Kotlin类型系统与空安全

Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程。在 Java 开发领域的诸多著名框架:Spring 框架及其衍生框架、做缓存Redis、消息...

程序员诗人
2018/04/19
0
0
【Spring Boot 实战开发】第1讲 Kotlin 的极简特性之:隐式类型与函数式编程

《Spring Boot 2.0极简教程》—— 基于 Gradle + Kotlin 的企业级应用开发最佳实践 Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过...

程序员诗人
2018/04/18
0
0
【Spring Boot 开发实战】第1讲 Kotlin 的极简特性之:隐式类型与函数式编程

《Spring Boot 开发实战》—— 基于 Gradle + Kotlin 的企业级应用开发最佳实践 Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程。...

程序员诗人
2018/04/18
0
0

没有更多内容

加载失败,请刷新页面

加载更多

实现线程类的两种方式

一、让目标类继承Thread类 package com.atzhongruan.springboot_boostrap.Test;/** * @Author jose * date 2019 */public class Task1 extends Thread{ @Override pub......

zhengzhixiang
26分钟前
3
0
OSChina 周日乱弹 —— 然而并不能,他是公的。

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @小小编辑推荐:《兔子姑娘》- 陈老实 《兔子姑娘》- 陈老实 手机党少年们想听歌,请使劲儿戳(这里) @曼尼22 :我倒要看看我头发啥时候掉完...

小小编辑
31分钟前
28
4
QML学习之浅谈Window

转载地址:http://blog.csdn.net/kanchuan1905/article/details/53762788 在Qt Quick的世界里,Window对象用于创建一个与操作系统相关的顶层窗口,包含了如Text, Rectangle, Image等元素。W...

shzwork
55分钟前
6
0
centos 查看删除旧内核

1、查看系统中安装的内核 $ yum list installed | grep kernel 2、删除系统中旧内核 $ yum install yum-utils$ package-cleanup --oldkernels --count=2...

编程老陆
今天
10
0
ES6

ES6:不改变原理的基础上,让API变得更简单 一、let:代替var用于声明变量 1、var的缺点: (1)声明提前 (2)没有块级作用域 2、let的优点: (1)组织了申明提前 (2)让let所在的块({}),...

wytao1995
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部