一、前言
其实大家在工作的时候,一般很少涉及框架大改的情况,除非是一个值得重视与长期发展的项目,碰巧我遇到了一个比较重要的项目,由于项目的初次搭建的时候欠缺很多方法的考虑,使用了比较旧的架构:Spring+Struts2+Hibernate+MyBatis,这个框架也有一些优点,比如Hibernate+MyBatis可以同时使用,而且还共用了数据源,也不影响事务的切面。由于Spring的发展比Struts2的发展快以及各项优点(此处不多说),加上SpringBoot、SpringCloud的出现,大多数程序员都倾向于使用SpringMVC而不是Struts2MVC,因此,考虑到公司大项目的发展,项目计划第一步由Struts2MVC转换成SpringMVC(Spring版本不变,还是3版本),第二步升级Spring版本(由版本3升级版本4,尽量做到无配置文件),第三步转为SpringBoot项目或者SpringCloud微服务(模块分割),此处由有多年架构方面经验的我来完成项目计划的第一步(由Struts2MVC转换成SpringMVC)
二、步骤(以下步骤仅供参考,要结合自己项目而调整)
1、修改pom文件:
去掉struts2的相关依赖:比如:struts2-core、struts2-convention-plugin、struts2-junit-plugin、struts2-json-plugin、struts2-spring-plugin等等包含struts2的依赖包
由于我的项目有appfuse-web的依赖,所以不需要添加spring-webmvc的依赖,如果你的项目没有SpringMVC相关依赖,请添加
2、修改web.xml文件:
(1)去掉Struts2的filter和filter-mapping以及删除struts.xml文件(建议备份)
<filter>
<filter-name>struts</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
<init-param>
<param-name>config</param-name>
<!-- 配置装载struts.xml路径 -->
<param-value>struts-default.xml,struts-plugin.xml,conf/struts/struts.xml</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>struts</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
(2)加上springMVC的servlet和servlet-mapping(需要注意url-pattern的值,文章下面有其他说明)
<!-- Spring MVC 核心控制器 DispatcherServlet 配置 -->
<servlet>
<servlet-name>springMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:conf/spring/spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
3、在classpath:conf/spring/下面添加spring-mvc.xml(Json转换器暂时使用Jackson,而MyExceptionMappingInterceptor是本项目的全局异常类,PrivilegeInterceptor是本项目的拦截器)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 扫描controller(controller层注入) -->
<context:component-scan base-package="com.test.cn.**.action"/>
<!-- 解决使用@ResponseBody乱码,Spring版本问题 -->
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes" value = "text/plain;charset=UTF-8" />
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!--避免IE执行AJAX时,返回JSON出现下载文件 -->
<bean id="mappingJacksonHttpMessageConverter"
class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=UTF-8</value>
</list>
</property>
</bean>
<!-- 启动SpringMVC的注解功能,完成请求和注解POJO的映射 -->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="mappingJacksonHttpMessageConverter" /> <!-- JSON转换器 -->
</list>
</property>
</bean>
<!--对静态资源文件的访问-->
<mvc:resources mapping="/lib/**" location="/lib/" />
<!-- 默认的视图解析器 在上边的解析错误时使用 (默认使用html)- -->
<bean id="defaultViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 配置springMVC处理上传文件的信息 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="utf-8"/>
<property name="maxUploadSize" value="1073741824"/><!-- 最大上传1G -->
<property name="maxInMemorySize" value="40960"/>
</bean>
<bean class="com.test.cn.core.web.interceptor.MyExceptionMappingInterceptor" />
<!-- 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.test.cn.core.web.interceptor.PrivilegeInterceptor" />
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.test.cn.core.web.interceptor.LogInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
</beans>
4、由于Controller注解在上面的xml已扫描,所以在applicationContext-service.xml去掉Controller注解的扫描(这个xml文件原有事务管理器与aop切面等配置,根据自己项目寻找自己的xml文件)【注:至于Controller注解扫描为什么与其他注解分开,这个问题请自行去了解一下SpringMVC的启动原理】
修改前:
<!--开启包注解扫描-->
<context:component-scan base-package="com.test">
</context:component-scan>
修改后:
<!--开启包注解扫描-->
<context:component-scan base-package="com.test">
<!--结合springmvc -->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
5、拦截器调整
(1)调整全局异常拦截器MyExceptionMappingInterceptor:由继承Struts2的ExceptionMappingInterceptor换成继承SpringMVC的ExceptionHandlerExceptionResolver,由于继承类不同,方法也不同,返回类型不同,稍微理解一下进行了更改如下:
public class MyExceptionMappingInterceptor extends ExceptionMappingInterceptor {
private static final long serialVersionUID = -1L;
private static final Logger logger = LoggerFactory.getLogger(MyExceptionMappingInterceptor.class);
@Override
public String intercept(ActionInvocation invocation) throws Exception {
String result = null;
try {
result = invocation.invoke();
} catch (GeneralException ex) {
result = handleException(ex, invocation);
} catch (UnauthorizedAccessException ex) {
result = handleException(ex, invocation);
} catch (BusinessException ex) {
result = handleException(ex, invocation);
} ca