概述
目录
前言
一、参数传递
一、View to Controller
二、Controller to View
二、视图解析器
总结
前言
在上一篇博文中,我们学习了Spring MVC的原理、处理流程及其体系结构,完成了请求与处理器之间的映射。今天,我们继续深入学习Spring MVC的一些知识,主要是参数传递(View到Controller、Controller到View)、视图解析器。
一、参数传递
参数传递在项目中是必不可少的,只要是动态页面,肯定会涉及数据传递,想都不用想。接下来我们就学习一下View与Controller之间的参数传递。
一、View to Controller
页面往控制器传参,最粗暴的方式是直接入参,改造我们之前的WelcomeController.java如下:
package com.smbms.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class WelcomeController{
@RequestMapping(value = "/welcome")
public String welcome(@RequestParam String name){
//如果方法的入参名与传来的参数名一致,可省略@RequestParam注解
System.out.println(name);
return "welcome";
}
}
修改我们的main.jsp如下,多传一个参数:
<%@ page language="java" import="java.util.*" contentType="text/html; charset=UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<a href="../welcome?name=MVC大爷">去拜访Spring MVC大爷</a>
<%--因为是在jsp目录下,所以先返回上一级目录,回到根目录--%>
</body>
</html>
运行项目,会在控制台输出“MVC大爷”字符串。
我们在以上示例中使用了@RequestParam在处理方法的的入参进行了标注,说明这是一个从View传来的参数(这里只简单的使用此注解,不进行任何设置,下文会讲述)。这种情况下我们必须保证方法的入参名与View传来的参数名一致,可以发现,如果我们传来的参数名不是name而是userName,那么会报错400,错误信息是“请求中参数name不存在”。但是在实际开发中,由于业务需求,可能有些参数并不是必需的,那么我们该如何解决这个问题呢?
这就需要详细了解@RequestMapping和@RequestParam,两者结合使用,实现灵活的参数传递。
1、@RequestMapping
通过之前的学习,我们知道@RequestMapping的主要职责就是将不同的请求映射到对应的控制器处理方法。HTTP请求信息除了最基本的URL地址之外,还包括请求方法、HTTP协议及版本、报文头、报文体等,所以我们除了使用URL进行映射,还可以应用其他的HTTP信息来完成更精确的映射。
使用@RequestMapping完成映射,具体包含4个方面的信息:请求URL、请求参数、请求方法、请求头。
1)通过请求URL进行映射
我们之前的写法就是通过请求URL映射,即@RequestMapping("/welcome"),等同于@RequestMapping(value="/welcome")
此外,拓展一点东西,@RequestMapping可以在控制器的类定义处指定URL,即是这种的:
package com.smbms.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
@RequestMapping("/user")
public class WelcomeController{
@RequestMapping(value = "/welcome")
public String welcome(@RequestParam String name){
//如果方法的入参名与传来的参数名一致,可省略@RequestParam注解
System.out.println(name);
return "welcome";
}
}
在以上代码中,我们使用@RequestMapping注解在WelcomeController的类定义处进行了标注,定义URL为“/user”,此时这个URL相对于Web应用的部署路径,而welcome()方法处指定的URL则相对于类定义处的URL而言的,什么意思呢?就是说,最后我们发过来的请求得是"http://localhost:8088/工程名/user/welcome"。所以修改main.jsp:
<%@ page language="java" import="java.util.*" contentType="text/html; charset=UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<a href="../user/welcome?name=MVC大爷">去拜访Spring MVC大爷</a>
<%--因为是在jsp目录下,所以先返回上一级目录,回到根目录--%>
</body>
</html>
运行,依然可以访问。
这种是很常用的方式,在不同的Controller的类定义处指定相应的@RequestMapping,以便区分不同的请求,不易出错,也更规范。比如/user/add,/user/update,/user/delete,等等。
此外,分析源码可以看出来,@RequestMapping的返回值是一个String类型的数组,也就是说,我们还可以这样写:
@RequestMapping({"/index","/welcome"})
这句话说明URL请求为/index的、/welcome的都可以进入该处理方法。
2)通过请求参数、请求方法、请求URL进行映射
@RequestMapping除了可以使用请求URL进行映射请求之外,还可以使用请求参数、请求方法加以限制,通过多条件可以让请求映射更加准确。改造WelcomeController.java,如下:
@RequestMapping(value = "/welcome",method = RequestMethod.GET,params = "name")
public String welcome(String name){
//如果方法的入参名与传来的参数名一致,可省略@RequestParam注解
System.out.println(name);
// model.addAttribute("userName",name);
return "welcome";
}
在上述代码中,@RequestMapping的value表示请求的URL,method表示请求方法,此次设置了GET,所以POST请求是进不来的,param表示请求参数,此处我们的参数名为name。运行我们的项目,同样可以访问此方法,下面分析一下:
首先,value(请求路径)匹配,method(超链接是GET请求)匹配,请求参数(name)也匹配,所以进入了处理方法。
下面分析几种错误情况。
如果将main.jsp页面的参数改为?userName=MVC大爷,则访问会出现400,控制台也会报出异常;
如果将处理方法的入参改为String userName,即这种的,与传参名不匹配:
@RequestMapping(value = "/welcome",method = RequestMethod.GET,params = "name")
public String welcome(String userName){
//如果方法的入参名与传来的参数名一致,可省略@RequestParam注解
System.out.println(userName);
// model.addAttribute("userName",name);
return "welcome";
}
此时不会报错,只是后台会获取不到参数值,输出null。所以我们若选择方法参数直接入参,方法入参名必须与请求中的参数名一致。
简单的学习@RequestMapping之后,一开始的问题依然没有解决,如果没有传递参数依然报错。接下来学习@RequestParam,两者结合完成灵活的请求传参。
2、@RequestParam
在方法入参处使用@RequestParam注解指定其对应的请求参数。@RequestParam有以下三个参数:
- value:参数名
- required:是否是必需的,默认为true,表示请求中必须包含对应的参数名,否则抛出异常
- defaultValue:默认参数名,不推荐使用,比较混乱
接下来就改造以上的示例:
@RequestMapping(value = "/welcome",method = RequestMethod.GET)
public String welcome(@RequestParam(value = "name",required = false) String userName){
//如果方法的入参名与传来的参数名一致,可省略@RequestParam注解
System.out.println(userName);
// model.addAttribute("userName",name);
return "welcome";
}
测试运行,没问题。
这种情况下不要求方法的入参名与请求中的参数名一致,保证@RequestParam注解能够将二者关联上即可。但是为了规范起见,建议一致。
二、Controller to View
了解完从View到Controller的传参,接下来学习Controller到View的传参。
这就需要模型数据的处理了,对于MVC框架来说,模型数据是最重要的一部分,因为控制层是为了产生模型数据(Model),而视图(View)最终也是为了渲染模型数据并进行输出。那么如何将模型数据传递给视图?Spring MVC提供了多种方式,介绍一下比较常用的:
1、ModelAndView
顾名思义,控制器的处理方法的返回值若是ModelAndView,即既包含Model,也包含View。那么拿到该对象之后,Spring MVC就可以使用视图对模型数据进行渲染。改造以上示例,拿到参数之后,再返回给页面:
@RequestMapping(value = "/welcome")
//表示与哪个URL请求相对应
public ModelAndView welcome(@RequestParam(value = "name",required = true) String name){
System.out.println(name);
ModelAndView modelAndView=new ModelAndView();
modelAndView.addObject("userName",name);
modelAndView.setViewName("welcome");
return modelAndView;
}
通过以上代码可以看出在welcome处理方法中,返回了ModelAndView对象,并通过addObject()方法添加模型数据,通过setViewName()方法设置逻辑视图名。
ModelAndView对象的常用方法如下:
- addObject(String attributeName,Object attributeValue); 该方法的第一个参数为key值,第二个参数为key对应的value。其中key的值可以随便指定,只要保证在该Model内唯一即可。
- addAllObjects(Map<String,?> modelMap); 从此方法可以看出,模型数据也是一个Map对象,我们可以添加Map到Model。
- setView(View view); 指定一个具体的视图对象。
- setViewName(String viewName); 指定一个逻辑视图名。
然后在welcome.jsp页面使用EL表达式展现从Controller返回的ModelAndView对象中的Model(至于返回其他格式的数据,在以后介绍)。
<%@ page language="java" import="java.util.*" contentType="text/html; charset=UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>Hello World</h1>
<p>来自<span style="font-weight: bold;color: red">${userName}</span>的回复</p>
</body>
</html>
我们在main.jsp页面传入参数“张三”,运行结果如下:
2、Model
除了可以使用ModelAndView对象来返回模型数据外,我们还可以使用Spring MVC提供的Model对象来完成模型数据的传递。
其实,Spring MVC在调用方法前会创建一个隐含的模型对象,作为模型数据的存储容器,一般称为“隐含模型”。若处理方法的入参为Model类型,Spring MVC会将这个隐含模型的引用传递给这些入参,这样开发者就可以通过一个Model类型的入参,访问到模型中的所有数据,当然也可以做修改、添加等。继续修改上面的示例,通过传入Model参数的方式完成参数返回。
@RequestMapping(value = "/welcome",method = RequestMethod.GET)
public String welcome(@RequestParam(value = "name",required = false) String name,Model model){
System.out.println(name);
model.addAttribute("userName",name);
return "welcome";
}
其他的都不需要做修改,运行项目是没问题的。此处直接使用Model对象入参,把需要传递的模型数据放入Model,返回字符串类型的逻辑视图名即可。
不管是ModelAndView还是Model,它们的用法很类似,运用起来也比较灵活。Model对象的addAttribute()方法和ModelView对象的添加模型数据的方法的用法是一样的,即Model对象也是一个Map类型的数据结构。
另外,对于Model对象的addAttribute()方法,key的指定并不是必须的,可以不指定。那么既然它是一个Map结构,如果不指定它会以什么为key呢?这种情况下,会默认使用对象的类型作为key。比如上面的示例中,我们的name是字符串类型的,我们如果不指定key,则“string”就是它的key,访问的时候即是${string}。不过个人不推荐此用法,容易混乱。
除此之外,addAttribute()方法可以添加任何数据类型,JavaBean、List、Map等都可以。
3、Map
我们之前说过,Spring MVC的Model其实就是一个Map数据结构,故我们使用Map作为处理方法的入参也是可行的。
@RequestMapping(value = "/welcome",method = RequestMethod.GET)
public String welcome(@RequestParam(value = "name",required = false) String name,Map<String,Object> model){
//如果方法的入参名与传来的参数名一致,可省略@RequestParam注解
System.out.println(name);
model.put("userName",name);
return "welcome";
}
这种方式依然可以达到我们想要的结果。
Spring MVC控制器的处理方法若有Map或者Model类型的入参,就会将请求内的隐含模型对象传递给这些入参,因此可以在方法体内对模型数据进行读写。推荐使用Model。
4、@ModelAttribute
若希望将入参的数据对象放入数据模型中,就需要在相应入参前使用此注解。在以后的博文讲解,此处知道有这么个东西即可。
5、@SessionAttributes
此注解可以将模型中的属性存入HttpSession中,以便在一次会话中共享该属性。以后会讲解。
二、视图解析器
请求处理方法执行完成之后,最终返回一个ModelAndView对象。对于那些返回String类型的处理方法,Spring MVC内部也会将它们装配成一个ModelAndView对象,它包含逻辑视图名和数据模型,那么接下来的工作就交给视图解析器(ViewResolver)了。
ViewResolver是Spring MVC处理视图的重要接口,通过它可以将控制器返回的逻辑视图名解析成一个真正的视图对象。
Spring MVC默认提供了多种视图解析器,所有的视图解析器都实现了ViewResolver接口,如图:
对于JSP这种常见的视图技术,通常使用InternalResourceViewResolver作为视图解析器。那么它是如何工作的?
InternalResourceViewResolver是最常用的视图解析器,通常用于查找JSP和JSTL等视图。通过源码就可以看出,它是URLBasedViewResolver的子类,会将返回的逻辑视图名都解析成InternalResourceViewResolver对象,该对象会把Controller的处理方法返回的模型属性都放在对应的请求作用域中,然后通过RequestDispatcher在服务器端把请求转发到目标URL。
我们只需要在配置文件中进行如下配置:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
如果控制器的处理方法返回字符串“welcome”,那么经过此视图解析器的解析,即为:/jsp/welcome.jsp。
总结
最后,结合前一篇博文,我们对Spring MVC做一下简单的总结:
- MVC设计模式在各种成熟框架中都得到了良好的运用,它将Model、View、Controller三层清晰地分开,搭建一个松耦合、高可用性、高可适用性的完美架构。
- Spring MVC框架是典型的MVC框架,是一个结构清晰的JSP Model2实现。它基于Servlet,核心是DispatcherServlet。
- Spring MVC的处理器映射(HandlerMapping)可配置为注解式的处理器,只需配置<mvc:annotation-driven />标签即可。
- Spring MVC的控制器的处理方法返回的ModelAndView对象,包括模型数据和视图信息。
- Spring MVC通过视图解析器来完成视图解析工作,把控制器的处理方法返回的逻辑视图名解析成一个真正的视图对象。
对于Spring MVC框架的更多内容,以及控制器返回JSON数据、HTML字符串等,会在以后的博文慢慢更新。
最后
以上就是感性抽屉为你收集整理的Spring MVC体系结构(二)前言一、参数传递二、视图解析器总结的全部内容,希望文章能够帮你解决Spring MVC体系结构(二)前言一、参数传递二、视图解析器总结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复