spring mvc

spring mvc

MVC 是一种软件架构的思想,将软件按照数据模型层(Model)、视图层(View)、控制层(Controller)来划分,使用 MVC 的目的是将 M 和 V 实现代码分离。

  • 数据模型层:指工程中的 JavaBean,作用是处理数据
    • 一类称为实体类Bean:专门存储业务数据的,如 Student、User 等
    • 一类称为业务处理 Bean:指 Service 或 Dao,dto对象,专门用于处理业务逻辑和数据访问
  • 视图层:指工程中的html或jsp等页面,作用是与用户进行交互,展示数据 thymeleaf之类
  • 控制层:指工程中的servlet,作用是接收请求和响应浏览器

1.mvc的请求流程

  1. 用户发起请求 DispatcherServlet前端控制器收到请求后自己不进行处理,而是委托给其他的解析器进行 处理,作为统一访问点,进行全局的流程控制
  2. DispatcherServlet——>HandlerMapping, HandlerMapping 将会把请求映射为 HandlerExecutionChain 对象(包含一 个Handler 处理器(页面控制器)对象、多个HandlerInterceptor 拦截器)对象,通过这种策略模式,很容易添加新 的映射策略;
  3. DispatcherServlet——>HandlerAdapter,HandlerAdapter 将会把处理器包装为适配器,从而支持多种类型的处理器, 即适配器设计模式的应用,从而很容易支持很多类型的处理器
  4. HandlerAdapter——>处理器功能处理方法的调用,HandlerAdapter 将会根据适配的结果调用真正的处理器的功能处 理方法,完成功能处理;并返回一个ModelAndView 对象(包含模型数据、逻辑视图名)
  5. ModelAndView 的逻辑视图名——> ViewResolver,ViewResolver 将把逻辑视图名解析为具体的View,通过这种策 略模式,很容易更换其他视图技术;
  6. View——>渲染,View 会根据传进来的Model 模型数据进行渲染,此处的Model 实际是一个Map 数据结构,因此 很容易支持其他视图技术;
  7. 返回控制权给DispatcherServlet,由DispatcherServlet 返回响应给用户,到此一个流程结束

2.典型例子

2.1新建web项目

  • 打开 IDEA,选择 Java Enterprise
  • 模版选择 Web 应用程序
  • 应用程序服务器选择 Tomcat
  • 构建系统选择 Maven
  • 依赖项勾选 Servlet 和 Thymeleaf
  • 删除掉 index.jsp

2.2 引入依赖

<dependencies>
    <!-- SpringMVC -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.3.1</version>
    </dependency>

    <!-- 日志 -->
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.3</version>
    </dependency>

    <!-- Spring5和Thymeleaf整合包也可以采用jsp模板引擎 -->
    <dependency>
        <groupId>org.thymeleaf</groupId>
        <artifactId>thymeleaf-spring5</artifactId>
        <version>3.0.12.RELEASE</version>
    </dependency>
</dependencies>

2.3 配置spring mvc

配置web.xml 在WEB-INFO目录下

  <!--配置前端过滤器-->
  <filter>
    <filter-name>characterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>characterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

<!-- 配置SpringMVC的前端控制器,对浏览器发送的请求统一进行处理 -->
<servlet>
    <servlet-name>springMVC</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- 通过初始化参数指定SpringMVC配置文件的位置和名称 创建spring容器 -->
    <init-param>
        <!-- contextConfigLocation为固定值 -->
        <param-name>contextConfigLocation</param-name>
        <!-- 使用classpath:表示从类路径查找配置文件,例如maven工程中的src/main/resources -->
        <param-value>classpath:springMVC.xml</param-value>
    </init-param>
    <!-- 
         作为框架的核心组件,在启动过程中有大量的初始化操作要做
        而这些操作放在第一次请求时才执行会严重影响访问速度
        因此需要通过此标签将启动控制DispatcherServlet的初始化时间提前到服务器启动时
    -->
    <load-on-startup>1</load-on-startup><!-- 表示容器再启动时立即加载servlet -->
</servlet>
<servlet-mapping>
    <servlet-name>springMVC</servlet-name>
    <!--
        设置springMVC的核心控制器所能处理的请求的请求路径
        /所匹配的请求可以是/login或.html或.js或.css方式的请求路径
        但是/不能匹配.jsp请求路径的请求
    -->
    <url-pattern>/</url-pattern>
</servlet-mapping>

在 resources 目录配置springmvc.xml 用于配置视图解析器等

<beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:context="http://www.springframework.org/schema/context"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"  
    xsi:schemaLocation="    
        http://www.springframework.org/schema/mvc   
        http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd  
        http://www.springframework.org/schema/beans         
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd    
        http://www.springframework.org/schema/context     
        http://www.springframework.org/schema/context/spring-context-3.0.xsd">  
    <!-- 配置handlerMapping-->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>
    <!-- 配置handlerAdapter-->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>
    <!-- 配置渲染器-->
    <bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property>
        <property name="prefix" value="/WEB-INF/jsp/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>
    <!-- 配置请求和处理器-->
    <bean name="/hello.do" class="com.controller.HelloController"></bean>
</beans>  

另一种基于注解配置(流行)

<beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:context="http://www.springframework.org/schema/context"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"  
    xsi:schemaLocation="    
        http://www.springframework.org/schema/mvc   
        http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd  
        http://www.springframework.org/schema/beans         
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd    
        http://www.springframework.org/schema/context     
        http://www.springframework.org/schema/context/spring-context-3.0.xsd">  
   <!-- 把标记了@Controller注解的类转换为bean -->  
    <context:component-scan base-package="com.controller" />  
    <!-- 启动Spring MVC的注解功能,配置annotation类型的处理器映射器-->  
    <bean class="org.springframework.web.servlet.mvc.annotation.RequestMappingHandlerMapping" /> 
     <!-- 启动Spring MVC的注解功能,配置annotation类型的处理器适配器-->  
    <bean class="org.springframework.web.servlet.mvc.annotation.RequestMappingHandlerAdapter" />  
    <!-- 配置视图解析器 -->  
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>
</beans>  

整合thymeleaf时,需要在maven映入坐标,并且在springmvc.xml添加

<!-- 自动扫描包 -->
<context:component-scan base-package="com.jwt.mvc.controller"/>

<!-- 配置Thymeleaf视图解析器 -->
<bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
    <property name="order" value="1"/>
    <property name="characterEncoding" value="UTF-8"/>
    <property name="templateEngine">
        <bean class="org.thymeleaf.spring5.SpringTemplateEngine">
            <property name="templateResolver">
                <bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
    
                    <!-- 视图前缀 -->
                    <property name="prefix" value="/WEB-INF/templates/"/>
    
                    <!-- 视图后缀 -->
                    <property name="suffix" value=".html"/>
                    <property name="templateMode" value="HTML5"/>
                    <property name="characterEncoding" value="UTF-8" />
                </bean>
            </property>
        </bean>
    </property>
</bean>

<!-- 
   处理静态资源,例如html、js、css、jpg
  若只设置该标签,则只能访问静态资源,其他请求则无法访问
  此时必须设置<mvc:annotation-driven/>解决问题
 -->
<mvc:default-servlet-handler/>

<!-- 开启mvc注解驱动 -->
<mvc:annotation-driven>
    <mvc:message-converters>
        <!-- 处理响应中文内容乱码 -->
        <bean class="org.springframework.http.converter.StringHttpMessageConverter">
            <property name="defaultCharset" value="UTF-8" />
            <property name="supportedMediaTypes">
                <list>
                    <value>text/html</value>
                    <value>application/json</value>
                </list>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>
  1. 当启动Tomcat服务器的时候,因为配置了load-on-startup标签,所以会创建DispatcherServlet对象,就会加载springmvc.xml配置文件
  2. 开启了注解扫描,那么HelloController对象就会被创建
  3. 从index.jsp发送请求,请求会先到达DispatcherServlet核心控制器,根据配置@RequestMapping注解找到执行的具体方法
  4. 根据执行方法的返回值,再根据配置的视图解析器,去指定的目录下查找指定名称的JSP文件
  5. Tomcat服务器渲染页面,做出响应

2.4 使用

在控制器层根据用户请求路径,返回页面

//交给spring处理
@Controller
public class HelloController {
  
    // @RequestMapping注解:处理请求和控制器方法之间的映射关系
    // localhost:8080/springMVC/
    @RequestMapping("/")
    public String index() {//控制器方法
        //设置视图名称
        return "index";
    }
}

index.html页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>首页</title>
    </head>
    <body>
        <h1>首页</h1>
        <h2>Hello World</h2>
    </body>
</html>

3.常用注解

为了让spring识别到注解需要在springmvc.xml添加

<!--方式一-->
<bean class="com.host.app.web.controller.MyController"/>
<!--方式二-->
< context:component-scan base-package = "com.host.app.web" />//路径写到controller的上一层(扫描包详解见下面浅析)

3.1@Controller

Controller 负责处理由DispatcherServlet 分发的请求,它把用户请求的数据经过业务处理层处理之后封装成一个Model ,然后再把该Model 返回给对应的View 进行展示。

3.2@RequestMapping

@RequestMapping源码

@Target({ElementType.TYPE, ElementType.METHOD}) //描述注解能够作用的位置
@Retention(RetentionPolicy.RUNTIME) //当前被描述的注解,会保留到class字节码文件中,并被`JVM`读取到
@Documented //描述注解是否被抽取到api文档中
@Mapping
public @interface RequestMapping {
    String name() default "";

    @AliasFor("path")
    String[] value() default {};

    @AliasFor("value")
    String[] path() default {};

    RequestMethod[] method() default {};

    String[] params() default {};

    String[] headers() default {};

    String[] consumes() default {};

    String[] produces() default {};
}

属性名 作用 说明
path 指定请求路径的url  
value value属性和path属性是一样的 @RequestMapping(“/login”) 相当@RequestMapping(value=”/login”)
method 指定该方法的请求方式 包括GET,POST,PUT,OPTIONS等 RequestMethod.GET
params 指定限制请求参数的条件  
headers 发送的请求中必须包含的请求头  

3.3 @PathVariable

Restful风格的URL

<a href="/user/1/roles/1"></a>
@Controller  
public class TestController {  
     @RequestMapping(value="/user/{userId}/roles/{roleId}",method = RequestMethod.GET)  
     public String getLogin(@PathVariable("userId") String userId,  
         @PathVariable("roleId") String roleId){  
         System.out.println("User Id : " + userId);  
         System.out.println("Role Id : " + roleId);  
         return "hello";  
     }  
     @RequestMapping(value="/product/{productId}",method = RequestMethod.GET)  
     public String getProduct(@PathVariable("productId") String productId){  
           System.out.println("Product Id : " + productId);  
           return "hello";  
     }  
     @RequestMapping(value="/javabeat/{regexp1:[a-z-]+}", method = RequestMethod.GET)  
     public String getRegExp(@PathVariable("regexp1") String regexp1){  
           System.out.println("URI Part 1 : " + regexp1);  
           return "hello";  
     }  
}

3.4 @RequestParam

  1. value:请求参数中的名称
  2. required:请求参数中是否必须提供此参数,默认值是true,必须提供
/**
* 接收请求
* @return
*/
@RequestMapping(path="/hello")
public String sayHello(@RequestParam(value="username",required=false)String name) {
    System.out.println("aaaa");
    System.out.println(name);
    return "success";
}

3.5 @ResponseBody

我们不需要返回一个页面,而是直接返回数据给到前端,该注解可以放在返回值前或者方法上,用于将返回值放在response体内,而不是返回一个页面

@Controller
@RequestMapping("/anno")
public class DemoAnnoController {
    @RequestMapping(value = "/index", method = RequestMethod.GET, produces = MediaType.TEXT_PLAIN_VALUE)
    public @ResponseBody
    String index(HttpServletRequest request) {
        return "url:" + request.getRequestURI() + " can access";
    }
}

请求路径

http://localhost:8080/spring-action-1.0-SNAPSHOT/anno/index

也可以将@ResponseBody注解放在方法上 ,所以现在前后端分离返回封装类的json数据基本上用@RestController它是@Controller和@ResponseBody的集成版本

3.6 @CookieValue

用于获取指定cookie的值

@RequestMapping(path="/hello")
public String sayHello(@CookieValue(value="JSESSIONID") String cookieValue) {
    System.out.println(cookieValue);
    return "success";
}

3.7 @RequestBody

主要用来接收前端传递给后端的json字符串中的数据的

    @PostMapping({"/login"})
    public ResponseBean login(@RequestBody @Validated GaeaUserDto dto) {
        return responseSuccessWithData(accessUserService.login(dto));
    }

4.请求参数绑定

意思是前端发送数据在controller怎么实现,SpringMVC的参数绑定过程是把表单提交的请求参数,作为控制器中方法的参数进行绑定的

4.1绑定方式

基本类型和 String 类型作为参数

Controller的形参为Interger id,它和请求参数的key一致,所以直接绑定成功 。如果不一样,请求参数key和controller方法的形参名称不一致时,需要使用@RequestParam注解才能将请求参数绑定成功 要指明该参数名

public String findItem(@RequestParam("itemId") Integer id)

<a href="param/testParam?id=006&username=keafmd">请求参数绑定</a>
@Controller
@RequestMapping("/param")
public class ParamController {

    @RequestMapping("/testParam")
    public String testParam(Integer id,String username){
        System.out.println("id:"+id);
        System.out.println("username:"+username);
        return "success";
    }

}

pojo类型作为参数 实体类需要实现序列化类 要求表单中参数名称和POJO类的属性名称保持一致

package com.Keafmd.domain;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

@Data
public class Account implements Serializable {

    private String username;
    private String password;
    private Double money;

    private User user;
}

@Controller
@RequestMapping("/param")
public class ParamController {

    /**
     * 请求参数绑定,把数据封装到javaBean的类中
     * @return
     */
    @RequestMapping("/saveAccount")
    public String testParam(Account account){
        System.out.println(account);
        return "success";
    }

}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>请求参数绑定</title>
</head>
<body>

<%--表单,把数据封装到Account类中--%>
<form action="param/saveAccount" method="post">
    姓名:<input type="text" name="username"/><br/>
    密码:<input type="text" name="password"/><br/>
    金额:<input type="text" name="money"/><br/>
    用户姓名:<input type="text" name="user.uname"/><br/>
    用户年龄:<input type="text" name="user.age"/><br/>
    <input type="submit" value="提交">
</form>

</body>
</html>

使用简单类型数组接收参数 使用POJO类型集合或数组接收参数 自定义参数绑定等

4.2 原始的参数绑定

Servlet中HttpServletRequest + HttpServletResponse

@RequestMapping("/httpServlet")
public void formPost(HttpServletRequest request, HttpServletResponse response) throws IOException{      
    String userAgent = request.getHeader("User-Agent");
    String host = request.getHeader("Host");
    String cacheControl = request.getHeader("Cache-Control");

    PrintWriter pw = response.getWriter();
    pw.println("User-Agent :"+ userAgent);
    pw.println("Host :" + host);
    pw.println("Cache-Control :" + cacheControl);

}

5.返回类型

5.1 ModelAndView

现在前后端分离后,后端都是以返回 JSON 数据为主,基本不用了

@RequestMapping("/book")
public ModelAndView getAllBook() {
    ModelAndView mv = new ModelAndView();
    List<Book> books = new ArrayList<>();
    Book b1 = new Book();
    b1.setId(1);
    b1.setName("三国演义");
    b1.setAuthor("罗贯中");
    books.add(b1);
    Book b2 = new Book();
    b2.setId(2);
    b2.setName("红楼梦");
    b2.setAuthor("曹雪芹");
    books.add(b2);
    //指定数据模型
    mv.addObject("bs", books);
    mv.setViewName("book");//指定视图名
    return mv;
}

5.2 void

不返回任何值

@RequestMapping("/test2")
@ResponseBody
public void test2(){
    //你的代码
}

重定向

@RequestMapping("/test1")
@ResponseBody
public void test1(HttpServletResponse resp){
    resp.setStatus(302);
    resp.addHeader("Location","/aa/index");
}

@RequestMapping("/test1")
@ResponseBody
public void test1(HttpServletResponse resp){
    resp.sendRedirect("/aa/index");
}

返回字符串

@RequestMapping("/test2")
@ResponseBody
public void test2(HttpServletResponse resp) throws IOException {
    resp.setContentType("application/json;charset=utf-8");
    PrintWriter out = resp.getWriter();
    List<Book> books = new ArrayList<>();
    Book b1 = new Book();
    b1.setId(1);
    b1.setName("三国演义");
    b1.setAuthor("罗贯中");
    books.add(b1);
    Book b2 = new Book();
    b2.setId(2);
    b2.setName("红楼梦");
    b2.setAuthor("曹雪芹");
    books.add(b2);
    String s = new Gson().toJson(books);
    out.write(s);
    out.flush();
    out.close();
}

5.3 String

逻辑视图名

@RequestMapping("/hello")
public String aaa(Model model) {
    model.addAttribute("username", "张三");
    return "hello";
}

重定向

@RequestMapping("/test4")
public String test4() {
    return "redirect:/aa/index";
}

就是字符串

@Controller
public class HelloController {
    @GetMapping("/hello")
    @ResponseBody
    public String hello() {
        return "hello provider!";
    }
}

5.4 json

@GetMapping("/user")
@ResponseBody
public User getUser() {
    User user = new User();
    List<String> favorites = new ArrayList<>();
    favorites.add("足球");
    favorites.add("篮球");
    user.setFavorites(favorites);
    user.setUsername("zhagnsan");
    user.setPassword("123");
    return user;
}
@GetMapping("/users")
@ResponseBody
public List<User> getALlUser() {
    List<User> users = new ArrayList<>();
    for (int i = 0; i < 10; i++) {
        User e = new User();
        e.setUsername("zhangsan:" + i);
        e.setPassword("pwd:" + i);
        users.add(e);
    }
    return users;
}

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦