首先,弄清楚springmvc跟struts使用的时候有哪些地方会有区别:
(一)从引入开始,struts2在web.xml中使用过滤器 (org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter)
而 springmvc采用 Servlet引入 org.springframework.web.servlet.DispatcherServlet
<!-- spring mvc servlet -->
<servlet>
<description>spring mvc servlet</description>
<servlet-name>springMvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<description>spring mvc 配置文件</description>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springMvc</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
(二)请求过来之后的mapping,struts采用配置文件,action method等
这里的springmvc,最方便的写法还是 注解,比如
spring-mvc.xml
<!-- 自动扫描controller包下的所有类,使其认为spring mvc的控制器 -->
<context:component-scan base-package="net.jeeshop" />
<!-- 启动Spring MVC的注解功能,完成请求和注解POJO的映射 -->
<bean
class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="mappingJacksonHttpMessageConverter" />
</list>
</property>
</bean>
在普通的action上面加上
@Scope("prototype")
@Controller("frontProductAction")
@RequestMapping("/product")
(三)处理完请求,返回视图
struts配置在 result,然后对应到jsp页面,为了最小程度的改动代码,并且也是用jsp视图
<!-- 对模型视图名称的解析,即在模型视图名称添加前后缀 -->
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/" p:suffix=".jsp" />
然后action中对返回的String视图,定义成一个变量,然后将每个方法的返回String改为对应的变量名,去掉引号。
这一步可以使用 getPage.pl (专门从 struts的配置文件中转化出对应的 jsp路径,然后转变成springmvc的action中的上述赋值语句)
具体示例请参考下述步骤。
(四)最费时间的部分:
修改jsp页面里面的 struts标签,改为JSTL标签或者 springmvc标签。这里如果作者一开始使用JSTL标签的话,那么基本就不用修改了,
实际操作过程中,这一步耗费时间最多。。。 有点小的感悟就是,用标准化的东西还是有好处,在换其他的框架时不用改动,当然struts标签也有它的优势,
所以也是要综合考虑的。
详细步骤(已忽略配置文件部分):
【1】公共处理:
1.baseAction里面 实现的接口 ActionSuppoer,需要去掉,因为它是Struts的,为了保证代码最小改动,我寻找了 springmvc中获取 request response的方法,
(当然常规做法是在方法的参数里面直接写上request就可以使用了),模拟写了一个 springmcActionContext,继承SpringmvcContextHolder,实现了getRequest、
getResponse方法等等。 但实际上这个做法最后可以忽略了,因为找到了 springmvc的一个有用注解,可以在每个方法调用前执行一段代码【当然就可以在这一段
执行的时候,把参数request response 甚至 <T> e 注入到 BaseAction中,然后其他子类Action即可直接使用已赋值的变量了! 】
protected Model model;
/*************
* 获取request response
*/
protected HttpServletRequest request;
protected HttpServletResponse response;
@ModelAttribute
public void setReqAndRes(HttpServletRequest request,
HttpServletResponse response,@ModelAttribute("e") E e,String[] ids,Model model) {
this.e = e; // 将request中的对象放入action,模拟struts2 属性对象的自动封装
this.request = request;
this.response = response;
this.model = model;
this.ids = ids;
model.addAttribute("e", e);
logger.error("BaseAction:method called before:request="+request+",response="+response
+",e="+JSON.toJSONString(e));
}
特别注意!这里的 model.addAttribute("e", e); 是把变量加入到 输出视图中,如果e的引用没有发生变化,则不需要重新加入一次,
如果 涉及到修改引用地址,则需要重新加入一次了!比如 e = 从数据库重新查出来的一个值,这时候,如果不重新加入,则返回视图获取不到!
2.BaseAction中 去掉 getRequest getResponse方法等
因为子类action中 getRequest()要替换为 request ;getResponse()替换为 response。这两个对象都可直接取自 BaseAction属性。
3.BaseAction中 需要被子类继承了的方法 需要加上 requestmapping 注解,这样有些被子类重写的方法就不需要重复加 mapping路径了
【2】子模块处理部分
-------------------------------------- java action 部分-------------------------------------
1. 先给action加上三个注解
@Scope("prototype")
@Controller
@RequestMapping("/manage/user")
@ModelAttribute
public void initStrutsActionParam(){
this.server = null ; // null这个地方替换成当前action注入的service
}
找到struts的配置文件
所有视图返回的地方,转换成 “视图赋值语句”,调用getPage.pl
2.
因为在baseaction中写好了直接获取requet response的方法【protected】
替换所有的getRequest() 为 request
替换所有的 getResponse() 为 response
3.所有“需要”的方法加上 @requestMapping
4.找到spring的配置文件,配置action的地方,如果是注入的service bean,则在action中将其改为 autowired注解,并将setter getter删掉
然后其他属性,就是非注入的,先注释掉,然后看哪些方法报错,将报错的方法中,选择public的方法[因为私有方法的变量不需要传到视图层]
① 先在方法声明中加上”Model model“ 然后方法体中加入 model.addAttribute(....); 这样表示将这个属性返回到视图[因为之前这些属性
直接在action中定义,struts2就能够传递到视图层了]
5. 注意这个方法
@Override
protected void selectListAfter() {
pager.setPagerUrl(getBasePath() + "manage/?/selectList.action");
}
##### 6.拷贝这个方法: 这一步不需要了
public String selectList() throws Exception {
return super.selectList();
}
----------------------------- page页面部分:所有struts标签 替换成jstl或者spring 标签-------------------------------------
在 common.jsp中加入以下语句
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<c:set var="ctx" value="<%=request.getContextPath()%>"/>
<input type="hidden" id="namespace" value="${ctx}" /> 当然这里的namespace 跟 struts2的不一样,只是我这里取了一个名称而已。
5-0 a标签
<a href="${ctx}/manage/user/toAdd.action" method="toAdd" class="btn btn-success">
<i class="icon-plus-sign icon-white"></i> 添加
</a>
button标签
<button method="${ctx}/manage/user/selectList.action" class="btn btn-primary" οnclick="selectList(this)">
<i class="icon-search icon-white"></i> 查询
</button>
<button method="${ctx}/manage/user/update.action"
class="btn btn-success">
<i class="icon-ok icon-white"></i> 保存
</button>
5-1 s:if s:else 标签
<c:choose>
<c:when test="${role.id == ''}">
</c:when>
<c:otherwise>
</c:otherwise>
</c:choose>
<c:choose>
<c:when test="${e.id=='' or e.id==null}">
<input type="submit" value="新增" method="insert" οnclick="return onSubmit();" class="btn btn-primary" />
</c:when>
<c:otherwise>
<input type="submit" value="保存" method="update" οnclick="return onSubmit();" class="btn btn-primary"/>
</c:otherwise>
</c:choose>
【<input type="text" name="role_name" id="role_name" /> <input type="text" name="role_name" id="role_name" readonly="readonly"/>】
5-2 select标签
5-2-1 数据从数据库查询的
<select id="" name="">
<c:forEach items="${roleList}" var="role">
<option value="${role.id}" <c:if test='${role.id == e.rid}'>selected='selected'</c:if> >${role.role_name}</option>
</c:forEach>
</select>
5-2-2 数据直接写死的
<select id="status" name="status" class="input-small">
<option value='0'></option>
<option value='1' <c:if test='${e.status=="y"}'>selected='selected'</c:if>>启用</option>
<option value='2' <c:if test='${e.status=="n"}'>selected='selected'</c:if>>禁用</option>
</select>
5-3 列表循环
<c:forEach items="${pager.list}" var="user">
<tr>
</tr>
</c:forEach>
<input type="hidden" name="id" label="id" value="${e.id}" />
============================================
未完成任务:
1.解决键值对 也有可能有其他的,需要唯一判断的地方,增加键 编辑的时候需要 唯一性验证。否则数据库报错
2.解决键值对编辑的时候,value字段不能够搜索的情况,mybatis bug
3.解决 系统设置功能模块, 并且保存完毕,之后不能继续保存的bug
4.解决定时调度的错误
Exception in thread "pool-2-thread-1" Exception in thread "pool-2-thread-3" java.lang.NullPointerException
at org.slf4j.impl.Log4jLoggerAdapter.error(Log4jLoggerAdapter.java:497)
at net.jeeshop.core.task.CancelOrderTask.run(CancelOrderTask.java:40)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:662)
java.lang.NullPointerException
at org.slf4j.impl.Log4jLoggerAdapter.error(Log4jLoggerAdapter.java:497)
at net.jeeshop.core.task.ManageCacheTask.run(ManageCacheTask.java:36)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:662)
Exception in thread "pool-2-thread-2" java.lang.NullPointerException
at org.slf4j.impl.Log4jLoggerAdapter.error(Log4jLoggerAdapter.java:497)
at net.jeeshop.core.task.SystemAutoNotifyTask.run(SystemAutoNotifyTask.java:47)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:662)
2014-08-26 20:25:59 net.jeeshop.core.task.SystemAutoNotifyTask:47 ERROR - OrderCancelTask.run...
=================================================================================
经验:springmvc 加上 urlrewrite之后,/A/A.action 经常出问题,所以最好改为 /A/B.action
否则会下面这种错误:
No matching handler method found for servlet request: path '/product/product.action', method 'GET', parameters map[[empty]]
而不是这种错误:
No mapping found for HTTP request with URI [/jeeshop/product/product1.action] 【这种才是正常的错误】
=======================================================================================
jstl 判断list 是否为空,不能这样写
<c:when test="${requestScope.commentPager.list==null or
requestScope.commentPager.list.size == 0 }">
而应该这样写:
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
<c:if test="${fn:length(list) <= 0}">
list对象为空
</c:if>
======================================================================================
java.lang.ArithmeticException: / by zero 发生这种错误是因为一个数除以0的情况,0是不能作为被除数的
======================================================================================================
严重: Servlet.service() for servlet springMvc threw exception
org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'e' on field 'catalogID': rejected value []; codes [typeMismatch.e.catalogID,typeMismatch.catalogID,typeMismatch.int,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [e.catalogID,catalogID]; arguments []; default message [catalogID]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'int' for property 'catalogID'; nested exception is java.lang.NumberFormatException: For input string: ""]
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.doBind(HandlerMethodInvoker.java:810)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.resolveHandlerArguments(HandlerMethodInvoker.java:359)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:153)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:426)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:414)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:790)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:719)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:644)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:560)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at net.jeeshop.core.filter.EncodeFilter.doFilter(EncodeFilter.java:22) <============= 每次都在这里
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
..............................