title: SpringMvc -- 开发手册

date: 2018-11-15 22:14:22

tags: SpringMvc

categories: SpringMvc #分类名

type: "SpringMvc"

本片博客记录SpringMvc带给了我们什么?,图解运行流程,以及开发中,常用的注解

SpringMvc基于servlet设计,用于处理用户的请求,基于方法级别拦截,参数通过入参传递,处理器设计为单例模式,和Spring无缝整合, 分离了 控制器,模型对象,让它们更容易被定制

主要组成部分:

  • DispatcherServlet:

前端控制器:所有的请求,都会首先被他拦截到,统一给请求分发处理的Handler

  • HandlerMapping

处理器映射器: 识别控制器中的注解,目的是找到具体的和Url对应的处理方法

  • HanderAdapter

处理器适配器,实例化控制器Controller,调用具体的方法,处理用户发来的请求

  • Controller

控制器, 用来处理用户的请求,返回ModelAndView给前端控制器

  • ViewResolver

视图解析器: 解析视图(ModeAndView), 把ModelAndView里面的逻辑视图编程一个正真的View对象,并把Model从ModelAndView中取出来

宏观上看,DispatcherServilet是整个web应用的控制器, 微观上,controller是单个http请求处理的控制器

@RequestMapping()

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping { String name() default ""; @AliasFor("path")
String[] value() default {}; @AliasFor("value")
String[] path() default {}; // 映射请求方式(get post put delete)
RequestMethod[] method() default {};
//
String[] params() default {};
// 映射请求参数
String[] headers() default {}; String[] consumes() default {}; String[] produces() default {}; }
  • 支持标注在类上和方法上

    • 最常用标注在方法上,用于映射处理前端发送过来的URL
    • 如果我们同时把它俩加载类和方法上, 那么请求路径就是两者的路径使用 / 分隔开
 //         映射url                   映射方法为POST                     指定必须包含username  并且age!=10
@RequestMapping(value = "sayHello", method = RequestMethod.POST, params = {"username", "age!=10"})
public String sayHello() {
System.out.println("来到了sayHello");
return "success";
}

@RequestMapping 路径支持通配符

  • ? 一个字符
  • *任意字符
  • ** 多层路径
/**
* 测试 ant风格的占位符
* @return
*/
@RequestMapping("/textAntPath/*/text")
public String textAntPath() {
System.out.println("v");
return "success";
}

@PathVariable 与 @RequestParam()

  • @PathVariable 映射URL绑定的占位符(一般在我们添加在方法上的注解上通过{XXX} 占位 )

  • @RequestParam取出来的是请求参数,发出的url格式如下:


/**
* 1. 假如:@RequestParam("password") url中并不存在的话, 报错400
* 1.1, 设置@RequestParam的required=false
* 2. 假如只有第一个:@RequestParam("username")String username,String password ( password同样会被装配进去值)
*

SpringMvc的REST风格

浏览器的form表单,只是支持GET POST请求,而Delete put的方法,是不支持的, 我们使用rest风格的提交表单时, 会被SpringMvc的 HiddenHttpMethodFilter拦截下来,过滤处理成标准的http方法,从而支持了 delete put

修改配置文件 web.xml

<!--配置  HiddenHttpMethodFilter   开启RestFul风格  -->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

jsp

<br>
<a href="text01/1">测试get请求</a>
<br>
<form action="text02" method="post">
<input type="text" name="id">
<input type="submit" value="点击发送post请求">
</form>
<br>
<form action="text03/8" method="post">
<input type="hidden" name="_method" value="PUT">
<input type="submit" value="点击发送put请求">
</form>
<br>
<form action="text04/7" method="post"> <%-- 我们需要添加隐藏域, name="_method" 不能不写,Spring会拿到它的信息,从而将post转换成 delete --%>
<input type="hidden" name="_method" value="delete">
<input type="submit" value="点击发送delete请求">
</form>

控制器

@RequestMapping(value="text01/{id}",method = RequestMethod.GET)
public String text01(@PathVariable("id")Integer id){
System.out.println("获取id=="+id+"的信息");
return "success";
} @RequestMapping(value = "text02",method = RequestMethod.POST)
public String text02(@RequestParam Integer id){
System.out.println("新增id=="+id+"的信息");
return "success";
} @RequestMapping(value = "text03/{id}",method = RequestMethod.PUT)
public String text03(@PathVariable("id") Integer id){
System.out.println("修改id=="+id+"的信息");
return "success";
} @RequestMapping(value = "text04/{id}",method = RequestMethod.DELETE)
public String text04(@PathVariable("id") Integer id){
System.out.println("删除id=="+id+"的信息");
return "success";
}

@RequestHeader()

映射请求头的信息到入参位置,不同浏览器的请求头的细节可能是不同的

@RequestMapping("/text")
public void text(@RequestHeader(value="Accept-Langnage") String s1){
//
}

@CookieValue()

很常用,在微服务的安全验证模块,安全中心会给满足条件的用户办法token, 存放到浏览器的cookie里面, 用户再次访问就会携带cookie,我们通过这个注解取出cookie的值,进行安全验证

@RequestMapping("textCookieValue")
public String textCookieValue(@CookieValue("JSESSIONID")String jessionId){
System.out.println("cookieValue=="+jessionId);
return "success";
}

POJO绑定请求参数

很多时候前端提交的表单对应着我们将要持久化对象,那么使用pojo绑定参数无疑是一件超赞的事

springMvc支持 按照请求参数名和pojo属性进行自定匹配,自动为该对象填充属性,支持级联属性

JSP


<form action="textPojo">
用户名: <input type="text" name="username">
密码: <input type="text" name="password">
邮箱: <input type="text" name="email">
<%-- 级联属性 --%>
省: <input type="text" name="adress.province">
市: <input type="text" name="adress.city">
<input type="submit" value="提交">
</form>

pojo

@AllArgsConstructor
@NoArgsConstructor
@Data
@ToString
public class Person {
String username;
String password;
String email;
Adress adress; } @AllArgsConstructor
@NoArgsConstructor
@Data
@ToString
public class Adress { String province;
String city; }

控制器

springmvc 会自定为我们的入参绑定上前端表单上的数据

@RequestMapping("textPojo")
public String textPojo(Person person){
System.out.println(person);
return "success";
}

支持Servlet原生API

@RequestMapping("textServletAPi")
public String textServletApi(HttpServletRequest request, HttpServletResponse response) {
return "success";
}

处理模型数据

ModelAndView

/**
* springMvc会把ModelAndView的Model放入request域对象中
* @return
*/
@RequestMapping("textModelAndView")
public ModelAndView textModelAndView(){
String viewName="success";
ModelAndView modelAndView = new ModelAndView();
// 设置视图名
modelAndView.setViewName(viewName);
// 添加模型数据
modelAndView.addObject("日期",new Date());
return modelAndView;
}

Map

说白了,就是方法的入参位置可以添加一个 Map或者Model类型的参数,mvc会把隐藏的模型引用传递给这个入参, 从而是开发者可以通过这个入参访问模型中的所有数据,同是可以添加新数据

@RequestMapping("textMap")
public String textMap(Map<String, Object> map) {
map.put("names", Arrays.asList("zhangsan", "lisi"));
return "success"; }

@SessionAttributes

如果我们希望多个请求之间共享某个模型属性数据,那么我们使用@SeesionAttribute,她会把我们存放到作用域中的信息备份到Session中

// 她会把我们存放到 作用域中的数据,备份到Session
@SessionAttributes("user",types = String.class)
public class HelloController { @RequestMapping("textSessionAttribute")
public String textSeesionAttribute(Map<String,Object> map){
Adress adress = new Adress("山东","XXX");
Person zhangsan = new Person("zhangsan", "234234", "4646@qq.com", adress);
map.put("user",zhangsan); // 放入请求域
return "success";
}

在前几个低版本的SpringMvc中版本中,如果本类标记上了@SessionAttribute但是却没有标记@ModelAttribute的方法,服务器报错500;

@ModelAttribute

这个注解可以帮我解决这样一种情况, 更新操作,很多时候,我们只是针对表中的其中几个字段进行更新, 另外一些字段不需要更新(比如插入时间),那怎么做? 如果自己new对象的话,前端的数据绑定到我们new的对象上,插入时间就会空着,这时已更新,原来的插入时间就会被覆盖,于是我们不new ,通常使用@ModelAttribute注解标注方法上,先查询数据库,得到有插入时间字段的对象

控制器(标注在方法上):

// 添加上这个注解的方法,会被SpringMvc拦截, 所有的方法在调用前都会先执行这个方法,把查询出来的信息放到作用域
// 这样SpringMvc把前端传递过来的信息,赋值给 作用域里面user -- 狸猫换太子,
// 用户得到的就是被 狸猫
@ModelAttribute
public void textModelAttribute(
@RequestParam(value = "id",required = false) Integer id,
Map<String,Object> map){ // 判断,如果id不为空, 就表示用户想修改信息
// 于是,我们的任务就是 把查数据库,把用户的信息查出来,通过Map放到作用域里面, 谁用,谁取
if(null!=id){
//模拟查库
System.out.println("查询数据库,获取user信息");
Person person = new Person("1", "lisi", "999", "8989@qq.com", new Adress("山西", "北京")); // 注意点: 在ModelAttribute修饰的方法中,放入作用域的 key 为 类名首字母小写
map.put("person",person);
} } @RequestMapping("textModelAttribute2")
public String textModelAttribute2(Person person) // 这里的名字也要和作用域里面的名字相同
{
System.out.println("修改=="+person);
return "success";
}

控制器: (标注在参数上)

**
* 1. 这里的名字随便, 但是上面那个方法的的key 尽量就是类名首字母小写
*
* 2. 使用 添加在入参上 @ModelAttribute(value="XXX") , 则名字随便
*
*@ModelAttribute
* @param user
* @return
*/
@RequestMapping("textModelAttribute2")
public String textModelAttribute2(@ModelAttribute(value = "abc")Person user)
{

自定义视图:

第一步: 实现View接口

public class HelloView implements View {
/**
* 返回内容类型
* @return
*/
@Override
public String getContentType() {
return "text/html";
} /**
* 渲染视图
* @param map
* @param httpServletRequest
* @param httpServletResponse
* @throws Exception
*/
@Override
public void render(Map<String, ?> map, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
httpServletResponse.getWriter().print("helloViewTime"+new Date());
}
}

第二步: 配置视图解析器

<!-- 配置视图解析器 BeanNameViewResolver 解析器: 使用视图的名字解析视图 (所以我们需要把我们的视图添加进IOC)-->
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
<!-- 到现在为止,我们就有了两个视图解析器,需要指定优先顺序-->
<!-- 常用的放在后边,我们的放前边 order越小,优先级越高-->
<property name="order" value="200"></property>
</bean>

测试

  @RequestMapping("textView")
public String textView(){
System.out.println("textView");
// 自定义的视图类名小写
return "helloView";
}

重定向

控制器返回的字符串被当作逻辑视图名称处理

如果返回的字符串中带有forward:或者redirect: 会被SpringMvc当作指示符特殊处理,后面的字符串当作url路径

@RequestMapping("textRedirect")
public String textRedirect(){
System.out.println("测试重定向!!!");
return "redirect:/index.jsp";
}

数据校验:

当我们添加<mvc:annotation-driven/>配置,SpringMvc会自动为我们做如下处理

  • RequestMappingHandlerMapping
  • RequestMappingHanderAdapter
  • ExceptionHandlerExceptionResolver 这三个bean
  • 支持使用ConversionService对表单参数进行类型转换
  • 支持使用@NumberFormatannptation @DateTimeFormat 注解完成数据类型格式化
  • 支持使用@Valid 注解对JavaBean 实例惊醒jsr303 验证
  • 支持使用@RequestBody和@ResponseBady // 处理ajax

常使用如下连个注解对bean进行校验

@DateTimeFormat(pattern="yyyy-MM-dd")
Date birth;
@NumberFormat(pattern = "#,###,###.#") // #表示数数字
Float salary;

返回Json

@ResponseBody

拦截器

  • 实现自己的拦截器实现HandlerInterceptor接口

重写他的三个方法

preHandle()
作用: 对用户请求request进行处理
调用时机: 在处理器处理请求之前被调用
返回:
true: 如果还需要调用其他拦截器或者是业务处理器
f 何组件处理请求 postHandle()
作用: 对用户请求request进行处理
调用时机: 业务处理器处理完请求之后,DispatcherServlet向客户端相应之前 aferCompletion() 作用: 进行资源清理工作
调用时机: 在DispatcherServlet向客户端响应数据之后调用

配置文件:

<!-- 配置拦截器 -->
<mvc:interceptors>
<bean class="com.changwu.interceptor.FirstInterceptor"/>
</mvc:interceptors>
  • 配置拦截指定请求路径的拦截器
<!-- 配置拦截器 -->
<mvc:interceptors>
<bean class="com.changwu.interceptor.FirstInterceptor"/> // 配置 专门针对某个求情的拦截器
<mvc:interceptor>
<mvc:mapping path="/textView"/>
<bean class="com.changwu.interceptor.SecondInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
  • 多个拦截器的执行顺序
  1. 第一个拦截器的firstInterceptor返回flase,其他拦截器不执行,目标方法不执行
  2. 第一个拦截器的firstInterceptor返回true,第二个拦截器的firstInterceptor返回false,目标方法不执行,但是第一个拦截器的afterCompletion方法会执行,回收资源

异常处理--ExceptionHandlerExceptionResolver

SpringMvc 使用HandlerExceptionResolver处理异常

ExceptionHandlerExceptionResolver

主要处理Handler中使用 @ExceptionHandler注解定义的方法

1. 控制器: 出现异常
@RequestMapping("textExceptionHandlerExceptionResolver")
public String textExceptionHandlerExceptionResolver(@RequestParam("i")int i){
System.out.println("result=="+10/i);
return "success";
} 2. 标注有@ExceptionHandler注解的方法 会处理异常
/**
* 捕获数学异常
* 把异常带到页面"ModelAndView
*/
@ExceptionHandler(value=ArithmeticException.class)
public ModelAndView handleArithmeticException(Exception e){
// 通过ModelAndView 把错误信息带到页面
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("error");
modelAndView.addObject("errorMsg",e);
System.out.println("异常信息:"+e);
return modelAndView;
}

@ExceptionHandler定义的方法优先级问题,例如发生的是 空指针异常,但是声明异常是 运行时异常和异常, 这时就会报 离空指针异常比较近的 运行时异常

1. 假如出现了数学异常, 它会优先使用下面的第一个异常

@RequestMapping("textExceptionHandlerExceptionResolver")
public String textExceptionHandlerExceptionResolver(@RequestParam("i")int i){
System.out.println("result=="+10/i);
return "success";
} /**
* 捕获数学异常
* 把异常带到页面"ModelAndView
*
*/ @ExceptionHandler(value=ArithmeticException.class)
public ModelAndView handleArithmeticException(Exception e){
// 通过ModelAndView 把错误信息带到页面
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("error");
modelAndView.addObject("errorMsg",e);
System.out.println("异常信息:"+e);
return modelAndView;
} @ExceptionHandler(value=RuntimeException.class)
public ModelAndView handleRuntimeException(Exception e){
// 通过ModelAndView 把错误信息带到页面
ModelAndView modelAndView = new ModelAndView("error");
modelAndView.addObject("errorMsg",e);
System.out.println("{异常信息}:"+e);
return modelAndView;
}

ExceptionHandlerExceptionResolver 内部找不到@ExceptionHandler 注解的话,就会找 @ControllerAdvice 中的 @ExceptionHandler注解方法

/**
* @Author: Changwu
* @Date: 2019/4/14 19:04
*/
@ControllerAdvice
public class HandException { @ExceptionHandler(value=ArithmeticException.class)
public ModelAndView handleArithmeticException(Exception e){
// 通过ModelAndView 把错误信息带到页面
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("error");
modelAndView.addObject("errorMsg",e);
System.out.println("异常信息:"+e);
return modelAndView;
}
}

异常处理-- ResponseStatusExceptionResolver

通过@ResponseStatus(value="异常信息",code="错误状态码")注解,定制返回给前端的异常信息以及状态码

异常处理 -- SimpleMappingExceptionResolver

xml文件中进行配置, 指定出现什么异常,跳往哪个页面

<!-- 配置SimpleMappingExceptionResolver 来映射异常 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<!-- 统一匹配的异常的全类名 跳往的异常页面 -->
<prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop>
</props>
</property>
</bean>
  • 异常信息会自动存储在 作用域 通过${requestScope.exception} 可以取出来

SpringMVC开发手册的更多相关文章

  1. Net力软快速信息化系统开发框架 + 开发手册+数据库说明

    源码目录结构说明LeaRun.Cache –缓存层LeaRun.Resource –本地语言LeaRun.Utilities –公共类库LeaRun.DataAccess –数据库访问核心组件LeaR ...

  2. 在线教学、视频会议 Webus Fox(2) 服务端开发手册

    上次在<在线教学.视频会议软件 Webus Fox(1)文本.语音.视频聊天及电子白板基本用法>里介绍了软件的基本用法.本文主要介绍服务器端如何配置.开发. 1. 配置 1.1 IIS配置 ...

  3. 在线教学、视频会议 Webus Fox(3) 客户端开发手册

    本文主要介绍webus fox 客户端的配置及接口说明. 1. 文件列表和配置 1.1 文件列表 1.2 common.xml 配置 根据服务器端的部署, 替换[ServerUrl] , [RtmpP ...

  4. Navi.Soft30.开放平台.聚合.开发手册

    1系统简介 1.1功能简述 现在是一个信息时代,并且正在高速发展.以前获取信息的途径非常少,可能只有电视台,收音机等有限的来源,而现在的途径数不胜数,如:QQ,微信,官方网站,个人网站等等 本开发手册 ...

  5. Navi.Soft30.开放平台.腾讯.开发手册

    1系统简介 1.1功能简述 现在是一个信息时代,并且正在高速发展.以前获取信息的途径非常少,可能只有电视台,收音机等有限的来源,而现在的途径数不胜数,如:QQ,微信,官方网站,个人网站等等 本开发手册 ...

  6. Navi.Soft30.开放平台.百度.开发手册

    1系统简介 1.1功能简述 现在是一个信息时代,并且正在高速发展.以前获取信息的途径非常少,可能只有电视台,收音机等有限的来源,而现在的途径数不胜数,如:QQ,微信,官方网站,个人网站等等 本开发手册 ...

  7. Navi.Soft30.框架.WinForm.开发手册

    阅读导航 Navi.Soft30.Core类库.开发手册 http://www.cnblogs.com/xiyang1011/p/5709489.html Navi.Soft30.框架.WinForm ...

  8. Discuz!开发手册

    如何使用Discuz开发手册? 1.首先建议你了解Discuz目录结构-全局篇 通过对目录结构的了解,会在以后的创作道路上提供坚实的基础! 2.你还需要了解Discuz! X3.1数据字典 3.创建自 ...

  9. dzzoffice教程、文档、开发手册等内容地址

    dzzoffice教程.文档.开发手册等内容全部都存放在DzzOffice开发者社区的文集中.搜索引擎收录不到DzzOffice中的应用内容,这里将文集地址提供在这里. 地址:http://dev.d ...

随机推荐

  1. disabled_button 按钮按不下去

    X老师今天上课讲了前端知识,然后给了大家一个不能按的按钮,小宁惊奇地发现这个按钮按不下去,到底怎么才能按下去 检查元素 删除 按钮就可以摁了 出现答案

  2. python面试题--初级(一)

    一. Python 中有多少种运算符? 这类面试问题可以判断你的 Python 功底,可以举一些实例来回答这类问题. 在 Python 中我们有 7 中运算符: 算术运算符.关系 (比较) 运算符.赋 ...

  3. linux 获取目录中详细信息 -rw-r--r--详解

    -rw-r–r– 1 root root 1313 Sep 3 14:59 test.log详解 查询目录中的内容命令 ls [选项] [文件或目录] 选项: -a 显示所有文件.包括隐藏文件 -l ...

  4. Ubantu上安装Redis

    Ubantu上安装Redis:Redis(Remote Dictionary Server):远程字典服务器,简称REDIS;Redis数据库产品用C语言编写而成,开源.少量数据存储.高速读写访问,是 ...

  5. 又一Tab切换效果(js实现)

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  6. 简单的物流项目实战,WPF的MVVM设计模式(二)

    往Models文件添加一个类,ConnectObject         /// <summary>        /// 链接数据库字符串        /// </summary ...

  7. 关于“如何只用2GB内存从20亿,40亿,80亿个整数中找到出现次数最多的数?”的一种思路

    小弟不才,只懂一些c#的皮毛,有一些想法, int32值范围大概在-20亿——20亿,按hashtable一个keyvalue占8B的设定来说,最大可以存储大约2.5亿个 数字-次数对. 那么,可以将 ...

  8. sql中关闭自增,并插入数据

    ET IDENTITY_INSERT 允许将显式值插入表的标识列中. 语法 SET IDENTITY_INSERT [ database.[ owner.] ] { table } { ON | OF ...

  9. C++函数声明与定义

    一个C++函数,如果没有函数声明而只有函数定义,程序照样运行,但要求这个函数定义必须放在main函数之前,否则编译按照从上到下的顺序扫描下来,就会出现编译器不认识它的情况. 如果一个程序同时有函数声明 ...

  10. Qt和其它GUI库的对比

    http://c.biancheng.net/view/3876.html 世界上的 GUI 库多如牛毛,有的跨平台,有的专属于某个操作系统:有的只有 UI 功能,有的还融合了网络通信.多媒体处理.数 ...