博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring MVC总结
阅读量:3949 次
发布时间:2019-05-24

本文共 10710 字,大约阅读时间需要 35 分钟。

1.理论

1.1执行过程及原理

在这里插入图片描述

简要分析执行流程

  • 1.DispatcherServlet表示前置控制器,是整个SpringMVC的控制中心。用户发出请求,
    • DispatcherServlet接收请求并拦截请求。
      我们假设请求的url为 : http://localhost:8080/hello
      如上url拆分成三部分:
      http://localhost:8080服务器域名
      SpringMVC部署在服务器上的web站点
      hello表示控制器
      通过分析,如上url表示为:请求位于服务器localhost:8080上的SpringMVC站点的hello控制器。
  • 2.HandlerMapping为处理器映射。DispatcherServlet调用HandlerMapping,HandlerMapping根据请求url查找Handler。
  • 3.HandlerExecution表示具体的Handler,其主要作用是根据url查找控制器,如上url被查找控制器为:hello。
  • 4.HandlerExecution将解析后的信息传递给DispatcherServlet,如解析控制器映射等。
  • 5.HandlerAdapter表示处理器适配器,其按照特定的规则去执行Handler。
  • 6.Handler让具体的Controller执行。
  • 7.Controller将具体的执行信息返回给HandlerAdapter,如ModelAndView。
  • 8.HandlerAdapter将视图逻辑名或模型传递给DispatcherServlet。
  • 9.DispatcherServlet调用视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图名。
  • 10.视图解析器将解析的逻辑视图名传给DispatcherServlet。
  • 11.DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图。
  • 12.最终视图呈现给用户。

2.Spring MVC组件

1、前端控制器DispatcherServlet。

2、处理器映射器HandlerMapping。
3、处理器Handler。
4、处理器适配器HandlerAdapter
5、视图解析器 View resolver。

3.请求参数绑定

而在springmvc中,接收页面提交的数据是通过方法形参来接收的。从客户端请求的key/value数据,经过参数绑定,将key/value数据绑定到controller方法的形参上,然后就可以在controller中使用该参数了。来看一下这个过程:

总结这个还是以需求为例吧,这样比较容易理解,假设现在有个需求:根据商品的id来修改对应点商品信息。所以前台页面肯定要传进来该商品的id,然后springmvc的controller进行处理,返回一个修改商品信息的页面。关于前台页面的东西都很简单,我就不贴代码了,主要部分截个图,具体的代码在文章最后有下载地址。
  前台页面通过url将参数传递过来,请求的是editItems.action。
  在这里插入图片描述
下面写controller中的editItems方法:

@RequestMapping("/editItems")public String editItems(Model model, Integer id) throws Exception {
//根据id查询对应的Items ItemsCustom itemsCustom = itemsService.findItemsById(id); model.addAttribute("itemsCustom", itemsCustom); //通过形参中的model将model数据传到页面 //相当于modelAndView.addObject方法 return "/WEB-INF/jsp/items/editItems.jsp";}

这是个很简单的demo,从上面的代码中可以看出model可以直接作为参数,springmvc默认会绑定它,然后使用model将查询到的数据放到request域中,这样就可以在前台页面取出该数据了。

  要注意一点的是,简单类型的绑定中,方法形参中的参数名要和前台传进来的名一样才能完成参数的绑定。那有人要问了,如果有特殊需求(比如更好的可读性?),这里定义的参数名就是不一样,那咋整呢?有解决办法么?有!我们可以使用注解@RequestParam对简单的类型进行参数绑定,如下:
  在这里插入图片描述
    所以说,如果不使用@RequestParam,要求request传入参数名称和controller方法的形参名称一致,方可绑定成功。如果使用@RequestParam,不用限制request传入参数名称和controller方法的形参名称一致。通过@RequestParam中的required属性指定参数是否必须要传入,如果设置为true,没有传入参数就会报错。

3.1pojo类绑定

3.1.1 普通pojo类型

再来总结下pojo类型的绑定,继续上面的案例,当页面展示了商品详细信息后,我做了修改,然后点击提交,后台应该将我提交的这些参数全部更新到数据库的items表中,也就是说,我提交的这些参数要绑定到Items对象或者其扩展对象中。先看一下Items中都有哪些属性:

在这里插入图片描述
 可以看到,有各种类型的属性,当我们提交后,要将这些属性全部封装到一个pojo中,然后去更新数据库。
  绑定很简单,对于基本类型,要求页面中input标签的name属性值和controller的pojo形参中的属性名称一致,即可将页面中数据绑定到pojo。也就是说前台页面传进来的name要和要封装的pojo属性名一模一样,然后就可以将该pojo作为形参放到controller的方法中,如下:
在这里插入图片描述
  这样就能将前台表单传进来的不同属性值封装到ItemsCustom中了。但是运行一下就会发现报错,报错的信息是无法将String类型转换成java.util.Date类型,因为上面Items中有一个属性是Date类型的createtime。这就需要我们自己定义转换器了,这也是这部分的重点,下面我们自己定义一个日期转换器:

//需要实现Converter接口,这里是将String类型转换成Date类型public class CustomDateConverter implements Converter
{
@Override public Date convert(String source) {
//实现 将日期串转成日期类型(格式是yyyy-MM-dd HH:mm:ss) SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); try {
//转成直接返回 return simpleDateFormat.parse(source); } catch (ParseException e) {
// TODO Auto-generated catch block e.printStackTrace(); } //如果参数绑定失败返回null return null; }}

定义好了转换器后,需要在springmvc.xml中进行如下配置: 在这里插入图片描述  现在就可以了,springmvc就能根据这个转换器将String类型正确转换成Date类型,然后封装到ItemsCustom中去了。

  这里说一个小小的插曲:修改商品详细信息后提交,可能会有中文乱码问题,表达提交都是post方式,springmvc中关于post方式的中文乱码问题可以在web.xml中配置一个过滤器来解决,如下:

//SpringMVC的乱码过滤器
encoding
org.springframework.web.filter.CharacterEncodingFilter
encoding
utf-8
encoding
//1.当配置
/
的时候,它会匹配到路径型的url,就不会匹配到模式为.jsp型的url
/
//2.当配置
/*
的时候,它就会匹配到所有类型的url,包括路径型的,有各种后缀的等等
/*
//由于jsp很少使用了推荐第一种
/
3.1.2 包装的pojo类型

这个包装类型pojo与上面普通的pojo有啥区别呢?包装类型pojo指的是pojo中有另一个也是pojo的属性,即pojo套pojo,为什么会设计这种pojo呢?在前面的博文中我也有提到,这种组合的设计方法对于后期程序的扩展很有用,比如复杂的查询条件就需要包装到这种包装类型中。

  那么该如何绑定呢?有两个思路:

1.在形参中添加HttpServletRequest request参数,通过request接收查询条件参数。2.在形参中让包装类型的pojo接收查询条件参数。

第一种方式就跟原始servlet差不多

@RequestMapping("/itemEdit")    public String itemEdit(HttpServletRequest request, Model model) {
//从Request中取id String strId = request.getParameter("id"); Integer id = null; //如果id有值则转换成int类型 if (strId != null && !"".equals(strId)) {
id = new Integer(strId); } else {
//出错 return null; } Items items = itemService.getItemById(id); //创建ModelAndView //ModelAndView modelAndView = new ModelAndView(); //向jsp传递数据 //modelAndView.addObject("item", items); model.addAttribute("item", items); //设置跳转的jsp页面 //modelAndView.setViewName("editItem"); //return modelAndView; return "editItem"; }

使用第二种方法,我们传进来一个包装类型的pojo。看一下这个包装类型的pojo:

public class Account implements Serializable {
private Integer id; private String name; private Float money; //Addres是包装的pojo类,内有provinceName,cityName两个属性 private Address address; ...get/set/toString/构造函数.../*- - - - - - - - - - - - - - - - - - *///Addres类在这public class Address implements Serializable {
private String provinceName; private String cityName; ...get/set/toString/构造函数...

这个包装pojo中还有一个Address 类,这个类继承了Serializable 类,并且用来扩展与Account 相关信息。所以这个Address 中有provinceName属性和cityName属性,假如我们要想将前台传进来的属性封装到Address 中,该如何传入呢?这就是包装类型的pojo参数绑定问题。

账户名称:
账户金额:
账户省份:
账户城市:

控制器代码

@Controller@RequestMapping("/account")public class AccountController {
/** 保存账户 */ @RequestMapping("saveAccount") public String saveAccount(Account account) {
System.out.println(account); return "success"; }}

3.2 list绑定和map绑定

通常在需要批量提交数据时,将提交的数据绑定到list中

public class User implements Serializable {
private String username; private String password; private Integer age; //list绑定 private List
accounts; //map绑定 private Map
accountMap; ... get/set/toString ...

控制器代码

@Controller@RequestMapping("/account")public class AccountController {
/** 更新账户 */ @RequestMapping("/updateAccount") public String updateAccount(User user) {
System.out.println(user); return "success"; }}

js代码

用户名称:
用户密码:
用户年龄:
<%-- List --%> 账户1名称:
账户1金额:
账户1省份:
账户1城市:
<%-- Map --%> 账户2名称:
账户2金额:
账户2省份:
账户2城市:

HttpSession绑定,HttpServletResponse绑定,HttpServletRequest绑定等等

js核心代码

<%-- 原始ServletAPI作为控制器参数 --%>测试访问ServletAPI

控制器代码

@Controller@RequestMapping("/account")public class AccountController {
/** 测试访问testServletAPI */ @RequestMapping("/testServletAPI") public String testServletAPI(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
System.out.println(request); System.out.println(response); System.out.println(session); return "success"; }}

4.常用注解

@Controller

@Controller注解类型用于声明Spring类的实例是一个控制器(在讲IOC时还提到了另外3个注解);

@RequestMapping

@RequestMapping注解用于映射url到控制器类或一个特定的处理程序方法。可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。

  1. @RequestMapping(value={"/get","/fetch"} )即 /get或/fetch都会映射到该方法上。
    method:指定请求的method类型, GET、POST、PUT、DELETE等;
  2. @RequestMapping(value="/get/{bookid}",method={RequestMethod.GET,RequestMethod.POST})
    params:指定request中必须包含某些参数值是,才让该方法处理。
  3. @RequestMapping(params=“action=del”),请求参数包含“action=del”,如:http://localhost:8080/book?action=del
    headers:指定request中必须包含某些指定的header值,才能让该方法处理请求。
  4. @RequestMapping(value="/header/id", headers = “Accept=application/json”):表示请求的URL必须为“/header/id 且请求头中必须有“Accept =application/json”参数即可匹配。

@Resource和@Autowired

@Resource和@Autowired都是做bean的注入时使用,其实@Resource并不是Spring的注解,它的包是javax.annotation.Resource,需要导入,但是Spring支持该注解的注入。

public class TestServiceImpl {
// 下面两种@Autowired只要使用一种即可 @Autowired private UserDao userDao; // 用于字段上 @Autowired public void setUserDao(UserDao userDao) {
// 用于属性的方法上 this.userDao = userDao; }}

如果我们想使用按照名称(byName)来装配,可以结合@Qualifier注解一起使用。如下:

public class TestServiceImpl {
@Autowired @Qualifier("userDao") private UserDao userDao; }

@ModelAttribute和 @SessionAttributes

代表的是:该Controller的所有方法在调用前,先执行此@ModelAttribute方法,可用于注解和方法参数中,可以把这个@ModelAttribute特性,应用在BaseController当中,所有的Controller继承BaseController,即可实现在调用Controller时,先执行@ModelAttribute方法。

@SessionAttributes即将值放到session作用域中,写在class上面。

SpringMVC 支持使用 @ModelAttribute 和 @SessionAttributes 在不同的模型(model)和控制器之间共享数据。 @ModelAttribute 主要有两种使用方式,一种是标注在方法上,一种是标注在 Controller 方法参数上。

当 @ModelAttribute 标记在方法上的时候,该方法将在处理器方法执行之前执行,然后把返回的对象存放在 session 或模型属性中,属性名称可以使用 @ModelAttribute(“attributeName”) 在标记方法的时候指定,若未指定,则使用返回类型的类名称(首字母小写)作为属性名称。关于 @ModelAttribute 标记在方法上时对应的属性是存放在 session 中还是存放在模型中,我们来做一个实验,看下面一段代码。

@Controller@RequestMapping ( "/myTest" )public class MyController {
@ModelAttribute ( "hello" ) public String getModel() {
System. out .println( "-------------Hello---------" ); return "world" ; } @ModelAttribute ( "intValue" ) public int getInteger() {
System. out .println( "-------------intValue---------------" ); return 10; } @RequestMapping ( "sayHello" ) public void sayHello( @ModelAttribute ( "hello" ) String hello, @ModelAttribute ( "intValue" ) int num, @ModelAttribute ( "user2" ) User user, Writer writer, HttpSession session) throws IOException {
writer.write( "Hello " + hello + " , Hello " + user.getUsername() + num); writer.write( "\r" ); Enumeration enume = session.getAttributeNames(); while (enume.hasMoreElements()) writer.write(enume.nextElement() + "\r" ); } @ModelAttribute ( "user2" ) public User getUser(){
System. out .println( "---------getUser-------------" ); return new User(3, "user2" ); }}

当我们请求 /myTest/sayHello.do 的时候使用 @ModelAttribute 标记的方法会先执行,然后把它们返回的对象存放到模型中。最终访问到 sayHello 方法的时候,使用 @ModelAttribute 标记的方法参数都能被正确的注入值。执行结果如下所示:

Hello world,Hello user210

@PathVariable

@PathVariable用于将请求URL中的模板变量映射到功能处理方法的参数上。

@RequestMapping(value="/users/{userId}/topics/{topicId}")public String test(        @PathVariable(value="userId") int userId,        @PathVariable(value="topicId") int topicId)

@requestParam

@RequestParam用于将请求参数区数据映射到功能处理方法的参数上。

@ResponseBody

使用时机:返回的数据不是html标签的页面,而是其他某种格式的数据时(如json、xml等)使用;

@Repository

用于注解dao层,在daoImpl类上面注解。

@Component

相当于通用的注解,当不知道一些类归到哪个层时使用,但是不建议。

5.Restful风格

转载地址:http://amrwi.baihongyu.com/

你可能感兴趣的文章
Elasticsearch-基础介绍及索引原理分析
查看>>
过滤敏感词算法
查看>>
linux学习之shell脚本if判断参数-n,-d,-f等
查看>>
linux学习之windos文件在linux里面乱码解决
查看>>
idea快捷键
查看>>
linux学习之shell遍历数组
查看>>
python函数取参及默认参数使用
查看>>
linux学习之shell中的${},##, %% , :- ,:+, ? 的使用
查看>>
Spring学习之Filter、Interceptor、Aop实现与区别
查看>>
Spring 添加@Autowired注释, 注入对象却为空
查看>>
springSecurity学习
查看>>
通过Java的api操作redis
查看>>
jquery基本选择器
查看>>
linux学习之shell字符串大小写转换
查看>>
Linux下用base64对字符串进行加密解密
查看>>
H5走迷宫小游戏
查看>>
mysql建表 表名与关键字冲突
查看>>
mysql 创建单表外键关联多表
查看>>
postman使用
查看>>
ClassNotFoundException和NoClassDefFoundError的区别
查看>>