在上一篇 Spring+SpringMVC+Mybatis 整合中说到了 SSM 的整合, 并且在其中添加了一个简单的查询功能, 目的只是将整个整合的流程进行一个梳理, 下面在上一篇中工程的基础上再说一些关于 SpringMVC 的 Controller 的一些细节.
首先附上整个项目结构图, 附上整个代码工程的下载地址, 下面所讲到的测试用例都是在下面这个测试项目的基础上进行的.
一, 关于 Controller 的注解形式
1, 使用 @Controller 注解可以实现 Controller 的注解开发, 然后在 springmvc.xml 的配置文件中配置注解扫描器, 就可以使用注解形式进行 Controller 的开发, 下面我们简单使用一个 helloworld 的例子进行说明
1在 springmvc.xml 中配置注解扫描器
其中也当然包含 springmvc 所需要的处理器映射器, 处理器适配器, 视图解析器(这几个组件个概念可以查看 SpringMvc 入门, 其中开篇说到了 SpringMVC 的处理流程和各个组件以及之间的关系), 我们这里直接使用下面的配置方式进行配置
2写一个简单的 helloworld, 在浏览器中请求对应的 Controller, 然后输出在页面上
- package cn.test.ssm.controller;
- import org.springframework.stereotype.Controller;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.Web.servlet.ModelAndView;
- @Controller
- public class HelloWorldController {
- @RequestMapping("/helloWorld.do")
- public ModelAndView helloWorld() throws Exception{
- ModelAndView modelAndView = new ModelAndView();
- modelAndView.addObject("test","HelloSSM");
- modelAndView.setViewName("/WEB-INF/items/hello.jsp");
- return modelAndView;
- }
- }
- <%@ page contentType="text/html;charset=UTF-8" language="java" %>
- <HTML>
- <head>
- <title>$Title$</title>
- </head>
- <body>
测试 Controller
- ${test}
- </body>
- </HTML>
hello.jsp
3然后在地址栏中请求 http://localhost:8080/TestSSM2/helloWorld.do, 输出
二, 关于 RequestMapping
1, 使用不同的处理器映射规则
a, 我们通过 RequestMapping 可以使用不同的处理器映射规则, RequestMapping 注解能够控制 http 请求的路径和方式(get,post......), 在同一个 Controller 中可以写不同的映射方法, 映射浏览器不同的请求业务.
具体的使用方式就是:@RequestMapping(value="/test.do")或 @RequestMapping("/test), 其中 value 的值是数组, 可以将多个 url 映射到同一个方法
b, 下面我们就在上一篇中查询列表的基础上增加查询详细信息的一个功能, 通过 RequestMapping 注解来实现
1首先在 mapper 中将 ProductDemo.xml 中添加查询详细信息的 Sql 配置
- <select id="queryProductInfo" parameterType="java.lang.Integer" resultType="cn.test.ssm.po.ProductExtend">
- SELECT pname,shop_price FROM product WHERE pid = #{id}
- </select>
2在 mapper 接口中添加上面的方法
3在 service 接口中添加相应的方法和方法实现
service 接口
接口实现类
4在 controller 层中加上 queryInfo 方法, 其中使用 RequestMapping 映射了两个不同请求对应的方法实现
- package cn.test.ssm.controller;
- import cn.test.ssm.po.ProductExtend;
- import cn.test.ssm.service.ProductService;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Controller;
- import org.springframework.Web.bind.annotation.RequestMapping;
- import org.springframework.Web.servlet.ModelAndView;
- import java.util.List;
- @Controller
- public class ProductController {
- @Autowired
- private ProductService productService;
- @RequestMapping("/queryList.do")
- public ModelAndView queryList() throws Exception{
- // 从 service 层调用方法
- List<ProductExtend> productExtendList = productService.findProductListByName(null);
- // 返回 ModelandView
- ModelAndView modelAndView = new ModelAndView();
- modelAndView.addObject(productExtendList);
- modelAndView.setViewName("/WEB-INF/items/itemsList.jsp");
- return modelAndView;
- }
- @RequestMapping("/queryInfo.do")
- public ModelAndView queryInfo() throws Exception {
- ProductExtend productExtend = productService.queryProductInfo(1);
- productExtend.setDesc("这是相机");
- ModelAndView modelAndView = new ModelAndView();
- modelAndView.addObject(productExtend);
- modelAndView.setViewName("/WEB-INF/items/editItem.jsp");
- return modelAndView;
- }
- }
5最后在查询列表中点击查询即可查看详细信息
2, 窄化请求映射
a)为了实现不同模块之间的开发, 我们可以进行这样的使用: 在 class 上添加 @RequestMapping(url)指定通用请求前缀, 限制此类下的所有方法请求 url 必须以请求前缀开头, 通过此方法对 url 进行分类管理.
b)如下:@RequestMapping 放在类名上边, 设置请求前缀
- @Controller
- @RequestMapping("/test")
然后在方法名上边设置请求映射 url:
@RequestMapping("/queryItem")
访问地址为: http://localhost:8080/TestSSM2/test/queryList.do
3, 关于 http 请求方式限定
a)限定 POST 方法:@RequestMapping(method = RequestMethod.POST)
如果通过 Get 访问则报错: HTTP Status 405 - Request method 'GET' not supported, 例如
然后访问 http://localhost:8080/TestSSM2/test/queryList.do, 就会是下面的错误
b)限定 GET 方法:@RequestMapping(method = RequestMethod.GET)
如果通过 Post 访问则报错: HTTP Status 405 - Request method 'POST' not supported
c)GET 和 POST 都可以:@RequestMapping(method={RequestMethod.GET,RequestMethod.POST})
三, 关于 Controller 的返回值问题
1, 返回 ModelAndView
a)我们上面编写 Controller 都是以这种方式进行的, 大概就是定义一个 ModelAndView 对象, 然后填充模型 (从数据库中得到的数据) 和逻辑视图(指定的 jsp 等路径), 并返回即可
b)例如
2, 使用 void 类型
a)在 controller 方法形参上可以定义 request 和 response, 使用 request 或 response 指定响应结果:
1使用 request 转向页面: request.getRequestDispatcher("页面路径").forward(request, response);
例如:
- @RequestMapping("/test_void.do")
- public void testVoid(HttpServletRequest request, HttpServletResponse response) throws Exception{
- request.setAttribute("test","返回值为 void 类型的测试");
- request.getRequestDispatcher("/WEB-INF/items/hello.jsp").forward(request,response);
- }
然后输出结果
2通过 response 页面重定向: response.sendRedirect("url"), 实现方式同上
3, 使用 String 作为返回值
a)Controller 中的方法形参为 model, 然后通过形参将数据返回到请求页面上, 最后返回字符串可以指定逻辑视图名(路径信息), 通过视图解析器解析为物理视图地址;
- @RequestMapping("/testString.do")
- public String testString(Model model) throws Exception {
- // 其他进行的操作
- // 通过形参 model 将数据返回到请求页面上 类似于返回 ModelAndView 中的 addObject 方法
- model.addAttribute("testString","testString");
- // 然后返回逻辑视图名, 经过视图解析器解析为相应的 jsp 等路径
- return "test/helloWorld";
- }
b)重定向: Contrller 方法返回结果重定向到一个 url 地址, 但是由于重定向之后原来的 request 中的数据不在, 所以如果要传参数可以 / item/queryItem.action 后边加参数:/test/queryTest?test1Key=test1Value&test2Key=test2Value
c)转发: Controller 中的方法执行后继续执行另一个 controller 方法, 如下信息 modify 提交后转向到信息显示页面, 修改信息的 id 参数可以带到修改方法中.// 结果转发到 update.action,request 可以带过去: return "forward:update.action";forward 方式相当于 "request.getRequestDispatcher().forward(request,response)", 转发后浏览器地址栏还是原来的地址. 转发并没有执行新的 request 和 response, 而是和转发前的请求共用一个 request 和 response. 所以转发前请求的参数在转发后仍然可以读取到.
四, 关于 SpringMVC 的参数绑定问题
1, 参数绑定过程
a)参数绑定: 注解适配器对 RequestMapping 标记的方法进行适配, 将从浏览器中请求的数据 (key/value 或者表单信息) 在方法中的形参会进行参数绑定, 所以在 springmvc 中的参数绑定是通过 Controller 的方法形参进行绑定的.
b)参数绑定所支持的默认参数类型, 可以直接在 Controller 方法上面定义下面类型的形参, 然后在方法体内直接使用
- HttpServletRequest(通过 request 对象获取请求信息)
- HttpServletResponse(通过 response 处理响应信息)
- HTTPSession(通过 session 对象得到 session 中存放的对象)
- Model(通过 model 向页面传递数据, 然后页面通过 ${
- test.XXXX
- }获取 item 对象的属性值. 如同上面 Controller 中方法返回值为 String 的情况)
2,RequestParam 注解使用
a)在没有使用注解的时候, 我们在前端提交参数的 key 名字应该 Controller 中方法的形参相同, 否则在 request 域中无法进行匹配. 当我们需要将 Controller 方法中的形参设置为不一样的参数名时候, 就需要使用这个注解
b)注解简介:@RequestParam 用于绑定单个请求参数.
1value: 参数名字, 即入参的请求参数名字, 如 value="test_id" 表示请求的参数区中的名字为 test_id 的参数的值将传入;
2required: 是否必须传入参数, 默认是 true, 表示请求中一定要有相应的参数, 否则将报 HTTP Status 400 - Required Integer parameter 'XXXX' is not present
3defaultValue: 默认值, 表示如果请求中没有同名参数时的默认值
比如: 形参名称为 id, 但是这里使用 value="test_id" 限定请求的参数名为 test_id, 所以页面传递参数的名必须为 test_id.
注意: 如果请求参数中没有 test_id 将抛出异常: HTTP Status 500 - Required Integer parameter 'test_id' is not present
这里通过 required=true 限定 itest_id 参数为必需传递, 如果不传递则报 400 错误, 可以使用 defaultvalue 设置默认值, 即使 required=true 也可以不传 item_id 参数值
c)下面使用例子来进行说明上面的几点内容
1测试注解
测试工程如同上一篇 SSM 整合中搭建的工程, 其中只有一个功能就是查询列表. 然后我们在本篇最开始的时候介绍 RequestMapping 时候添加了查询详细信息的功能, 但是其中我们没有接收前端传入的数据, 全部都是用的默认值 1, 下面来将这个方法使用 RequestParam 进行改写, 接收前端传入的参数进行查询.
- @RequestMapping("/queryInfo.do")
- public ModelAndView queryInfo(@RequestParam(value = "id") Integer testId) throws Exception {
- // 在没有使用注解的时候, 方法形参中的参数名需要和前端请求的 key 名称一样, 使用之后就可以自定义
- ProductExtend productExtend = productService.queryProductInfo(testId);
- productExtend.setDesc("这是相机");
- ModelAndView modelAndView = new ModelAndView();
- modelAndView.addObject(productExtend);
- modelAndView.setViewName("/WEB-INF/items/editItem.jsp");
- return modelAndView;
- }
然后我们再次进行测试, 首先访问 http://localhost:8080/TestSSM2/test/queryList.do, 然后查看 id=2 的信息
得到下面的结果
在对比 id=1 的时候
2测试 required
在上面的方法中加上
然后进行测试, 输入 http://localhost:8080/TestSSM2/test/queryInfo.do, 不加参数, 则报出下面的错误
3测试 defaultValue
当 required 设置为 true 的时候, 如果没有传入 key/value, 当在 Controller 中设置 defaultValue 的时候也不会报出上面的异常, 在 Controller 方法参数中改成下面这样
然后直接输入 http://localhost:8080/TestSSM2/test/queryInfo.do 不加 id 参数, 还是能够查询到默认的 id=1 的数据
3, 普通 POJO 类型绑定
a)将 pojo 对象中的属性名和传递进来的属性名对应, 如果传进来的参数名称和对象中的属性名称一致则将参数值设置在 pojo 对象中 , 然后在 Contrller 方法定义: 请求的参数名称和 pojo 的属性名称一致, 会自动将请求参数赋值给 pojo 的属性.
b)我们现在在 Controller 中新添加一个下面的方法, 输出从页面上面提交的数据
c)在页面表单中点击提交数据
然后后台中输出
4, 自定义参数绑定
a)定义一个 Date 类型的参数绑定, 首先建议里个转换器的 java 工具类, 用来将 String 转换为 java.util.Date 类型
- package cn.test.ssm.controller.converter;
- import org.springframework.core.convert.converter.Converter;
- import java.text.ParseException;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- public class StringToDateConverter implements Converter<String, Date> {
- @Override
- public Date convert(String s) {
- try {
- SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- return simpleDateFormat.parse(s);
- } catch (ParseException e) {
- e.printStackTrace();
- }
- return null;
- }
- }
然后在 springmvc.xml 中配置上上面的转换器
- <!-- 配置 mvc:annotation 代替基于注解方式的处理器映射器和适配器的配置 -->
- <mvc:annotation-driven conversion-service="converterService"></mvc:annotation-driven>
- <bean id="converterService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
- <!-- 配置转换器 -->
- <property name="converters">
- <list>
- <bean class="cn.test.ssm.controller.converter.StringToDateConverter"></bean>
- </list>
- </property>
- </bean>
最后可以修改一下上面的查看信息方法, 在后台中打印出所有提交的数据, 注意下面的方法中形参 createtime 的类型已经设置为 Date 类型, 在前端页面中也添加上了 createtime 的输入
- @RequestMapping("/printInfo.action")
- public String printInfo(Product product, Date createtime) throws Exception {
- System.out.println("输出信息"+product);
- System.out.println(createtime);
- return "forward:queryList.do";
- }
最后在浏览器中进行如下输入测试, 输入一个日期类型
最后在后台查看打印的信息
5, 自定义包装类型
a)当前天传入的参数比较复杂的时候(比如说涵盖不同数据实体类之间关联的查询或者某个实体类的扩展属性信息), 这个时候我们可以在扩展类中加上额外的属性然后将其作为我们自定义的包装类的属性, 然后将前台页面传入参数的 name 设置为包装类的属性名. 实体类属性(testExtend.name)
b)看下面的例子, 这是一个模糊查询的简单功能实现
1我们首先在实体类 Product 的扩展类 ProductExtend 中添加接受模糊查询参数的一个属性
2然后定义一个包装类型, 其中将上面的扩展类型设置为一个属性
3然后就在 Controller 方法中将自定义的包装类型设置为方法形参, 用以进行参数绑定, 并且调用 service 的方法进行查询
4在前台中查看查询
5在 debug 模式中查看是否接收到前端传入的参数, 发现可以接收到页面传入的参数
并且在调用 service 方法之后的查询结果也为
最终页面显示结果
来源: https://www.cnblogs.com/fsmly/p/10413163.html