springMVC学习--day01

1.概述

JavaEE中的三层架构:

表现层    web层    MVC它只是表现层的一种设计模型
业务层    service层
持久层    dao层

我们的开发架构一般都是基于两种形式,一种是C/S架构,也就是客户端/服务器,另一种是B/S架构,也就是浏览器服务器。在JavaEE开发中,几乎全都是基于B/S架构的开发。那么在B/S架构中,系统标准的三层架构包括:表现层、业务层、持久层

三层架构中,每一层都各司其职,接下来我们就说说每层都负责哪些方面:
表现层:
    也就是我们常说的web层。它负责接收客户端请求,向客户端响应结果,通常客户端使用http协议请求web层,web 需要接收http请求,完成http响应。
    表现层包括展示层和控制层:控制层负责接收请求,展示层负责结果的展示。
    表规层依赖业务层,接到客户端请求一般会调用业务层进行业务处理:并将处理结果响应给客户端。
    表现层的设计一般都使用,MVC模型。(MVC是表现层的设计模型:和其他层没有关系)

业务层:
    也就是我们常说的service层。它负责业务逻辑处理,和我们开发项目的需求息息相关。web层依赖业务层,但业务层不依赖web层;  

    业务层在业务处理时可能会依赖持久层,如果要对数据持久化需要保证事务一致性。(也就是我们说的,事务应该放到业务层来控制)

持久层:
    也就是我们是常说的dao层。负责数据持久化,包括数据层即数据库和数据访问层,数据库是对数据进行持久化的载体,数据访问层是业务层和持久层交互的接口,业                务层需要通过数据访问层将数据持久化到数据库中。通俗的讲,持久层就是和数据库交互,对数据库表进行增删改查的。

MVC模型:

M:model
V:view
C:controller

    MVC全名是Model View Controller,是模型(mode1)-视图(view)-控制器(controller)的缩写,是一种用于设计创健Web 应用程序表现层的模式。MVC中每个       部分各司其职:
  Model(模型):
  通常指的就是我们的数据模型。作用一般情况下用于封装数据(实体类就是一种模型)。
  View(视图)
  通常指的就是我们的jsp或者HTML。作用一般就是展示数据的。
  通常视图是依据模型数据创建的。
  Controller(控制器):
  是应用程序中处理用户交互的部分。作用一般就是处理程序逻错的。
  它相对于前两个不是很好理解,这里举个例子:
  例如:
  我们要保存一个用户的信息,该用户信息中包含了姓名,性别,年龄等等。
  这时候表单输入要求年龄必须是1-100之润的整数。姓名和性别不能为空。并且把数据填交到模型之中。
  此时除了js.的校验之外服务器端也应该有数据准确性的校验,那么校验就是控制器的该做的。
  当校验失败后,由控制器负责把错误页面展示给使用者。
  如果按验成劝也是控制器负责把款据填充到模型,并且调用业务层实现完整的业务需求。

2.springMVC环境搭建

使用IDEA创建maven Web项目:

 创建完毕之后在main文件夹下面新建两个文件夹java和Resources,并分别按照下图设置:

 导入所需jar包:

    <!--  spring-context:导入spring核心jar包  -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.1.2.RELEASE</version>
      <!--      <scope>test</scope>-->
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>5.1.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
        <version>5.1.2.RELEASE</version>
    </dependency>

先看看项目最终截图:(按照截图创建对应的文件)

创建配置文件springMvc.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--告知spring创建容器时要扫描的包-->
    <context:component-scan base-package="web"></context:component-scan>
<!--    配置spring的视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="viewResolver">
        <property name="prefix" value="/WEB-INF/pages/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>
</beans>

 配置web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
  <!--  配置Spring核心控制器-->
  <servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--    配置servlet初始化参数,告诉servlet  springmvc的配置文件位置-->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springMVC.xml</param-value>
    </init-param>
<!--    配置servlet启动顺序-->
    <load-on-startup>1</load-on-startup>
  </servlet>
<!--  过滤器-->
  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
<!--配置默认启动页-->
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file><welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file><welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  <display-name>Archetype Created Web Application</display-name>
</web-app>

启动页面代码添加超链接:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<body>
<h2>Hello World!</h2>
<a href="${pageContext.request.contextPath}/hello">访问SpringMVCD第一个案例</a>
</body>
</html>

编写Controller:

/**
 * springMVC的第一个控制器:他就是一个普通的java类
 */
//使用spring注解保证当前类由spring来管理
@Controller("helloController")
public class Hello {
    @RequestMapping("/hello")//请求的映射:他要和浏览器请求的url保持一致
    public String sayhello(){
        System.out.println("方法执行了");
        return "sucess";
    }
}

创建返回的jsp页面sucess.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
执行成功
</body>
</html>

配置TomCat访问项目:

 

 

案例过程分析

 3.组件

DispatcherServlet:前端控制器:

用户请求到达前端控制器,它就相当于mvc模式中的c,dispatcherservlet是整个流程控制的中心,由它调用其它组件处理用户的请求,diapatcherServlet的存在降低了组件之间的耦合性。根据web.xml里面配置的请求捕获规则对前端发送的请求进行捕获:例如下面的捕获规则是"/"表示捕获所有请求,并都转给名字为dispatcherServlet的servlet处理:

<!--  请求捕获-->
  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern><!--/表示捕获所有请求交给dispatcherServlet处理-->
  </servlet-mapping>

HandlerMapping:处理器映射器

HandlerMapping负责根据用户请求找到Handler即处理器(例如我们的Controller),springmvc提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。

对类中标记@ResquestMapping的方法进行映射,根据ResquestMapping定义的url匹配ResquestMapping标记的方法,匹配成功返回HandlerMethod对象给前端控制器,HandlerMethod对象中封装url对应的方法Method

配置如下:

<!--注解映射器 -->

<beanclass="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>

Handler:处理器

Handler 是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。

由于Handler涉及到具体的用户业务请求,所以一般情况需要程序员根据业务需求开发Handler。

HandlAdapter:处理器适配器

通过HandlerAdapter对处理器进行执行,把我们的处理器加工成 统一的对接形式,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。

对标记@ResquestMapping的方法进行适配。

 

配置如下:

<!--注解适配器 -->

<beanclass="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>

View Resolver:视图解析器

View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。

View:视图

springmvc框架提供了很多的View视图类型的支持,包括:jstlView、freemarkerView、pdfView等。我们最常用的视图就是jsp。

一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面

说明:在springmvc的各个组件中,处理器映射器、处理器适配器、视图解析器称为springmvc的三大组件。

需要用户开发的组件有handlerview

 <mvc:annotation-driven>

springmvc使用<mvc:annotation-driven>自动加载RequestMappingHandlerMappingRequestMappingHandlerAdapter,替代上面的注解,可用在springmvc.xml配置文件中使用<mvc:annotation-driven>替代注解处理器和适配器的配置。

<!-- 配置springMVC的注解驱动:加载映射器,适配器以及其他组件-->

<mvc:annotation-driven></mvc:annotation-driven>

4.@RequestMapping:

作用:

  定义请求url到处理器功能方法的映射(建立url和处理器方法的对应关系)

出现位置:

  写在类上:给访问url进行窄化处理.可以让我们的url变成模块化的形式;account/find;account/add;account/delete......

  写在方法上:和请求url绑定

属性:

  value:用于指定访问ur1和执行的方法之间的对应关系。

  method:用于限定请求的方式

  params:用于限定请求url必须的参数要求

  headers:用于限定请求消息头的信息

  注意:
  以上属性,只要有两个及以上同时出现,他们的关系是并列

实例代码:

处理器:

@RequestMapping("/test")
@Controller("helloController2")
public class _02requestMapping {
    @RequestMapping("/hello")
    public String sayhello() {
        System.out.println("/test下面的sayhello方法执行了");
        return "sucess";
    }

    @RequestMapping(value = "/methodTest", method = RequestMethod.POST)
    public String methodTest() {
        System.out.println("methodTest方法执行了");
        return "sucess";
    }

    //1.要求必须有名为name的参数
    @RequestMapping(value = "/paramsTest", params = "name")
    public String paramsTest1() {
        System.out.println("paramsTest方法执行了");
        return "sucess";
    }

    //2.要求必须有名为name的参数,且值是test
    @RequestMapping(value = "/paramsTest", params = "name=test")
    public String paramsTest2() {
        System.out.println("paramsTest方法执行了");
        return "sucess";
    }

    @RequestMapping(value = "/headersTest", headers = {"Accept-Language=zh-CN,zh;q=0.9"})
    public String headersTest() {
        System.out.println("headersTest方法执行了");
        return "sucess";
    }
}

前台jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<body>
<h2>Hello World!</h2>
<%--下面这种写法可以直接写处理器里面@requeatMapping配置的路径(类上的配置和方法上的配置的拼接)--%>
<%--注意!!!最前面不要加斜杠--%>
<%--不加斜杠的访问地址是正确的:http://localhost:8080/springMVC_war/test/hello--%>
<%--加斜杠的访问地址是错误的:http://localhost:8080/test/hello--%>
<%--1.@RequestMappin演示--%>
<a href="/test/hello">@RequestMapping错误写法:href="/test/hello"</a>
<br>
<a href="test/hello">@RequestMapping正确写法:href="test/hello"</a>
<br>
<%--2.method属性演示--%>
<a href="test/methodTest">测试@RequestMapping的method属性,get提交</a>
<br>
<form action="test/methodTest" method="post">
    <input type="submit" value="测试@RequestMapping的method属性,post提交">
</form>
<br>
<%--3.param属性演示--%>
<a href="test/paramsTest?name=test">param属性演示</a>
<%--4.headers属性演示--%>
<br>
<a href="test/headersTest">headers属性演示</a>
</body>
</html>

 5.参数绑定

示例:

<a href="getParam/findAccountById?acountId=1">最基本请求参数绑定--int类型</a>
@RequestMapping("/getParam")
@Controller("helloController3")
public class _03getParams {
    @RequestMapping("/findAccountById")
    public String findAccountById(Integer acountId) {
        System.out.println("参数:"+acountId);
        return "sucess";
    }
}

1.简单数据类型绑定

2.POJO类型参数绑定

3.POJO类型关联其他POJO类型参数绑定

4.POJO类型中数组类型参数绑定

5.POJO类型中List类型参数绑定

6.POJO类型中Map类型参数绑定

  涉及的三个实体类代码,都重写了toString方法,并添加了get和set方法,下面代码中省略:并且下面几种值的传递要求前后台参数名必须完全一样

public class Account implements Serializable {
    private Integer id;
    private String name;
    private Float money;
    private AddressPOJO addressPOJO;
    private String[] accountType;//[大额账户,隐私账户,冻结账户,小额账户]
}
/**
 * 一个用户的实体类,它可以包含多个账户
 */
public class User implements Serializable {
    private String username;
    private String password;
    private Integer age;
    private List <Account> accountList;
    private Map<String ,Account> accountMap;
}
public class AddressPOJO implements Serializable {
    private String provinceName;
    private String cityName;
}

前台JSP代码:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>请求参数绑定</title>
</head>
<body>
<%--1.简单数据类型绑定--%>
<a href="getParam/findAccountById?acountId=1">最基本请求参数绑定--int类型</a>
<%--2.POJO类型参数绑定--%>
<form action="getParam/saveAccount">
    账号名称:<input name="name" type="text"><br>
    账号金额:<input name="money" type="text"><br>
    <input type="submit" value="提交"><br>
</form>
<br>
<%--3.POJO类型关联其他POJO类型参数绑定--%>
<form action="getParam/saveAccount2">
    账号名称:<input name="name" type="text"><br>
    账号金额:<input name="money" type="text"><br>
    账号省份:<input name="addressPOJO.provinceName" type="text"><br>
    账号城市:<input name="addressPOJO.cityName" type="text"><br>
    <input type="submit" value="提交"><br>
</form>
<%--4.POJO类型中数组类型参数绑定--%>
<form action="getParam/saveAccount3">
    账号名称:<input name="name" type="text"><br>
    账号金额:<input name="money" type="text"><br>
    <label><input type="checkbox" value="大额账户" name="accountType">大额账户</label>
    <label><input type="checkbox" value="小额账户" name="accountType">小额账户</label>
    <label><input type="checkbox" value="冻结账户" name="accountType">冻结账户</label>
    <label><input type="checkbox" value="隐私账户" name="accountType">隐私账户</label>
    <br>
    <input type="submit" value="提交"><br>
</form>
<br>
<%--5.POJO类型中List类型参数绑定--%>
<form action="getParam/saveUser">
    用户姓名:<input name="username" type="text"><br>
    用户密码:<input name="password" type="password"><br>
    用户年龄:<input name="age" type="text"><br>
    账号1名称:<input name="accountList[0].name" type="text"><br>
    账号1金额:<input name="accountList[0].money" type="text"><br>
    账号2名称:<input name="accountList[1].name" type="text"><br>
    账号2金额:<input name="accountList[1].money" type="text"><br>
    <input type="submit" value="提交"><br>
</form>
<%--6.POJO类型中Map类型参数绑定--%>
<form action="getParam/saveUser2">
    用户姓名:<input name="username" type="text"><br>
    用户密码:<input name="password" type="password"><br>
    用户年龄:<input name="age" type="text"><br>
    账号1名称:<input name="accountMap['one'].name" type="text"><br>
    账号1金额:<input name="accountMap['one'].money" type="text"><br>
    账号2名称:<input name="accountMap['two'].name" type="text"><br>
    账号2金额:<input name="accountMap['two'].money" type="text"><br>
    <input type="submit" value="提交"><br>
</form>
</body>
</html>

Controller代码:

@RequestMapping("/getParam")
@Controller("helloController3")
public class _03getParams {
    //    1.最简单类型绑定
    @RequestMapping("/findAccountById")
    public String findAccountById(Integer acountId) {
        System.out.println("参数:" + acountId);
        return "sucess";
    }

    //    2.POJO类型绑定
    @RequestMapping("/saveAccount")
    public String saveAccount(Account account) {
        System.out.println("参数:" + account);
        return "sucess";
    }

    //3.POJO类型关联其他POJO类型
    @RequestMapping("/saveAccount2")
    public String saveAccount2(Account account) {
        System.out.println("参数:" + account);
        return "sucess";
    }
    //4.POJO类型关联其他POJO类型
    @RequestMapping("/saveAccount3")
    public String saveAccount3(Account account,String[] accountType) {
        System.out.println("参数:" + account);//数组accountType数据会自动填充到account之中
        return "sucess";
    }
//    5.POJO类型中List类型参数绑定
    @RequestMapping("/saveUser")
    public String saveUser(User user) {
        System.out.println("参数:" + user);
        return "sucess";
    }
//    6.POJO类型中Map类型参数绑定
    @RequestMapping("/saveUser2")
    public String saveUser2(User user) {
        System.out.println("参数:" + user);
        return "sucess";
    }
}

6.自定义类型转换器

需求:假设我们的user对象有一个日期属性,我们前台传递过来的的时候是String,那么,如果我们不做类型转化,就会报错:

public class User implements Serializable {
...
    private Date date;
}
<%--6.自定义类型转化器--%>
<form action="getParam/saveUser2">
    用户姓名:<input name="username" type="text"><br>
    用户密码:<input name="password" type="password"><br>
    创建日期:<input name="date" type="text"><br>
    <input type="submit" value="提交"><br>
</form>
//    7.自定义类型转换器
    @RequestMapping("/saveUser3")
    public String saveUser3(User user) {
        System.out.println("参数:" + user);
        return "sucess";
    }

 

前端控制器接收到请求后,找到注解形式的处理器适配器,对RequestMapping标记的方法进行适配,并对方法中的形参进行参数绑定。由于日期数据有很多种格式,所以springmvc没办法把字符串转换成日期类型。所以需要自定义参数绑定。springmvc这可以在处理器适配器上自定义Converter进行参数绑定。如果使用<mvc:annotation-driven/>可以在此标签上进行扩展。

配置自定义转化器步骤:

自定义转化器:

创建一个类实现Converter接口,并重写转换方法:

/**
 * 自定义类型转化器:把字符串转化成日期对象
 */
public class DateConverter implements Converter<String,Date> {
    @Override
    public Date convert(String s) {
        if (StringUtils.isNullOrEmpty(s)) {
            throw new NullPointerException("日期参数为空");
        }
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-mm-dd");
        try {
            return simpleDateFormat.parse(s);
        } catch (ParseException e) {
            throw new RuntimeException("请输入正确的日期字符串");
        }
    }
}

配置我们的自定义转换器:

在springMVC.xml里面添加:

<!--    配置自定义类型转换器:它的配置方式是采用注册服务的方式来实现的。-->

    <!-- 转换器配置 :将我们的自定义转化器注册到conversionService服务中-->
    <bean id="conversionService"
          class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <bean class="web.controller.converter.DateConverter"/><!--我们只需要更改这里为我们自定义的转换器路径即可,其他不用动-->
            </set>
        </property>
    </bean>
    <!-- 加载注解驱动时引用我们上面的转换服务 -->
    <mvc:annotation-driven conversion-service="conversionService"/>

7.访问原始Servlet对象

    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
    </dependency>
<%--访问原始Servlet对象--%>
访问原始Servlet对象
<a href="servletAPI/servlet">访问原始Servlet对象</a>
/**
 * 访问原始Servlet对象
 */
@RequestMapping("/servletAPI")
@Controller("helloController4")
public class _04servletAPI {
    @RequestMapping("/servlet")
    public String servlet(HttpServletRequest request, HttpServletResponse response) {
        System.out.println("request:" + request);
        System.out.println("response:" + response);
        System.out.println("Session:" + request.getSession());
        System.out.println("Context:" + request.getServletContext());
        return "sucess";
    }
}

 8.常用注解

@RequestParam

<a href="annotation/testRequestParam?id=1">@RequestParam</a>
    /**
     * 作用:
     *      把请求中指定名称的参数给控制器中的形参赋值。
     * 属性:
     *      value:请成参数中的名称。
     *      required:请求参数中是否必须提供此参数。默认值:true。表示必须提供,如果不提供将报错。
     */
    @RequestMapping("/testRequestParam")
    public String testRequestParam(@RequestParam(value = "id",required = false) Integer accountID) {
        System.out.println(accountID);
        return "sucess";
    }

@RequestBody

<a href="/annotation/testRequestBody?id=1&name=战兽山">@RequestBody--get提交</a>
<br>
<form action="annotation/testRequestBody" method="post">
    ID:<input name="id" value="1"><br>
    姓名:<input name="name" value="扎狗屎运"><br>
    <input type="submit" value="@RequestBody--post提交"><br>
</form>
 
    /**
     * 作用:
     *      用于获取请求体内容。直接使用得到是key=value&key=value...结构的数据。
     *      get请求方式不适用。
     * 属性:
     *      required:是需必须有请求体。默认值是:true。当取值为true时,get请求方或会报错。如果取值为false,get请求得到是nul1。
     */
    @RequestMapping("/testRequestBody")
    public String testRequestBody(@RequestBody(required = false) String body) {
        System.out.println(body);
        return "sucess";
    }

@PathVariable

<%--假如我们要删除一个账号,我们在url后面拼接了一个id,他的值是1--%>
<a href="annotation/testPathVariable/1">@PathVariable--删除</a>
    /**
     * 作用:
     * 用于绑定URL中的占位符。例如:请求url中/delete/{id},这个{id}就是url位符。
     * url支持占位符是spring3.0之后加入的。是springMVC支持rest风格url的重要标志。
     * 属性:
     * value:用于指定url中占位符的名称。
     * required:是否必须提供占位符。
     * <a href="annotation/testPathVariable/1">@PathVariable--删除</a>
     */
    @RequestMapping("/testPathVariable/{id}")//这里映射地址后面要加占位符(和url里面的内容匹配)
    public String testPathVariable(@PathVariable(required = false) Integer id) {
        System.out.println(id);
        return "sucess";
    }

@RequestHeader( 在实际开发中一般不怎么用)

<a href="annotation/testRequestHeader">@RequestHeader--获取消息头</a>
    /**
     * 作用:
     *      用于获取请求消息头。
     * 属性:
     *      value:提供消息头名称
     *      required:是否必须有此消息头
     */
    @RequestMapping("/testRequestHeader")
    public String testRequestHeader(@RequestHeader(value = "Accept-Language",required = false) String header) {
        System.out.println(header);//zh-CN,zh;q=0.9
        return "sucess";
    }

@CookieValue(不常用)

<a href="annotation/testCookieValue">@CookieValue--获取cookie的id</a>
    /**
     * 作用:
     *      用于把指定Cookie名称的值传入按制器方法参数。
     * 属性:
     *      value:指定cookie的名称。
     *      required:是否必须有此 cookie。
     */
    @RequestMapping("/testCookieValue")
    public String testCookieValue(@CookieValue("JSESSIONID") String value) {
        System.out.println(value);//A092463C19C4764986360FDC0D96B9D9
        return "sucess";
    }

@ModelAttribute(重点)

作用:
  该注解是SpringlMVC4.3版本以后新加入的。它可以用于修饰方法和参数。
  出现在方法上,表示当前方法会在控制器的方法执行之前,先执行。它可以修饰没有返回值的方法,也可以修饰有具体返回值的方法。
  出现在参数上,获取指定的数据给参数赋值。
属性:

  value:用天获取数据的.key。key可以是POJO的属性名称;也可以是map结构的key。
应用场景1:
  当表单提交数据不是完整的实体类数据时,保证没有提交数据的字段使用数据库对象原来的数据。
  例如:
  我们在编辑一个用户时,用户有一个用户名字段,该字段的值是不允许被修改的。在提交表单数据是肯定没有此字段的内容,一日更新会把该字段内容置为nu11,此时      就可以使用此注解解决问题。

用户账户类:

public class User implements Serializable {
    private String loginName;//登录名,不可修改
    private String userName;//昵称可以修改
    private String password;//密码,可以修改
}

前台代码:

<form action="annotation/testModelAttribute">
<%--    登录名:<input type="text" name="loginName"> 登录名不予展示和修改;--%>
    昵称:<input type="text" name="userName" ><br>
    密码:<input type="text" name="password" ><br>
    <input type="submit" value="ModelAttribute测试"><br>
</form><br>

后台接收代码:

    @RequestMapping("/testModelAttribute")
    public String testModelAttribute(User user) {
        System.out.println("接收到请求中的用户信息:"+user);//{loginName=null, userName='***', password='***'}
        return "sucess";
    }

如果我们就这样把新的数据插入到数据库,就会导致loginName=null,我们这时候可以使用注解 @ModelAttribute:

    @ModelAttribute//加在方法上面,该方法会在这个Controller所有的方法被调用之前执行,因此我们可以在这个方法里面去数据库查询出最全面的用户数据,并返回一个User对象
    public User testModelAttributeBeforeController() {
        User_testModelAttribute user = new User_testModelAttribute();
        user.setLoginName("1111");
        user.setUserName("李虎明");
        user.setPassword("666666");
        System.out.println("假设是数据库查询出来的用户信息"+user);
        return user;
    }

加上上面的方法之后,前台用户传过来的属性都会得到更新,而没有传过来的例如loginName就会使用从数据库查询出来的原有值

应用场景2:

 给项目添加"保质期"(定时炸弹,或者说隐形BUG)

例如甲公司给乙公司做了一个接口,接口做完上线,然后乙公司没有给钱...咋办?别怕,假如项目2020年6月15上线,我们可以把我们的接口配置成两个月后就"报错",如果两个月还没有收到钱,那么不好意思,接口报错,你们用不了了...

假设我们整个项目的核心接口或者程序入口在下面这个Controller:
@RequestMapping("/annotation")
@Controller("annotation")
public class _05Annotation {

...
...其他各种业务接口省略
...
    @ModelAttribute//添加该注解之后,在每次调用该Controller里面的接口之前,都要先执行下面的方法
    protected void setBaseModel() {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Date date = new Date();//获取当前日期
        Date finalDate = null;
        try {
            finalDate = simpleDateFormat.parse("2020-08-24");//设置到期时间
        } catch (ParseException e) {
            e.printStackTrace();
        }
        if (date.compareTo(finalDate) > 0) {
            throw new RuntimeException("已过试用期限,如需继续使用,请与商务人员联系!");//抛出异常,程序不再往下执行,其他接口都调用不了
        }
    }
}

 9.补充:

post请求中文乱码解决:

web.xml:

    <!--配置springMVC编码过滤器-->
    <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><!--启动过滤器-->
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <!--过滤所有请求-->
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    

springMVC.xml:

    <!--设置静态资源不过滤编码格式-->
    <mvc:resources location="/css/" mapping="/css/**"/><!--样式-->
    <mvc:resources location="/images/" mapping="/images/**"/><!--图片-->
    <mvc:resources location="/scripts/" mapping="/javascript/**"/><!--javascrip-->
 
原文地址:https://www.cnblogs.com/luzhanshi/p/13263538.html