SpringMVC | 基础(一)

作者: EclipseO2 | 来源:发表于2018-07-13 10:37 被阅读5次

一、简介

SpringMVC 通过一套 MVC 注解,让 POJO 成为处理请求的控制器,而无需实现任何接口。支持 REST 风格的 URL 请求

二、搭建环境及HelloWorld

2.1 加入架包
  • commons-logging-1.2.jar
  • spring-aop-5.0.7.RELEASE.jar
  • spring-beans-5.0.7.RELEASE.jar
  • spring-context-5.0.7.RELEASE.jar
  • spring-core-5.0.7.RELEASE.jar
  • spring-expression-5.0.7.RELEASE.jar
  • spring-web-5.0.7.RELEASE.jar
  • spring-webmvc-5.0.7.RELEASE.jar

2.2 配置web.xml

配置 DispatcherServlet:默认加载 /WEB-INF/<servlet-name>-servlet.xml 的 Spring 配置文件来启动 WEB 层的容器。同时也可以通过 contextConfigLocation 初始化参数自定义配置文件的位置和名称

<!-- 配置 dispatcherServlet -->
<servlet>
    <servlet-name>springDispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- 配置 DispatcherServlet 的一个初始化参数: 配置 SpringMVC 配置文件的位置和名称 -->
    <!-- 
        实际上也可以不通过  contextConfigLocation 来配置 SpringMVC 的配置文件, 而使用默认的 
        默认的配置文件为: /WEB-INF/<servlet-name>-servlet.xml
    -->
    
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    
    <!-- 当前 Servlet 在 WEB 应用被加载的时候就被创建, 而不是在第一次请求的时候创建  -->
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>springDispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

DispatcherServlet 的任务就是拦截请求发送给 Spring MVC 控制器

2.3 创建 SpringMVC 视图解析器
<!-- 配置自动扫描的包 -->
<context:component-scan base-package="edu.just.springmvc"></context:component-scan>
    
<!-- 配置视图解析器: 如何把 handler 方法的返回值解析为实际的物理视图 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/"></property>
    <property name="suffix" value=".jsp"></property>
</bean>

将视图逻辑名解析为:/WEB-INF/views/<viewName>.jsp

2.4 创建请求处理器类
  1. 使用 @RequestMapping 注解来映射请求的 URL
  2. 返回值会通过视图解析器解析为实际的物理视图,对于 InternalResourceViewResolver 视图解析器,会做如下的解析:
    prefix + returnVal + suffix 这样的方式得到实际的物理视图,然后会做转发操作
@Controller     //请求处理器
public class HelloWorld {
    
    @RequestMapping("/helloworld")
    public String hello() {
        System.out.println("hello world");
        return "success";
    }

}

以上转发到 /WEB-INF/views/success.jsp 这个页面

三、具体使用

3.1 使用@RequestMapping映射请求

3.1.1

@RequestMapping 注解为控制器指定可以处理哪些 URL 请求,在类和方法都可以标注

  • 类定义:提供初步的请求映射信息。相对于 WEB 应用的根目录
  • 方法处:进一步细分映射信息。相对于类定义处的 URL,若类上面没有 @RequestMapping 注解,则方法处标记的 URL 相对于 WEB 应用的根目录

DispatcherServlet 截取请求后,就通过 @RequestMapping 提供的映射信息确认请求所对应的处理方法

@RequestMapping("/springmvc")
@Controller
public class SpringMVCTest {
    
    private static final String SUCCESS = "success";

    @RequestMapping("/testRequestMapping")
    public String testRequestMapping() {
        System.out.println("testRequestMapping");
        return SUCCESS;
    }

}

访问到此方法的地址是:WEB容器的根路径/springmvc/testRequestMapping,同时由于配置了视图解析器,因此直接转到 /WEB-INF/views/ 下的 success.jsp 页面

@RequestMapping 的 value、method、params 以及 heads 分别标识请求 URL、请求方法、请求参数以及请求头的映射条件,他们之间是与的关系

@RequestMapping(..., method=RequestMethod.POST)                //使用 method 属性来指定请求方式
@RequestMapping(..., params={"username", "age!=10"}, headers="Content-Type=text/html")          //使用 params 和 headers 

PS:用的比较少

3.1.2 使用 Ant 风格来写地址

?:匹配文件名中的一个字符
*:匹配文件名中的任意字符
**:匹配多层路径

/springmvc/test??? ——> /springmvc/testaaa
/springmvc/*/test ——> /springmvc/asv/test
/springmvc/**/test ——> /springmvc/aaa/bbb/test

PS:用的比较少

3.1.3 ⭐使用@PathVariable映射URL绑定的占位符

通过 @PathVariable 可以将 URL 占位符参数绑定到控制器处理方法的入参中

@RequestMapping("/testPathVariable/id={id123}")
public String testPathVariable(@PathVariable("id123") String id) {
    System.out.println(id);
    return SUCCESS;
}

URL 中的 {xxx} 占位符可以通过 @PathVariable("xxx") 绑定到操作方法的入参中。这个时候 @PathVariable 中的参数必须与占位符 {} 中的参数相同

@RequestMapping("/testPathVariable/id={id12}")
public String testPathVariable(@PathVariable String id12) {
    System.out.println(id12);
    return SUCCESS;
}

当然,也可以不用指定 @PathVariable 后面的参数,此时 {xxx} 占位符里面的参数可以直接绑定到控制器处理方法的入参中,但是两个参数必须相同

3.2 映射请求参数和请求参数

3.2.1 使用@RequestParam绑定请求参数值

@RequestParam 可以在后台获取从前台传过来的数据。
value:请求参数的参数名
required:该参数是否必须。默认为 true
defaultValue:请求参数的默认值

@RequestMapping(value="/testRequestParam")
public String testRequestParam(@RequestParam(value="username") String un,
        @RequestParam(value="age", required=false, defaultValue="0") Integer age) {
    System.out.println("testRequestParam: " + un + ",age: " + age);
    return SUCCESS;
}

上图获取从前台传过来的参数名为 usernameage 的值,其中 age 是可选的,如前台的地址是 ...?username=5&age=5

如果参数为 int 类型,且没有加默认的值,则会报错。两种修改的方式:1. 将 int 类型改为 Integer;2. 如果参数类型必须为 int,则添加默认值

3.2.2 使用POJO对象绑定请求参数值

SpringMvc 会按请求参数名和 POJO 属性名进行自动匹配,自动为该对象填充属性值,支持级联属性。

前台代码

<form action="springmvc/testpojo" method="post">
    username: <input type="text" name="username"/>
    <br>
    password: <input type="password" name="password"/>
    <br>
    email: <input type="text" name="email"/>
    <br>
    city: <input type="text" name="address.city"/>
    <br>
    <input type="submit" value="Submit"/>
</form>

从后台获取前台传过来的数据

@RequestMapping("/testpojo")
public String testPojo(User user) {
    System.out.println("^^" + user);
    return SUCCESS;
}
3.2.3 使用Servlet API作为入参

包括HttpServletRequestHttpServletResponseHttpSessionPrincipalLocaleInputStreamOutputStreamReaderWriter

@RequestMapping("/testServletAPI")
public void testServletAPI(HttpServletRequest request, HttpServletResponse response, Writer out) throws IOException {
    System.out.println("testServletAPI, " + request + ", " + response);
    out.write("hello qorld");
}

3.3 处理模型数据(常用于回显操作)

3.3.1 ModelAndView

1.
springmvc 会把 ModelAndView 的 model 中数据放在 request 域对象中

/**
  * 目标方法的返回值可以是 ModelAndView 类型
  * 其中包含视图和模型信息
  * SpringMVC 会把 ModelAndView 的 model 中的数据放入到 request 域对象中
*/
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView() {
    String viewName = SUCCESS;
    ModelAndView modelAndView = new ModelAndView(viewName);
        
    //添加模型数据到 ModelAndView 中
    modelAndView.addObject("time", new Date());
        
    return modelAndView;
}

success.jsp

time: ${requestScope.time }

这里从 request 作用域中获取参数 time 对应的值

3.3.2 Map和Model
  1. SpringMVC 在调用方法前会创建一个隐含的模型对象作为模型数据的存储容器
  2. 如果方法的入参为 Map 或 Model 类型,SpringMVC 会将隐含模型的引用传递给这些入参

Map 方式

/**
  * 目标方法可以添加 Map(实际上也可以是 Model 类型或者 ModelMap 类型) 类型的参数
  */
@RequestMapping("/testMap")
public String testMap(Map<String, Object> map) {
    map.put("names", Arrays.asList("Tom", "Jerry", "Mike"));

    return SUCCESS;
}

success.jsp

map: ${requestScope.names }

Model 方式:比较常用

@RequestMapping("value")
public String test(Model model) {
    model.addAttribute("time", "hello");
    return SUCCESS;
}

success.jsp

time: ${requestScope.time }
3.3.3 @SessionAttributes

将模型中的某个属性暂存到 HttpSession 中,以便多个请求之间可以共享这个属性

@SessionAttribute 除了可以通过属性名指定需要放到会话中的属性外,还可以通过模型属性的对象类型指定哪些模型属性需要放到会话中

  1. @SessionAttributes(types={User.class}) 会将隐含模型中类型为 User.class 的属性放到会话中
  2. @SessionAttributes(value={"user"}) 将名为 user 的模型属性添加到会话中
  3. @SessionAttribute(values={"user1", "user2"})
@SessionAttributes(value={"user"}, types={User.class})
@RequestMapping("/springmvc")
@Controller
public class SpringMVCTest {

    private static final String SUCCESS = "success";
  
    @RequestMapping("/testSessionAttributes")
    public String testSessionAttributes(Map<String, Object> map) {
        User user = new User(null, "luwenhe", "lwh011305", "123@126.com");
        
        map.put("user", user);
        map.put("user1", "ni hao shi jie");
        return SUCCESS;
    }
}

PS:该注解只能放在类的上面,而不能修饰方法

session: ${sessionScope.user }
session1: ${sessionScope.user1 }
3.3.4 @ModelAttribute

方法上使用 @ModelAttribute 注解,SpringMVC 在调用目标方法之前,会先逐个调用在方法上标注了 @ModelAttribute 的方法

index.jsp:用于从表单中输入数据,模拟从数据库中获取数据

<form action="springmvc/testModelAttribute" method="post">
    <input type="hidden" name="id" value="1"/>
    username: <input type="text" name="username" value="Tom"/>
    <br>
    email: <input type="text" name="email" value="tom@126.com"/>
    <br>
    <input type="submit" value="Submit"/>
</form>

如果没用 @ModelAttribute 注解,则会输出 User [Id=1, username=Tom, password=null, email=tom@126.com]

@RequestMapping("/testModelAttribute")
public String testModelAttribute(User user) {
    System.out.println("修改: " + user);
    return SUCCESS;
}

如果在上述代码之前加入标注了 @ModelAttribute 注解的方法,模拟修改了对象 User 的信息

@ModelAttribute
public void getUser(@RequestParam(value="id", required=false) Integer id,
            Map<String, Object> map) {      //从后台获取前台参数名字为 id 的元素的值
    System.out.println("id: " + id);
    if(id != null) {
        User user = new User(1, "Tom", "123", "tom@126.com");
        System.out.println("从数据库中获取一个对象: " + user);
        
        map.put("user", user);
    }
}

最终的输出是:

id: 1
从数据库中获取一个对象: User [Id=1, username=Tom, password=123, email=tom@126.com]
修改: User [Id=1, username=Tom, password=123, email=tom@126.com]

可见 SpringMVC 从 Map 中取出 User 对象,并把对象传入了目标方法的参数中

注意:在 @ModelAttribute 修饰的方法中, 放入到 Map 时的键需要和目标方法参数的第一个字母小写的字符串一致, 如参数为 User, 则 Map 的键为 user

SpringMVC 确定目标方法 POJO 类型入参的过程:
1. 确定一个 key: 
   1). 若目标方法的 POJO 类型的参数没有使用 @ModelAttribute 作为修饰, 则 key 为 POJO 类名第一个字母的小写
   2). 若使用了 @ModelAttribute 来修饰, 则 key 为 @ModelAttribute 注解的属性值

2. 在 implicitModel 中查找 key 对应的对象, 若存在, 则作为入参传入
   1). 若在 @ModelAttribute 标记的方法在 Map 中保存过, 且 key 和 1 确定的 key 一致, 则会获取到.

3. 若 implicitModel 不存在 key 对应的对象, 则检查当前的 Handler 是否使用 @SessionAttributes 注解修饰, 若使用了该注解, 
   且 @SessionAttributes 注解的 value 属性值中包含了 key, 则会从 HttpSession 中来获取 key 所对应的 value 值, 
   若存在则直接传到目标方法的入参中, 若不存在则将抛出异常

4. 若 Handler 没有标识 @SessionAttribute 注解或 @SessionAttribute 注解的 value 
   值中不包含 key, 则会通过反射来创建 POJO 类型的参数, 传入为目标方法的参数, 此时只能获取从前台传过来的值

5. SpringMVC 会把 key 和 POJO 类型的对象保存到 implicitModel 中, 进而保存到 request 中

3.4 视图和视图解析器

  • 请求方法执行完成后,最后返回一个 ModelAndView 对象。对于那些返回 String,View 或者 ModelMap 类型的方法,SpringMVC 会在内部将他们装配成一个 ModelAndView 对象
  • SpringMVC 接主视图解析器(ViewResolver)得到最终的视图对象 View,最终的视图可以是 JSP,也可以是其他类型

视图

视图的作用是渲染模型数据,将模型数据以某种形式呈现给客户。常见的视图实现类有:

  • InternalResourceView:将 JSP 或其他资源封装成一个视图,是 InternalResouceViewResolver 默认使用的视图实现类
  • JstlView:如果 JSP 文件中使用了 JSTL 国际化标签的功能,则需要使用该试图类
  • AbstractExcelView:Excel 文档视图的抽象类,用于构造 Excel 文档

视图解析器

所有视图解析器必须实现 ViewResovler 接口。常见的视图解析器类有:

  • BeanNameViewResolver:将逻辑视图名解析为一个 Bean,Bean 的 id 等于逻辑视图名
  • InternalResourceViewResolver:将视图名解析为一个 URL 文件
<!-- 配置自动扫描的包 -->
<context:component-scan base-package="edu.just.springmvc"></context:component-scan>
    
<!-- 配置视图解析器: 如何把 handler 方法的返回值解析为实际的无力视图 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/"></property>
    <property name="suffix" value=".jsp"></property>
</bean>
重定向

如果字符串中带有 forward: 或者 redirect,SpringMVC 会对他们做特殊处理,前者为转发操作,后者
为重定向

@RequestMapping("/testRedirect")
public String testRedirect() {
    System.out.println("testRedirect...");
    return "redirect:/index.jsp";
}

此代码会重定向到根目录下的 index.jsp 页面

3.5 使用RESTFul

REST 即 Representational State Tranfer,即表现层状态转化。
表现层(Representation):把资源具体呈现出来的形式。
状态转化(State Tranfer):每发出一个请求,就代表了客户端和服务器的一次交互。

如何发送 PUT 请求和 DELETE 请求?

  1. 需要配置 HiddenHttpMethodFilter
  2. 需要发送 POST 请求
  3. 需要在发送 POST 请求时携带一个 name="_method" 的隐藏域, 值为 DELETE 或 PUT

配置文件

<!-- 
    配置 org.springframework.web.filter.HiddenHttpMethodFilter: 
    可以把 POST 请求转为 DELETE 或者 POST 请求 
-->
<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>

HiddenHttpMethodFilter:由于浏览器只支持 GET 和 POST,不支持 DELETE 和 PUT,因此此过滤器可以将请求转换为支持四种请求

前台页面:

<form action="springmvc/testRest/1" method="post">
    <input type="hidden" name="_method" value="DELETE"/>
    <input type="submit" value="TestRest DELETE"/>
</form>

从后台获取

@RequestMapping(value="/testRest/{id}", method=RequestMethod.DELETE)    //delete 方法
@ResponseBody
public String testRestDelete(@PathVariable Integer id) {
    System.out.println("testRest DELETE: " + id);
    return SUCCESS;
}

@ResponseBody 一开始没有加,会报错,百度之后由此加上

3.5 处理静态资源

若将 DispatcherServlet 请求映射配置为 /,则 SpringMVC 将捕获 WEB 容器的所有请求,包括静态资源的请求,SpringMVC 会将他们当成一个普通请求来处理,因此会找不到对应处理器而报错

解决方案:可以在 SpringMVC 的配置文件中配置 <mvc:default-servlet-handler></mvc:default-servlet-handler> 的方式

原理:
<mvc:default-servlet-handler/> 将在 SpringMVC 上下文定义一个 DefaultServletHttpRequestHandler,他会对进入 DispatcherServlet 的请求进行筛选,如果发现是没有经过映射的请求(@RequestMapping),就将请求交给 WEB 应用服务器的 Servlet 处理,如果不是静态资源的请求,才由 DispatcherServlet 继续处理

此时还需要在配置文件中加入 <mvc:annotation-driven/> 才可以运行

<mvc:default-servlet-handler></mvc:default-servlet-handler>
    
<!-- 标准配置 -->
<mvc:annotation-driven></mvc:annotation-driven>

关于<mvc:annotation-driven>

  1. <mvc:annotation-driven/> 会自带注册 RequestMappingHandlerMappingRequestMappingHandlerAdapterExceptionHandlerExceptionResolver 三个 Bean

  2. 同时提供以下支持:
    ①. 支持使用 ConversionService 实例对表单进行类型转换
    ②. 支持使用 @NumberFormat annotation@DateTimeFormat 注解完成数据类型的格式化
    ③. 支持使用 @Valid 注解对 JavaBean 实例进行 JSR 303 验证
    ④. 支持使用 @RequestBody@ResponseBody 注解

PS:一般情况下,都会配置 <mvc:annotation-driven/> 这个说明

3.6 数据绑定

数据绑定流程
核心是 DataBinder
  • DataBinder:调用装配在 SpringMVC 上下文的 ConversionService 组件进行数据类型转换、数据格式化工作。将 Servlet 中的请求信息填充到入参对象中
  • Validator :对已经绑定了请求信息的入参对象进行数据合法性校验
  • BindingResult:SpringMVC 抽取 BindingResult 中的入参对象和校验错误对象,将它们赋给处理方法的响应入参

@InitBinder注解
  • @InitBinder 标识的方法,可以对 WebDataBinder 对象进行初始化。WebDataBinder 是 DataBinder 的字类,用于完成由表单字段到 JavaBean 属性的绑定
  • @InitBinder 方法不能有返回值,必须声明为 void
  • @InitBinder 方法的参数通常是 WebDataBinder
@InitBinder
public void initBinder(WebDataBinder binder) {
    binder.setDisallowedFields("lastName");
}

以上代码表示不能绑定对象中的名为 lastName 的属性,造成的结果就是最后不能输出属性名为 lastName 字段的值

格式化操作

可以在定义的属性上面使用 @NumberFormat@DateTimeFormat 注解进行数据格式化

对数字进行格式化

@NumberFormat(pattern="###.#")
private Float salary;

对日期进行格式化

@DateTimeFormat(pattern="yyyy-MM-dd")
private Date birth;

最后别忘了加上注解

<mvc:annotation-driven/>

五、参考

相关文章

  • Spring MVC入门

    SpringMVC核心组件 SpringMVC实现流程 XML配置使用 1、SpringMVC基础配置2、XML配...

  • 史上最全最强SpringMVC详细示例实战教程

    SpringMVC学习笔记---- 一、SpringMVC基础入门,创建一个HelloWorld程序 1.首先,导...

  • SpringMVC | 基础(一)

    一、简介 SpringMVC 通过一套 MVC 注解,让 POJO 成为处理请求的控制器,而无需实现任何接口。支持...

  • Spring框架系列 - SpringMVC实现原理之Dispa

    前文我们有了IOC的源码基础以及SpringMVC的基础,我们便可以进一步深入理解SpringMVC主要实现原理,...

  • 史上最全的SpringMVC学习笔记

    写文章 一、SpringMVC基础入门,创建一个HelloWorld程序 1.首先,导入SpringMVC需要的j...

  • Spring MVC框架原理与开发

    一、学习指南 1.基础知识: 1)什么是springMVC;2)springMVC框架原理(掌握);3)sprin...

  • springMVC基础

    请求流程图 1、客户端发请求,被spring的前段控制器DispatcherServlet捕获 2、Dispatc...

  • SpringMVC基础

    Spring MVC工作原理 在请求离开浏览器时,会带有用户所请求内容的信息,至少会包含请求的URL。但是还可能带...

  • SpringMVC 基础

    一、回顾 MVC 1.1 什么是 MVC MVC 是模型(Model)、视图(View)、控制器(Controll...

  • java面试题目

    框架 1.实现一个springmvc的思路 2.springmvc的事务 Java基础 1.java类的加载机制 ...

网友评论

    本文标题:SpringMVC | 基础(一)

    本文链接:https://www.haomeiwen.com/subject/qfqrpftx.html