文档章节

struts2和spring mvc,孰优孰劣?

杨尚川
 杨尚川
发布于 2015/04/19 17:40
字数 1465
阅读 11918
收藏 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 开发实战》—— 基于 Gradle + Kotlin 的企业级应用开发最佳实践 Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程。...

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

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

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

没有更多内容

加载失败,请刷新页面

加载更多

Mybatis Plus删除

/** @author beth @data 2019-10-17 00:30 */ @RunWith(SpringRunner.class) @SpringBootTest public class DeleteTest { @Autowired private UserInfoMapper userInfoMapper; /** 根据id删除......

一个yuanbeth
今天
4
0
总结

一、设计模式 简单工厂:一个简单而且比较杂的工厂,可以创建任何对象给你 复杂工厂:先创建一种基础类型的工厂接口,然后各自集成实现这个接口,但是每个工厂都是这个基础类的扩展分类,spr...

BobwithB
今天
5
0
java内存模型

前言 Java作为一种面向对象的,跨平台语言,其对象、内存等一直是比较难的知识点。而且很多概念的名称看起来又那么相似,很多人会傻傻分不清楚。比如本文我们要讨论的JVM内存结构、Java内存模...

ls_cherish
今天
4
0
友元函数强制转换

友元函数强制转换 p522

天王盖地虎626
昨天
5
0
js中实现页面跳转(返回前一页、后一页)

本文转载于:专业的前端网站➸js中实现页面跳转(返回前一页、后一页) 一:JS 重载页面,本地刷新,返回上一页 复制代码代码如下: <a href="javascript:history.go(-1)">返回上一页</a> <a h...

前端老手
昨天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部