Skip to content
xautlx edited this page Aug 15, 2013 · 2 revisions

SpringMVC vs Struts

Struts1已经算是过去时了,因此在此讨论只涉及到Struts2。有关SpringMVC和Struts2对比的文章可以说在网上一大把,各位有兴趣直接搜索引擎即可。

对此探讨我的观点很简单:没有最好,适合最好,存在即是合理。

其实自己作为一个Spring Framework的追随者,我也尝试在整个架构上面以Spring一统天下来降低技术整合复杂度。 结合自己以往大部分Struts2和少部分SpringMVC技术项目的经验,并也尝试过基于SpringMVC做一个简单的POC框架原型,考虑此框架主要面向企业应用开发,最后还是选择了Struts2,一句话理由总结:SpringMVC更适合Internet门户应用,Struts2更适合Intranet企业应用。 主要理由介绍如下(不同的项目或团队有不同背景和选型考虑,以下仅供参考):

  • REST:SpringMVC对于REST有着良好的支持,Struts通过struts2-rest-plugin提供勉强可用的支持。就REST技术本身,可以为WWW站点应用以一种优雅的URL链接形式提供资源或页面访问是很不错的特性;但是对于企业应用来说,要想把负责的业务逻辑请求都转换为REST形式那基本是个费力不讨好的事情。
  • URL Mapping: SpringMVC以Request Mapping注解方式定义,Struts以struts2-convention-plugin插件提供支持。 SpringMVC的注解定义方式虽然灵活,但是如果对于大型企业应用开发没有良好的约定规范而由开发人员自由注解定义那简直就是灾难,为了得知一个URL请求对应哪个Controller代码还需要全文查询Java代码进行定位。 而struts2-convention-plugin基于约定大于配置的规则,可以使URL和相关Action、JSP代码强制映射,可以快速根据URL反推定位相关代码文件路径位置和名称,简化项目运维成本。
  • Data Binding:SpringMVC基于注解支持Method级别细粒度数据绑定,Struts基于OGNL语法进行Model和Action更广范围的数据绑定。虽然SpringMVC的绑定机制提供更严谨的数据处理模式,但是对于企业应用来说Struts更粗粒度的处理模式反倒可以简化复杂业务数据绑定处理。
  • Data Validation:SpringMVC提供JSR303支持,框架对此专门做了扩展设计,详见对于Form/Data Validation的增强
  • Tag Libs: 应该说Struts标签支持还是比SpringMVC更加丰富和强大,借助Struts灵活的标签扩展支持,框架扩展设计封装定制化UI标签
  • Dev Resource:虽然熟悉SpringMVC的开发人员数量大有赶超Struts人员数量之势,但是熟悉Struts的人员数量依然处于主流位置
  • 最后,还得加上一条自私的理由:比起SpringMVC,自己更熟悉Struts
## Struts Enhancement

选择了Struts之后,框架也对此作了一定扩展增强,以下列出struts.xml文件主要配置部分,如REST、Jackson、Token、Interceptor等,具体可以根据struts.xml配置文件相关注释和代码实现:

    <!-- 配置为默认基于Bootstrap的样式输出 -->
    <constant name="struts.ui.theme" value="bootstrap" />

    <!-- struts2-spring-plugin,参考http://struts.apache.org/2.x/docs/spring-plugin.html -->
    <constant name="struts.objectFactory"
        value="org.apache.struts2.spring.StrutsSpringObjectFactory" />
    <constant name="struts.ognl.allowStaticMethodAccess" value="false" />

    <!-- 参考Spring MVC Rest的方式,增强提供Negotiation方式动态计算Result响应输出模式 -->
    <bean type="com.opensymphony.xwork2.ActionProxyFactory" name="rest-negotiation"
        class="org.apache.struts2.rest.NegotiationRestActionProxyFactory" />
    <constant name="struts.actionProxyFactory" value="rest-negotiation" />

    <!-- Struts2默认只提供Jackson1版本支持,增强补充提供Jackson2版本支持 -->
    <bean type="org.apache.struts2.rest.handler.ContentTypeHandler"
        name="jackson" class="org.apache.struts2.rest.Jackson2LibHandler" />
    <!-- 默认为Struts内部的JSON序列化,配置为采用功能更加强大的Jackson组件 -->
    <constant name="struts.rest.handlerOverride.json" value="jackson" />

    <!-- struts2-rest-plugin,参考http://struts.apache.org/2.x/docs/rest-plugin.html -->
    <constant name="struts.mapper.class" value="rest" />
    <constant name="struts.convention.action.suffix" value="Controller" />
    <constant name="struts.convention.action.mapAllMatches" value="true" />
    <constant name="struts.rest.defaultExtension" value="xhtml" />
    <constant name="struts.rest.content.restrictToGET" value="false" />
    <constant name="struts.action.excludePattern"
        value="/components/.*,/resources/.*,/pub/jasper/images.*" />

    <!-- struts2-convention-plugin,参考https://cwiki.apache.org/WW/convention-plugin.html -->
    <constant name="struts.convention.result.path" value="/pages/" />
    <constant name="struts.convention.action.fileProtocols" value="jar,zip" />
    <constant name="struts.convention.action.includeJars" value=".*common-service.*,.*_wl_cls_gen.*" />

    <constant name="struts.convention.default.parent.package"
        value="crud-default" />

    <package name="crud-default" extends="rest-default,jasperreports-default">
        <interceptors>
            <!-- 默认实现是配置了Token拦截器只要没有提供token参数则抛出异常,为了灵活控制,
                                                  调整实现为:有token才进行检测,没有token参数则放行的宽松模式 -->
            <interceptor name="extTokenInterceptor"
                class="lab.s2jh.core.web.interceptor.ExtTokenInterceptor" />
            <!-- 扩展参数拦截器:添加可配置的参数绑定支持 -->
            <interceptor name="extParametersInterceptor"
                class="lab.s2jh.core.web.interceptor.ExtParametersInterceptor" />
            <!-- 修改标准的PrepareInterceptor: 先执行prepare再执行相关的prepareXXX方法 -->
            <interceptor name="extPrepareInterceptor"
                class="lab.s2jh.core.web.interceptor.ExtPrepareInterceptor" />
Clone this wiki locally