Spring mvc 框架 学习整理,如有错误,欢迎指出!

Springbootmvc三層架構的編寫順序_MVC


Spring mvc

  • 一、SpringMVC 的基本概念
  • 1.关于三层架构和 MVC
  • 1.1 三层架构
  • 1.2 MVC 模型
  • 2.SpringMVC 是什么
  • 二、SpringMVC 的入门
  • 1.Springmvc环境搭建
  • 2、SpringMVC 的请求响应流程
  • 三、RequestMapping 注解
  • 四、请求参数的绑定
  • 1、基本类型和 String 类型作为参数
  • 2、POJO 类型作为参数
  • 3、POJO 类中包含集合类型参数
  • 4、过滤器解决中文乱码问题
  • 5、自定义类型转换器
  • 五、常用注解


一、SpringMVC 的基本概念

1.关于三层架构和 MVC
1.1 三层架构
  • 表现层:
    也就是我们常说的web层。它负责接收客户端请求,向客户端响应结果,通常客户端使用http协议请求web 层,web 需要接收 http 请求,完成 http 响应。表现层包括展示层和控制层:控制层负责接收请求,展示层负责结果的展示。表现层依赖业务层,接收到客户端请求一般会调用业务层进行业务处理,并将处理结果响应给客户端。表现层的设计一般都使用 MVC 模型。(MVC 是表现层的设计模型,和其他层没有关系)
  • 业务层:
    也就是我们常说的 service 层。它负责业务逻辑处理,和我们开发项目的需求息息相关。web 层依赖业务层,但是业务层不依赖 web 层。业务层在业务处理时可能会依赖持久层,如果要对数据持久化需要保证事务一致性。(也就是我们说的,事务应该放到业务层来控制)
  • 持久层:
    也就是我们是常说的 dao 层。负责数据持久化,包括数据层即数据库和数据访问层,数据库是对数据进行持久化的载体,数据访问层是业务层和持久层交互的接口,业务层需要通过数据访问层将数据持久化到数据库中。通俗的讲,持久层就是和数据库交互,对数据库表进行曾删改查的。
1.2 MVC 模型
  • MVC 全名是 Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,
    是一种用于设计创建 Web 应用程序表现层的模式。MVC 中每个部分各司其职:
    1、Model(模型):
    通常指的就是我们的数据模型。作用一般情况下用于封装数据。
    2、View(视图):
    通常指的就是我们的 jsp 或者 html。作用一般就是展示数据的。通常视图是依据模型数据创建的。
    3、Controller(控制器):
    是应用程序中处理用户交互的部分。作用一般就是处理程序逻辑的。
2.SpringMVC 是什么
  • SpringMVC 是一种基于 Java 的实现 MVC 设计模型的请求驱动类型的轻量级 Web 框架,属于 Spring FrameWork 的后续产品,已经融合在 Spring Web Flow 里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。使用 Spring 可插入的 MVC 架构,从而在使用 Spring 进行 WEB 开发时,可以选择使用 Spring的 Spring MVC 框架或集成其他 MVC 开发框架,如 Struts1(现在一般不用),Struts2 等。
  • SpringMVC 已经成为目前最主流的 MVC 框架之一,并且随着 Spring3.0 的发布,全面超越 Struts2,成为最优秀的 MVC 框架。
    它通过一套注解,让一个简单的 Java 类成为处理请求的控制器,而无须实现任何接口。同时它还支持RESTful 编程风格的请求。

二、SpringMVC 的入门

Springbootmvc三層架構的編寫順序_mvc_02

1.Springmvc环境搭建
  • 1、创建maven工程,选择webapp项目
  • 2、导入依赖
    这里我们把版本统一锁定为:5.0.2,以后也方便版本的更改
<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <!--1、版本锁定-->
    <spring.version>5.0.2.RELEASE</spring.version>
  </properties>

  <!--2、导入依赖-->
  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.5</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>jsp-api</artifactId>
      <version>2.0</version>
    <scope>provided</scope>
    </dependency>
  </dependencies>
  • 3、在web.xml中配置核心控制器
<web-app>
  <display-name>Archetype Created Web Application</display-name>

  <!--3、前端控制器配置-->
  <servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <!--加载配置文件-->
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <!--上面的servlet这个类正常的是:当第一次接收到请求时候创建,
    此配置就是:当启动服务器时候,就可以给我们创建servlet对象,从而帮助我们加载springmvc.xml,从而就可以扫描创建对象-->
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <!-- /:代表不论发送什么请求都会经过控制器-->
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>
  • 4、创建 spring mvc 的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">
    <!--开启注解扫描-->
    <context:component-scan base-package="cn.itcast"/>
    <!--视图解析器-->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
    <!--开启springmvc注解的支持-->
    <mvc:annotation-driven/>
</beans>
  • 编写控制器并使用注解配置
//控制器类
@Controller  //把这个类交给IOC容器管理
public class HelloController {
    /**
     * 入门案例
     * @return
     */                             
    @RequestMapping(value="/hello")
    public String sayHello(){
        System.out.println("hello");
        return "success"; //我们就把pages包下的success.jsp返回,怎么去找呢?
        // 我们就要在配置文件配置视图解析器
    }
}
  • 在WEB-INF/pages文件夹下创建:success.jsp
    很简单,返回:入门成功
<%--
  Created by IntelliJ IDEA.
  User: SS
  Date: 2021/3/27
  Time: 15:32
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h3>入门成功</h3>
</body>
</html>
  • 把我们的工程部署到:tomcat服务器上
  • 当我们启动服务器,会默认访问:webapp文件夹下的:index.jsp,所以我们在index.jsp添加一个超链接,请求路径就是上面控制器接收路径:hello
<a href="hello">超链接</a>

上面入门案例的流程分析:

Springbootmvc三層架構的編寫順序_java_03


大致流程:

  • 1、启动服务器,加载一些配置文件:web.xml,因为我们在web.xml 配置了<load-on-startup>1</load-on-startup>,也就是在服务器启动的时候,就会给我们创建:DispatcherServlet对象
  • 2、DispatcherServlet对象创建了之后,springmvc配置文件也就被加载了,那么springmvc里面的配置也会生效:注解扫描、视图解析器等
  • 3、注解扫描开启后,那我们的控制器 HelloController 就会被IOC容器管理,同时容器里面的添加注解的方法,也可以响应对应的请求

详细流程:

  • 1、启动服务器,DispatcherServlet前端控制器初始化完成,当我们点击超链接发送请求到我们的前端控制器
  • 2、DispatcherServlet前端控制器根据请求的路径:hello,来查找对应的控制器方法:sayHello()来处理请求,返回一个:success
  • DispatcherServlet前端控制器去找视图解析器:InternalResourceViewResolver,然后找到:success.jsp,相响应给客户端
2、SpringMVC 的请求响应流程

Springbootmvc三層架構的編寫順序_MVC_04

  • 1、用户发送请求,DispatcherServlet前端控制器接受到请求,然后调用HandlerMapping处理映射器
  • 2、HandlerMapping处理映射器会根据请求路径来查找对用的处理器,然后返回处理器对象及处理器拦截器(如果有则生成)
  • 3、DispatcherServlet调用HandlerAdapter处理器适配器,HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)
  • 4、Controller执行完成返回ModelAndView,HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
  • 5、DispatcherServlet将ModelAndView传给ViewReslover视图解析器,ViewReslover解析后返回具体View
  • DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中),DispatcherServlet响应用户

三、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 {};

    RequestMethod[] method() default {};

    String[] params() default {};

    String[] headers() default {};

    String[] consumes() default {};

    String[] produces() default {};
}
  • 1、出现位置
    类上:请求 URL 的第一级访问目录。此处不写的话,就相当于应用的根目录。写的话需要以/开头。它出现的目的是为了使我们的 URL 可以按照模块化管理
    方法上:请求 URL 的第二级访问目录,用于建立请求 URL 和处理请求方法之间的对应关系。
  • 2、method:设置请求方式,get/post
  • 3、params :指定限制请求参数的条件
  • 4、headers :发送的请求中必须包含的请求头
  • 用法:
/**
     * 入门案例
     * @return
     */                              //method :设置请求方式
//    @RequestMapping(value="/hello",method = {RequestMethod.GET})  //header请求头
    @RequestMapping(value="/hello",params = "username=hehe",headers = "Accept")
    public String sayHello(){
        System.out.println("hello");
        return "success"; //我们就把pages包下的success.jsp返回,怎么去找呢?
        // 我们就要在配置文件配置视图解析器
    }

四、请求参数的绑定

1、基本类型和 String 类型作为参数
  • jsp 代码:
<%--1、请求参数绑定--%>
      <a href="param/testParam?username=hehe&password=123">参数绑定</a>
  • 控制器代码
@Controller
@RequestMapping("/param")
public class paramController {
    /**
     * 请求参数绑定入门
     * @return
     */
    @RequestMapping(path ="/testParam")
    public String testParam(String username,String password){
        System.out.println("执行了testParam  "+username+password);
        return "success";
    }
}
2、POJO 类型作为参数
  • 1、创建Account和User实体类
public class Account implements Serializable {
    private String username;
    private String password;
    private Double money;
    private User user;
/*
*  省略get/set/toString
*/
}
public class User implements Serializable {
    private String uname;
    private Integer age;
    /*
*  省略get/set/toString
*/
}
}
  • jsp代码
<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.name"><br>
          <input type="submit" value="提交">
      </form>
  • 控制器代码
@RequestMapping(path ="/saveAccount")
    public String saveAccount(Account account){
        System.out.println(account);
        return "success";
    }
3、POJO 类中包含集合类型参数
public class Account implements Serializable {
    private String username;
    private String password;
    private Double money;

    private List<User> list;
    private Map<String,User> map;
/*
* 省略get/set/toString方法
*/
}
  • jsp代码
<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="list[0].uname"><br>
          用户年龄:<input type="text" name="list[0].age"><br>

          用户姓名:<input type="text" name="map['one'].uname"><br>
          用户年龄:<input type="text" name="map['one'].age"><br>
          <input type="submit" value="提交">
      </form>
  • 控制器代码:
@RequestMapping(path ="/saveAccount")
    public String saveAccount(Account account){
        System.out.println(account);
        return "success";
    }
4、过滤器解决中文乱码问题
  • 我们上面的表单提交方式如果为:post,那么我们控制台打印输出的中文信息都乱码,所以我们要在:web.xml 配置过滤器
<!--配置过滤器,解决中文乱码问题-->
  <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>
5、自定义类型转换器
  • 我们通过页面提交的任何数据都是字符串类型的,但是我们在后台依然可以把数据接受到,并且可以把数据封装到我们的实体类属性,对应起来,这就说明:Spring MVC 框架,会自动帮我们把提交的数据进行转换
  • 但这种转换并不是万能的,不会出错的,例如字符串转换日期格式:当我们提交字符串为 yyyy/mm/dd格式,并不会转换出错;但是我们提交的字符串为 yyyy-mm-dd 转换就会出错,springmvc框架无法转换,那这就需要我们自定义转换

步骤如下:

  • 1、定义一个类,实现 Converter 接口,该接口有两个泛型,S:表示接受的类型,T:表示目标类型
    我们可以看到,Converter被@FunctionalInterface修饰,它是一个函数式接口
@FunctionalInterface
public interface Converter<S, T> {
    @Nullable
    T convert(S var1);
}
  • 2、定义StringtoDateConverter类实现 Converter
/*
字符串->日期
 */
public class StringtoDateConverter implements Converter<String,Date> {
    /**
     *
     * @param s 传入进来的字符串
     * @return
     */
    @Override
    public Date convert(String s) {
        if (s==null){
            throw new RuntimeException("请传入日期");
        }
        DateFormat df = new SimpleDateFormat("yyyy-mm-dd");
        //字符串转换为日期
        try {
            return df.parse(s);
        } catch (ParseException e) {
            throw new RuntimeException("数据类型转换错误");
        }
    }
}
  • 在springmvc.xml中配置,自定义类型转换器,就是把咱写的这个StringtoDateConverter给加入到spring mvc框架转换器里面
<!--自定义类型转换器-->
    <bean id="conversionservice" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <bean class="cn.itcast.utils.StringtoDateConverter"></bean>
            </set>
        </property>
    </bean>
  • 让我们上面配置的自定义类型转换器生效
<!--开启springmvc注解的支持-->
    <mvc:annotation-driven conversion-service="conversionservice"/>

五、常用注解

  • 1、RequestParam
    作用:
    把请求中指定名称的参数给控制器中的形参赋值。
    属性:
    value:请求参数中的名称。
    required:请求参数中是否必须提供此参数。默认值:true。表示必须提供,如果不提供将报错。
    使用:
jsp代码:
 <a href="anno/testRequestParam?name=哈哈">RequestParam</a>

控制器代码:
 @RequestMapping("/testRequestParam")
    public String testRequestParam(@RequestParam(name="name") String username){
        System.out.println("执行了...");
        System.out.println(username);
        return "success";
    }
  • 2、RequestBody
    作用:
    用于获取请求体内容。直接使用得到是 key=value&key=value…结构的数据。get 请求方式不适用。
    属性:
    required:是否必须有请求体。默认值是:true。当取值为 true 时,get 请求方式会报错。如果取值为 false,get 请求得到是 null。
    使用:
jsp代码:
<form action="anno/testRequestBody" method="post">
        用户姓名:<input type="text" name="username" /><br/>
        用户年龄:<input type="text" name="age" /><br/>
        <input type="submit" value="提交" />
</form>

控制器代码:
  @RequestMapping("/testRequestBody")
    public String testRequestBody(@RequestBody String body){
        System.out.println("执行了...");
        System.out.println(body);
        return "success";
    }
  • 3、PathVaribale
    作用:
    用于绑定 url 中的占位符。例如:请求 url 中 /delete/{id},这个{id}就是 url 占位符。
    url 支持占位符是 spring3.0 之后加入的。是 springmvc 支持 rest 风格 URL 的一个重要标志。
    属性:
    value:用于指定 url 中占位符名称。
    required:是否必须提供占位符。
    使用:
jsp代码:
 <a href="anno/testPathVariable/10">testPathVariable</a>
 
控制器代码:
    @RequestMapping(value="/testPathVariable/{sid}")
    public String testPathVariable(@PathVariable(name="sid") String id){
        System.out.println("执行了...");
        System.out.println(id);
        return "success";
    }
  • 4、ModelAttribute
    作用:
    该注解是 SpringMVC4.3 版本以后新加入的。它可以用于修饰方法和参数。出现在方法上,表示当前方法会在控制器的方法执行之前,先执行。它可以修饰没有返回值的方法,也可以修饰有具体返回值的方法。出现在参数上,获取指定的数据给参数赋值。
    属性:
    value:用于获取数据的 key。key 可以是 POJO 的属性名称,也可以是 map 结构的 key。
    应用场景:
    当表单提交数据不是完整的实体类数据时,保证没有提交数据的字段使用数据库对象原来的数据。
    例如:
    我们在编辑一个用户时,用户有一个创建信息字段,该字段的值是不允许被修改的。在提交表单数据是肯定没有此字段的内容,一旦更新会把该字段内容置为 null,此时就可以使用此注解解决问题
    使用:
jsp代码:
<form action="anno/testModelAttribute" method="post">
        用户姓名:<input type="text" name="uname" /><br/>
        用户年龄:<input type="text" name="age" /><br/>
        <input type="submit" value="提交" />
    </form>
控制器代码:
  /**
     * ModelAttribute注解
     * @return
     */
    @RequestMapping(value="/testModelAttribute")
    public String testModelAttribute(@ModelAttribute("abc") User user){
        System.out.println("testModelAttribute执行了...");
        System.out.println(user);
        return "success";
    }

    @ModelAttribute
    public void showUser(String uname, Map<String,User> map){
        System.out.println("showUser执行了...");
        // 通过用户查询数据库(模拟)
        User user = new User();
        user.setUname(uname);
        user.setAge(20);
        user.setDate(new Date());
        map.put("abc",user);
    }

    /**
     * 该方法会先执行

    @ModelAttribute
    public User showUser(String uname){
        System.out.println("showUser执行了...");
        // 通过用户查询数据库(模拟)
        User user = new User();
        user.setUname(uname);
        user.setAge(20);
        user.setDate(new Date());
        return user;
    }
     */
  • 5、SessionAttribute
    作用:
    用于多次执行控制器方法间的参数共享。
    属性:
    value:用于指定存入的属性名称
    type:用于指定存入的数据类型。
    使用:
@Controller
@RequestMapping("/anno")
@SessionAttributes(value={"msg"})   // 把msg=美美存入到session域对中
public class AnnoController {
    /**
     * SessionAttributes的注解
     * @return
     */
    @RequestMapping(value="/testSessionAttributes")
    public String testSessionAttributes(Model model){
        System.out.println("testSessionAttributes...");
        // 底层会存储到request域对象中
        model.addAttribute("msg","美美");
        return "success";
    }

    /**
     * 获取值
     * @param modelMap
     * @return
     */
    @RequestMapping(value="/getSessionAttributes")
    public String getSessionAttributes(ModelMap modelMap){
        System.out.println("getSessionAttributes...");
        String msg = (String) modelMap.get("msg");
        System.out.println(msg);
        return "success";
    }

    /**
     * 清除
     * @param status
     * @return
     */
    @RequestMapping(value="/delSessionAttributes")
    public String delSessionAttributes(SessionStatus status){
        System.out.println("getSessionAttributes...");
        status.setComplete();
        return "success";
    }
}

(稍后继续更新)