Spring MVC入门第3天--注解开发
Posted 鹿天斐
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring MVC入门第3天--注解开发相关的知识,希望对你有一定的参考价值。
文档版本 | 开发工具 | 测试平台 | 工程名字 | 日期 | 作者 | 备注 |
---|---|---|---|---|---|---|
V1.0 | 2016.07.01 | lutianfei | none |
商品修改功能开发
一、需求
- 操作流程:
- 1、进入商品查询列表页面
- 2、点击修改,进入商品修改页面,页面中显示了要修改的商品(从数据库查询)
- 要修改的商品从数据库查询,根据商品id(主键)查询商品信息
- 3、在商品修改页面,修改商品信息,修改后,点击提交
二、开发mapper
- mapper:
- 根据id查询商品信息
- 根据id更新Items表的数据
- 不用开发了,使用逆向工程生成的代码。
- mapper:
三、开发service
- 接口功能:
- 根据id查询商品信息
- 修改商品信息
- 接口功能:
// ItemsServiceImpl
@Override
public ItemsCustom findItemsById(Integer id) throws Exception{
Items items= itemsMappper.selectByPrimaryKey(id);
//中间对信息进行了业务处理
//。。。
//返回ItemsCustom
ItemsCustom itemsCustom = new ItemsCustom();
//将items的属性拷贝到itemsCustom中
BeanUtils.copyProperties(items,itemsCustom);
return itemsCustoom;
}
@Override
public void updateItems(Integer id, ItemsCustom itemsCustom) throws Execption{
//添加业务校验,通常在service接口对关键参数进行校验
//校验id是否为空,如果为空,抛出异常
// 更新商品信息
//因为有大文本类型属性所以要使用updateByPrimaryKeyWithBLOBs函数
itmesCustom.setId(id); //updateByPrimaryKeyWithBLOBs要求必须传人id。
itemsMapper.updateByPrimaryKeyWithBLOBs(itemsCustom);
}
- 四、开发controller
- 方法:
- 商品信息修改页面显示
- 方法:
@RequestMapping("/editItems")
public ModelAndView editItems() throws Exception{
//调用service根据商品id查询商品信息
ItemsCustom itemsCustom = itemsService.findItemsById(1);
//返回ModelAndView
ModelAndView modelAndView = new ModelAndView();
//将商品信息放到model
modelAndView.addObject("itemsCustom",itemsCustom);
//商品修改页面
modelAndView.setViewName("items/editItems");
return modelAndView;
}
* 商品信息修改提交
@RequestMapping("/editItemsSubmit")
public ModelAndView editItemsSubmit() throws Exception{
//调用service更新商品信息,页面需要将商品信息传到此方法
//。。。。。
//返回ModelAndView
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("success");
return modelAndView;
}
- 添加success.jsp 和 editItems.jsp
springmvc注解开发
@RequestMapping
注解
- 通过RequestMapping注解可以定义不同的处理器映射规则。
URL路径映射
- @RequestMapping(value=”/item”)或@RequestMapping(“/item)
- value的值是数组,可以将多个url映射到同一个方法
窄化请求映射
- 在class上添加
@RequestMapping(url)
指定通用请求前缀, 限制此类下的所有方法请求url必须以请求前缀开头,通过此方法对url进行分类管理。 - 如下:
- @RequestMapping放在类名上边,设置请求前缀
- @Controller
- @RequestMapping(“/item”)
- 方法名上边设置请求映射url:@RequestMapping放在方法名上边,如下:
- @RequestMapping(“/queryItem “)
- 访问地址为:/item/queryItem
- @RequestMapping放在类名上边,设置请求前缀
请求方法限定
- 限定GET方法
- @RequestMapping(method = RequestMethod.GET)
- 如果通过Post访问则报错:
- HTTP Status 405 - Request method** ‘POST’** not supported
例如:
- @RequestMapping(value=”/editItem”,method=RequestMethod.GET)
限定POST方法
- @RequestMapping(method = RequestMethod.POST)
- 如果通过Post访问则报错:
- HTTP Status 405 - Request method ‘GET’ not supported
- GET和POST都可以
- @RequestMapping(method={RequestMethod.GET,RequestMethod.POST})
controller方法返回值
- 返回ModelAndView
- controller方法中定义ModelAndView对象并返回,对象中可添加model数据、指定view。
- 需要方法结束时,定义ModelAndView,将model和view分别进行设置。
//指定逻辑视图名,经过视图解析器解析为jsp物理路径:/WEB-INF/jsp/item/editItem.jsp
return "item/editItem";
- 返回string
- 当controller方法返回string时,会有如下3种情况:
1、表示返回逻辑视图名。真正视图(jsp路径)=前缀+逻辑视图名+后缀
2、redirect重定向
- 商品修改提交后,重定向到商品查询列表。
- redirect重定向特点:浏览器地址栏中的url会变化。修改提交的request数据无法传到重定向的地址。因为重定向后重新进行request(request无法共享)
- 注: 这里因为在同一个类下,不需要将窄化映射的路径加入其中
- redirect方式相当于“response.sendRedirect()”,转发后浏览器的地址栏变为转发后的地址,因为转发即执行了一个新的request和response。
- 由于新发起一个request原来的参数在转发时就不能传递到下一个url,如果要传参数可以/item/queryItem.action后边加参数,如下:
- /item/
queryItem?...&…
3、forward页面转发
- 通过forward进行页面转发,浏览器地址栏url不变,request可以共享。
- forward方式相当于
request.getRequestDispatcher().forward(request,response)
,转发后浏览器地址栏还是原来的地址。转发并没有执行新的request和response,而是和转发前的请求共用一个request和response。所以转发前请求的参数在转发后仍然可以读取到。
//结果转发到editItem.action,request可以带过去
return "forward:editItem.action";
- 返回void
- 在controller方法形参上可以定义request和response,使用request或response指定响应结果:
- 1、使用request转向页面,如下:
- request.getRequestDispatcher(“页面路径”).forward(request, response);
- 2、也可以通过response页面重定向:
- response.sendRedirect(“url”)
- 3、也可以通过response指定响应结果,例如响应json数据如下:
- response.setCharacterEncoding(“utf-8”);
- response.setContentType(“application/json;charset=utf-8”);
- response.getWriter().write(“json串”);
spring参数绑定过程
- 从客户端请求key/value数据,经过参数绑定,将key/value数据绑定到controller方法的形参上。
- springmvc中,接收页面提交的数据是通过方法形参来接收。而不是在controller类定义成员变量接收。
参数绑定默认支持的类型
直接在controller方法形参上定义下边类型的对象,就可以使用这些对象。在参数绑定过程中,如果遇到下边类型直接进行绑定。
HttpServletRequest
通过request对象获取请求信息- HttpServletResponse
- 通过response处理响应信息
- HttpSession
- 通过session对象得到session中存放的对象
- Model/ModelMap
- model是一个接口,modelMap是一个接口实现 。
- 作用:将model数据填充到request域。
参数绑定简单类型
- 通过
@RequestParam
对简单类型的参数进行绑定。
- 如果不使用@RequestParam,要求request传入参数名称和controller方法的形参名称一致,方可绑定成功。
- 如果使用@RequestParam,不用限制request传入参数名称和controller方法的形参名称一致。
- 通过
required属性
指定参数是否必须要传入,如果设置为true,没有传入参数,报下边错误:
使用@RequestParam常用于处理简单类型的绑定。
value
:参数名字,即入参的请求参数名字,如value=“item_id”表示请求的参数区中的名字 为item_id的参数的值将传入;- required:是否必须,默认是true,表示请求中一定要有相应的参数,否则将报;
- TTP Status 400 - Required Integer parameter ‘XXXX’ is not present
- defaultValue:默认值,表示如果请求中没有同名参数时的默认值
例子如下:
public String editItem(@RequestParam(value="item_id",required=true) String id) {
}
- 形参名称为id,但是这里使用value=”item_id”限定请求的参数名为item_id,所以页面传递参数的名必须为item_id。
- 注意:如果请求参数中没有item_id将跑出异常:
- HTTP Status 500 - Required Integer parameter ‘item_id’ is not present
- 这里通过required=true限定item_id参数为必需传递,如果不传递则报400错误,可以使用defaultvalue设置默认值,即使required=true也可以不传item_id参数值
pojo参数绑定
页面中input的name和controller的pojo形参中的属性名称一致,将页面中数据绑定到pojo。
页面定义:
controller的pojo形参的定义:
测试:
post乱码
- 在web.xml添加post乱码filter
<!-- post乱码过虑器 -->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- 对于get请求中文参数出现乱码解决方法有两个:
- 修改tomcat配置文件添加编码与工程编码一致,如下:
<Connector URIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
- 另外一种方法对参数进行重新编码:
String userName new String(request.getParamter("userName").getBytes("ISO8859-1"),"utf-8")
- ISO8859-1是tomcat默认编码,需要将tomcat编码后的内容按utf-8编码
自定义参数绑定实现日期类型绑定
- 对于controller形参中pojo对象,如果属性中有日期类型,需要自定义参数绑定。
- 将请求日期数据串转成* 日期类型,要转换的*日期类型和pojo中日期属性的类型保持一致。
所以自定义参数绑定将日期字符串转成java.util.Date类型。
需要向处理器适配器中注入自定义的参数绑定组件
自定义日期类型绑定
配置方式:springmvc.xml
包装类型pojo参数绑定
- 需求: 商品查询controller方法中实现商品查询条件传入。
实现方法
第一种方法:在形参中 添加HttpServletRequest request参数,通过request接收查询条件参数。
第二种方法:在形参中让包装类型的pojo接收查询条件参数。
- 页面传参数的特点:复杂,多样性。
- 例如条件包括 :用户账号、商品编号、订单信息。。。
- 如果将用户账号、商品编号、订单信息等放在简单pojo(属性是简单类型)中,pojo类属性比较多,比较乱。
- 建议使用包装类型的pojo,pojo中属性是pojo。
- 页面传参数的特点:复杂,多样性。
页面参数和controller方法形参定义
- 页面参数:
- 商品名称:
<input name="itemsCustom.name" />
- 注意:itemsCustom和包装pojo中的属性一致即可。
- 商品名称:
- 页面参数:
包装对象定义:
- controller方法 测试
@RequestMapping("/queryItems")
public ModelAndView queryItems(HttpServletRequest request, ItemsQueryVo itemsQueryVo) throws Exception{
System.out.println(request.getParameter("id"));
List<ItemsCustom> itemsList= itemsService.findItemsList(itemsQueryVo);
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("itemsList", itemsList);
modelAndView.setViewName("items/itemsList");
return modelAndView;
}
集合类型绑定
数组绑定
需求 : 商品批量删除,用户在页面选择多个商品,批量删除。
表现层实现(controller层)
- 关键:将页面选择(多选)的商品id,传到controller方法的形参,方法形参使用数组接收页面请求的多个商品id。
controller方法定义:
页面定义:
<title>查询商品列表</title>
<script type="text/javascript">
function deleteItems(){
//提交form
document.itemsForm.action="${pageContext.request.contextPath }/items/deleteItems.action";
document.itemsForm.submit();
}
function queryItems(){
//提交form
document.itemsForm.action="${pageContext.request.contextPath }/items/queryItems.action";
document.itemsForm.submit();
}
</script>
</head>
<body>
当前用户:${username },
<c:if test="${username!=null }">
<a href="${pageContext.request.contextPath }/logout.action">退出</a>
</c:if>
<form name="itemsForm" action="${pageContext.request.contextPath }/items/queryItems.action" method="post">
查询条件:
<table width="100%" border=1>
<tr>
<td>
商品名称:<input name="itemsCustom.name" />
商品类型:
<select name="itemtype">
<c:forEach items="${itemtypes }" var="itemtype">
<option value="${itemtype.key }">${itemtype.value }</option>
</c:forEach>
</select>
</td>
<td><input type="button" value="查询" onclick="queryItems()"/>
<input type="button" value="批量删除" onclick="deleteItems()"/>
</td>
</tr>
</table>
商品列表:
<table width="100%" border=1>
<tr>
<td>选择</td>
<td>商品名称</td>
<td>商品价格</td>
<td>生产日期</td>
<td>商品描述</td>
<td>操作</td>
</tr>
<c:forEach items="${itemsList }" var="item">
<tr>
<td><input type="checkbox" name="items_id" value="${item.id}"/></td>
<td>${item.name }</td>
<td>${item.price }</td>
<td><fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/></td>
<td>${item.detail }</td>
<td><a href="${pageContext.request.contextPath }/items/editItems.action?id=${item.id}">修改</a></td>
</tr>
</c:forEach>
</table>
</form>
list绑定
- 需求 : 通常在需要批量提交数据时,将提交的数据绑定到
list<pojo>
中。
- 比如:成绩录入(录入多门课成绩,批量提交)。
本例子需求:批量商品修改,在页面输入多个商品信息,将多个商品信息提交到controller方法中。
表现层实现
- controller方法定义:
- 1、进入批量商品修改页面(页面样式参考商品列表实现)
- 2、批量修改商品提交
- 使用List接收页面提交的批量数据,通过包装pojo接收: 在包装pojo中定义
list<pojo>
属性
- 页面定义:(editItemsQuery.jsp)
- controller方法定义:
<title>查询商品列表</title>
<script type="text/javascript">
function editItemsAllSubmit(){
//提交form
document.itemsForm.action="${pageContext.request.contextPath }/items/editItemsAllSubmit.action";
document.itemsForm.submit();
}
function queryItems(){
//提交form
document.itemsForm.action="${pageContext.request.contextPath }/items/queryItems.action";
document.itemsForm.submit();
}
</script>
</head>
<body>
<form name="itemsForm" action="${pageContext.request.contextPath }/items/queryItems.action" method="post">
查询条件:
<table width="100%" border=1>
<tr>
<td>
商品名称:<input name="itemsCustom.name" />
</td>
<td><input type="button" value="查询" onclick="queryItems()"/>
<input type="button" value="批量修改提交" onclick="editItemsAllSubmit()"/>
</td>
</tr>
</table>
商品列表:
<table width="100%" border=1>
<tr>
<td>商品名称</td>
<td>商品价格</td>
<td>生产日期</td>
<td>商品描述</td>
<td>操作</td>
</tr>
<c:forEach items="${itemsList }" var="item" varStatus="status">
<tr>
<td><input name="itemsList[${status.index }].name" value="${item.name }"/></td>
<td><input name="itemsList[${status.index }].price" value="${item.price }"/></td>
<td><input name="itemsList[${status.index }].createtime" value="<fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/>"/></td>
<td><input name="itemsList[${status.index }].detail" value="${item.detail }"/></td>
</tr>
</c:forEach>
</table>
</form>
</body>
</html>
map绑定
- 通过在包装pojo中定义map类型属性。
- 在包装类中定义Map对象,并添加get/set方法,action使用包装对象接收。
- 包装类中定义Map对象如下:
Public class QueryVo {
private Map<String, Object> itemInfo = new HashMap<String, Object>();
//get/set方法..
}
- 页面定义如下:
<tr>
<td>学生信息:</td>
<td>
姓名:<inputtype="text"name="itemInfo[‘name‘]"/>
年龄:<inputtype="text"name="itemInfo[‘price‘]"/>
.. .. ..
</td>
</tr>
- Contrller方法定义如下:
public String useraddsubmit(Model model,QueryVo queryVo)throws Exception{
System.out.println(queryVo.getStudentinfo());
}
springmvc校验
在项目中通常使用较多是前端的校验,比如页面中js校验。对于安全要求较高点建议在服务端进行校验。
服务端校验:
- 控制层conroller:校验页面请求的参数的合法性。在服务端控制层conroller校验,不区分客户端类型(浏览器、手机客户端、远程调用)
- 业务层service(使用较多):主要校验关键业务参数,仅限于service接口中使用的参数。
- 持久层dao:一般是不校验的。
springmvc使用hibernate的校验框架validation(和hibernate没有任何关系)。
校验思路:
- 页面提交请求的参数,请求到controller方法中,使用validation进行校验。如果校验出错,将错误信息展示到页面。
- 具体需求:
- 商品修改,添加校验(校验商品名称长度,生产日期的非空校验),如果校验出错,在商品修改页面显示错误信息。
具体实现流程
- 环境准备
- hibernate的校验框架validation所需要jar包:
- hibernate的校验框架validation所需要jar包:
- 1、配置校验器并添加校验错误信息配置文件
<!-- 校验器 -->
<bean id="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<!-- hibernate校验器-->
<property name="providerClass" value="org.hibernate.validator.HibernateValidator" />
<!-- 指定校验使用的资源文件,在文件中配置校验错误信息,如果不指定则默认使用classpath下的ValidationMessages.properties -->
<property name="validationMessageSource" ref="messageSource" />
</bean>
<!-- 校验错误信息配置文件 -->
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<!-- 资源文件名-->
<property name="basenames">
<list>
<value>classpath:CustomValidationMessages</value>
</list>
</property>
<!-- 资源文件编码格式 -->
<property name="fileEncodings" value="utf-8" />
<!-- 对资源文件内容缓存时间,单位秒 -->
<property name="cacheSeconds" value="120" />
</bean>
2、校验器注入到处理器适配器中
3、在pojo中添加校验规则
- 在Items.java中添加校验规则:
- 在Items.java中添加校验规则:
4、添加校验错误配置信息
- 在CustomValidationMessages.properties配置校验错误信息:
- 在CustomValidationMessages.properties配置校验错误信息:
5、捕获校验错误信息
- 在需要校验的pojo前边添加
@Validated
, - 在需要校验的pojo后边添加
BindingResult bindingResult
接收校验出错信息 - 注意:
@Validated
和BindingResult bindingResult
是配对出现,并且形参顺序是固定的(一前一后)。
- 在需要校验的pojo前边添加
- 6、在页面显示校验错误信息
- 在controller中将错误信息传到页面即可。
- 在controller中将错误信息传到页面即可。
- 7、页面显示错误信息:
分组校验
需求 : 在pojo中定义校验规则,而pojo是被多个 controller所共用,当不同的controller方法对同一个pojo进行校验,但是每个controller方法需要不同的校验。
解决方法:
- 定义多个校验分组(其实是一个java接口),分组中定义有哪些规则
- 每个controller方法使用不同的校验分组
流程如下:
1、校验分组
2、在校验规则中添加分组(Items.java)
3、在controller方法使用指定分组的校验
数据回显
- 提交后,如果出现错误,将刚才提交的数据回显到刚才的提交页面。
pojo数据回显方法
springmvc默认支持pojo数据回显,springmvc自动将形参中的pojo重新放回request域中,request的key为pojo的类名(首字母小写),如下:
controller方法:
@RequestMapping("/editItemSubmit")
public String editItemSubmit(Integer id,ItemsCustom itemsCustom)throws Exception{
springmvc自动将itemsCustom放回request,相当于调用下边的代码:
model.addAttribute("itemsCustom", itemsCustom);
jsp页面:
使用@ModelAttribute完成数据回显
- 1、使用
@ModelAttribute
指定pojo回显到页面在request中的key
// 商品修改提交
@RequestMapping("/editItemSubmit")
public String editItemSubmit(Model model,@ModelAttribute("item") ItemsCustom itemsCustom)
页面:
<tr>
<td>商品名称</td>
<td><input type="text" name="name" value="${item.name }"/></td>
</tr>
<tr>
<td>商品价格</td>
<td><input type="text" name="price" value="${item.price }"/></td>
</tr>
如果不用@ModelAttribute也可以使用model.addAttribute(“item”, itemsCustom)完成数据回显。
2、@ModelAttribute还可以将方法的返回值传到页面
- 在商品查询列表页面,通过商品类型查询商品信息。
- 在controller中定义商品类型查询方法,最终将商品类型传到页面。
//商品分类
@ModelAttribute("itemtypes")
public Map<String, String> getItemTypes(){
Map<String, String> itemTypes = new HashMap<String,String>();
itemTypes.put("101", "数码");
itemTypes.put("102", "母婴");
return itemTypes;
}
页面:
商品类型:
<select name="itemtype">
<c:forEach items="${itemtypes }" var="itemtype">
<option value="${itemtype.key }">${itemtype.value }</option>
</c:forEach>
</select>
简单类型数据回显
- 对于简单数据类型,如:Integer、String、Float等使用Model将传入的参数再放到request域实现显示。
@RequestMapping(value="/editItems",method={RequestMethod.GET})
public String editItems(Model model,Integer id)throws Exception{
//传入的id重新放到request域
model.addAttribute("id", id);
springmvc和struts2的区别
- 1、springmvc基于方法开发的,struts2基于类开发的。
- springmvc将url和controller方法映射。映射成功后springmvc生成一个Handler对象,对象中只包括了一个method。
- 方法执行结束,形参数据销毁。
- springmvc的controller开发类似service开发。
- 2、springmvc可以进行单例开发,并且建议使用单例开发,struts2通过类的成员变量接收参数,无法使用单例,只能使用多例。
- 3、经过实际测试,struts2速度慢,在于使用struts标签,如果使用struts建议使用jstl。
以上是关于Spring MVC入门第3天--注解开发的主要内容,如果未能解决你的问题,请参考以下文章
Spring MVC入门第2天--SpringSpringMVC与MyBatis三大框架整合