public interface HandlerAdapter {
boolean supports(Object handler);
ModelAndView handle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception;
long getLastModified(HttpServletRequest request, Object handler);
public boolean supports(Object handler) {
return getMethodResolver(handle).hasHandlerMethods();
public final boolean hasHandlerMethods() {
return !this.handlerMethods.isEmpty();
对于每一个处理器适配器,都要实现HandlerAdapter接口,由上面源码可以看到,supports()方法会去判断Handler处理器中是否至少存在一个实现方法,当存在至少一个时,则表明支持。
知道了处理器适配器是根据存在方法判断是否支持Handler处理器后,最后一个问题,处理器映射器是如何判断使用哪一个Handler处理器的呢?在注解的处理器映射器中,答案是使用@RequestMapping注解。
@RequestMapping
@RequestMapping注解的作用是将用户的HTTP请求,映射到可以处理该请求的Handler处理器中,即制定了某一个Handler可以处理那些URL请求。@RequestMapping注解提供的配置参数有很多,例如name属性指定映射器的名称,method属性指定请求方法的类型,params属性指定请求的参数,value和path属性指定映射路径等。这里我们拿value属性举例,因为它是一个十分常用到的属性配置。@RequestMapping注解可以放在Handler处理器类上,也可以放在类中的具体实现方法上,或者同时放在类与方法上都可以。
当@RequestMapping注解放到处理器类上时,表示的是一个前置请求路径,例如配置@RequestMapping(value=“/user”),那么我们就可以通过请求路径:(这是我的配置路径,以“.action”作为后缀)
http://localhost:8080/SpringMVC__Project/user.action
映射到该Handler进行处理。如果只把注解放在方法上,效果一样。而如果在Handler类和具体实现方法上都放置@RequestMapping注解,那么方法上的注解就会变成下一级URL路径。例如这种情况:
@RequestMapping(“/user”)
Handler类 {
@RequestMapping(“/findUser“)
那么映射的请求路径就变为
http://localhost:8080/SpringMVC__Project/user/findUser.action
当@RequestMapping注解中只配置一个请求映射属性时,value字样可以省略。
前面说到,使用了annotation-driven标签配置的处理器映射器和适配器,还提供了数据绑定功能,即在HTTP请求路径中,可以进行数据参数的绑定。
处理器参数绑定
用户在前端页面发出一些请求时,通常会携带一些参数到后端进行处理,例如用户登录,注册和查询等操作。在Spring MVC中,数据参数绑定组件由处理器映射器负责,将HTTP请求中携带的数据参数绑定到Controller处理器中,接收参数的自然就是处理器类里具体实现方法的形参。在处理器类实现方法支持的参数绑定有HttpServletRequest、HttpServletResponse、HttpSession还有Model/ModelMap。这里使用Model/ModelMap举例,它可以实现视图和模型的分离,在后台完成数据的操作后,通过model对象将数据传达到前端页面。
基本类型的数据参数绑定
基本类型的数据绑定,即int、long、float、double、char,boolean等。来看一个例子,假如我们要查询id为1的用户信息,在处理器类中应该怎么写:
@Controller
@RequestMapping("/user")
public class SimpleParamBind {
private UserService userService = new UserService();
@RequestMapping(value="/queryUserById", method= {RequestMethod.GET})
public String queryUserById(Integer id, Model model) throws Exception {
// 查询用户信息
List<User> userList = userService.queryUserById(id);
// 通过形参中的model将数据传到页面
model.addAttribute("userList", userList);
return "users/userList"; // 返回视图
@RequestMapping注解中除了配置了URL映射路径,还配置了请求类型要为GET类型,也就是说该处理器方法只接受请求GET方法类型的HTTP请求。在进行queryUserById()方法进行用户查询时,我们为其提供一个参数Integer id,标识用户id,第19行并根据传入的id查询用户信息。第11行,将查询结果userList,通过model对象的addAttribute()方法传到前端视图中,最后返回逻辑视图名。按照这样的配置,在URL请求中我们可以传入id参数:
http://localhost:8080/SpringMVC__Project/user/queryUserById.action?id=1
查询id为1的用户信息:
@RequestParam注解自定义参数别名
在URL请求中,假如我们不给id参数,或者参数名字不为“id”,都会导致出错,例如我们在请求路径中把参数名改为userId的话:
就会出错,因为参数名不一致嘛。不过我们可以使用@RequestParam注解,来自定义基本类型的参数绑定,也就是不需要 URL请求中参数名与Controller控制器中具体方法的参数名一致。@RequestParam注解的具体使用是在Controller方法中:
@Controller
@RequestMapping("/user")
public class SimpleParamBind {
private UserService userService = new UserService();
@RequestMapping(value="/queryUserById", method= {RequestMethod.GET})
public String queryUserById(@RequestParam(value="userId")Integer id, Model model) throws Exception {
// 查询用户信息
List<User> userList = userService.queryUserById(id);
// 通过形参中的model将数据传到页面
model.addAttribute("userList", userList);
return "users/userList"; // 返回视图
第7行,我们在queryUserById()方法的参数id前加上@RequestParam注解,value=“userId”,这样就可以在URL请求中可以接收名为“userId”的参数传递:
如果我们在Controller方法中设置了参数传递,但在HTTP请求里又没有给予参数,显然这样会出错:
如果我们想在参数为空时,给予参数一个默认值,可以在@RequestParam注解中设置defaultValue属性,来指定某一个参数的默认值:
@Controller
@RequestMapping("/user")
public class SimpleParamBind {
private UserService userService = new UserService();
@RequestMapping(value="/queryUserById", method= {RequestMethod.GET})
public String queryUserById(@RequestParam(value="userId,
defaultValue="2")Integer id, Model model) throws Exception {
// 查询用户信息
List<User> userList = userService.queryUserById(id);
// 通过形参中的model将数据传到页面
model.addAttribute("userList", userList);
return "users/userList"; // 返回视图
第8行,我们设置defaultValue=“2”,为id参数设置一个默认值,当HTTP请求中没有id参数传递时,则默认查询id为2的用户信息:
除了基本类型的数据参数绑定外,还可以进行JavaBean或者包装类型的数据绑定,例如我们想要根据用户姓名和性别两个条件来查询数据表中这名用户的信息,怎么做?
包装类型的数据参数绑定
来看一个简单的测试用例,在前端页面传递Java包装类参数,与Controller处理器绑定参数后,根据参数条件查询用户信息。首先简单地创建一个查询包装类:
public class User {
private Integer id;
private String username;
private String password;
private String gender;
private String email;
private String province;
private String city;
private Date birthday;
private Integer age;
// 省略get()和set()方法
Service接口与实现类
接下来看具体的数据处理部分方法,在Spring MVC部分的日志中,我没有把它和MyBatis整合,打算放到以后做一个简单项目例子时在整合讲解,Spring MVC部分的日志我们就专心于Spring MVC,所以在数据处理部分,我们模拟数据库的数据初始化与查询。首先创建数据处理部分的接口:
package com.mvc.service;
import java.util.List;
import com.mvc.model.User;
public interface UserService {
public List<User> queryUserList(); // 查询所有顾客信息
public List<User> queryUserById(Integer id); // id查找
public List<User> queryUserByCondition(User user); // 条件查找
接口中定义了三个方法,分别查询所有的顾客用户信息,按id查找(上面的实现)以及接下来要用到的,根据包装类信息查找。来看queryUserByCondition()方法,根据Java包装类查询的实现:
@Override
public List<User> queryUserByCondition(User user) {
init();
String name = user.getUsername();
String gender = user.getGender();
List<User> queryList = new ArrayList<User>();
User u = null;
for(int i=0; i<userList.size(); i++) {
u = userList.get(i);
if((!name.equals("") && u.getUsername().contains(name)) &&
(!gender.equals("") && u.getGender().contains(gender))) {
queryList.add(u);
return queryList;
init()方法初始化用户数据,然后根据传入的参数user对象,首先获取姓名和性别属性,接着61到67行逐一判断,当条件不为空且在数据表中找到对应的用户信息,第65行就把该信息保存到查询结果集合中,最后返回出去。
Controller处理器类
数据处理部分完成,回到Controller处理器类,它负责调用数据处理方法,完成业务逻辑,也就是将前端页面发来的请求user对象参数,传递到数据处理方法中,进行条件查询:
package com.mvc.controller;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import com.mvc.model.User;
import com.mvc.service.UserServiceImpl;
@Controller
@RequestMapping("user")
public class PackParamBind {
private UserServiceImpl userService = new UserServiceImpl();
@RequestMapping("findUserByCondition")
public String queryUserByCondition(Model model, User user) {
List<User> userList = null;
if(user == null || (user.getUsername() == null && user.getGender() == null)) {
// 如果查询框中两个查询条件都为空,则默认查询所有顾客数据
userList = userService.queryUserList();
} else {
// 否则进行条件查询
userList = userService.queryUserByCondition(user);
// model数据传到页面
model.addAttribute("userList", userList);
return "users/queryUser"; // 返回视图
可以看到,具体方法中需要User类的对象作为传入参数,如果查询条件为空,则进行所有数据的查询,当查询条件不为空,调用方法进行条件查询。第28行调用addAttribute()方法,将查询结果集合userList传递到视图中进行视图渲染,最后返回的便是要使用的视图逻辑名。
最后来看看我们的视图部分,接收到后端传来的数据,就要进行数据填充,完成视图渲染:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>用户查询列表</title>
</head>
<form action="findUserByCondition.action" method="post">
用户名:<input type="text" name="username" />
性别:<input type="text" name="gender" />
<input type="submit" value="查找" />
</form>
<h2>搜索结果</h2>
<table width="300px;" border=1>
<td>顾客名</td>
<td>性别</td>
<td>电子邮箱</td>
<td>省会</td>
<td>城市</td>
<c:forEach items="${userList }" var="user">
<td>${user.username }</td>
<td>${user.gender }</td>
<td>${user.email }</td>
<td>${user.province }</td>
<td>${user.city }</td>
</c:forEach>
</table>
</body>
</html>
第10行,form表单中的action制定了该页面请求地址要为findUserByCondition.action,即对应了我们Controller处理器类中的具体方法名。第11行和12行用户名文本框和性别文本框的name属性分别为username和gender,对应Java包装类中的属性,这样才能被处理器适配器解析,为其创建对应的Java实体类,通过set()方法将数据绑定到实体类的对象中进行参数传递。最后来看看运行结果:
当搜索框中没有填入查询条件时,默认查询所有用户的信息。当填入查询条件后,就调用条件查询的方法:
完整实现已上传GitHub:
https://github.com/justinzengtm/SSM-Framework/tree/master/SpringMVC_Project
文章目录背景正文直接把表单的参数写在Controller相应的方法的形参中通过HttpServletRequest接收,post方式和get方式都可以。通过一个bean来接收,post方式和get方式都可以。(不推荐)用request.getQueryString() 获取spring MVC get请求的参数,只适用get请求提问
在使用REST接口的时候,需要传递多个参数,遵循REST的...
@RequestMapping("/users/{username}")
@ResponseBody
public String getUser(@PathVariable String username)
function abolishTransportOrder(id){
var url = contextPath_js + "/pg/sale/distribContract/abolishTransportOrder.pfv?contra...
遇到的问题
1.导入json依赖,从网上看到好多都是说导入json-lib-2.4,还因为没有加jdk15报错,但是这些我都加上了,还是报错,打开Mavenlibrary,发现提示jar has no source attachment 搜了好久都没解决,后来看到一篇博客说 jdk7以及7以上都已经不用json-lib了,目前有好多j好用有快速的json库,我用的fa...
前段时间开发的的一个接口是个下载Excel模板的接口,本来是想放在公司的资源服务器上面,后来听组长说正在数据量小并且文件很小的东西可以维护在自己项目中的本地资源,然而问题出现了,我是直接给前端返回一个指向静态资源的URL地址,但是我在dev环境 是dev路径 到test环境还要修改,到生产还要修改,这样每次发版每次修改都很麻烦,今天有时间来看下这个问题,想到了个思路【前端每次请求的时候 我...
原因是 和下面的 post 方法 url 冲突了
@ApiOperation(value = "用户详情", notes = "用户详情", httpMethod = "GET", produces = MediaType.APPLICATION_JSON_VALUE)
@GetMapping(value = "/{p
springmvc 如何通过请求url找到具体的controller方法1.前言2.实例代码3.Springmvc获取具体handler的流程
这是本人的处女作,用来记录在工作中遇到的各种问题和解决办法,同时也记录自己学习的点点滴滴,内容如果有瑕疵,请在评论区告知,本人会尽快修改,谢谢。
2.实例代码
先上实例代码:
@RestController
public class DomeController {
@RequestMapping("/hello")
public Strin
序:先贴一张SpringMVC整体的框架原理图
此文主要描述Spring在响应请求的时候是如何根据URL找到对应的处理方法(Handler Method)的。即上图中的第二步(版本号:4.3.4.RELEASE)。
读源码最好的方法是一边调试一边理解,直接靠读很可能最后的结果就是能看懂每行甚至每个方法的意思,但是一串联起来就完全懵圈了,至少我是这个样子的。
1,那么既然要调试
这几天一直有个问题困扰着我,我是做网站后端的,用的是mvc,前端的技术我可能不太懂,但是能看个大概是什么意思吧,页面出完后我开始套页,前端做的告诉我的是登录验证成功后用json返回一个0,否则返回1,前台用了kissy框架,我看了一下,如果验证成功kissy能自动帮我跳页,拿来前端的东西,配了一下样式,script什么的,然后启动网页,随便输入用户名和密码,点击登录,它返回一个这样的url:htt
SpringMVC支持URL编码的参数(也就是键值编码的方式)自动注入Java对象.例如id=123&name=weber, 我们有一个Java对象 User user,具有id和name属性,
那么SpringMVC会自动吧URL参数注入user对象中.
这里需要注意的是,不仅get请求方式可以,enctype="application/x-www-form-urlencoded