Spring MVC入门案例和常用注解

Spring MVC第一天

一、三层架构和MVC

三层架构:

表现层:

也就是我们常说的web层。它负责接收客户端请求,向客户端响应结果,通常客户端使用http协议请求web 层,web 需要接收 http 请求,完成 http 响应。

表现层包括展示层和控制层:控制层负责接收请求,展示层负责结果的展示。

表现层依赖业务层,接收到客户端请求一般会调用业务层进行业务处理,并将处理结果响应给客户端。

表现层的设计一般都使用 MVC 模型。(MVC 是表现层的设计模型,和其他层没有关系)

业务层:

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

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

持久层:

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

image-20210723092337751

MVC 模型

MVC 全名是 Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,是一种用于设计创建 Web 应用程序表现层的模式。MVC 中每个部分各司其职:

Model(模型):

通常指的就是我们的数据模型。作用一般情况下用于封装数据。

View(视图):

通常指的就是我们的 jsp 或者 html。作用一般就是展示数据的。通常视图是依据模型数据创建的。

Controller(控制器):

是应用程序中处理用户交互的部分。作用一般就是处理程序逻辑的。

它相对于前两个不是很好理解,这里举个例子:

例如:

我们要保存一个用户的信息,该用户信息中包含了姓名,性别,年龄等等。

这时候表单输入要求年龄必须是 1~100 之间的整数。姓名和性别不能为空。并且把数据填充到模型之中。

此时除了 js 的校验之外,服务器端也应该有数据准确性的校验,那么校验就是控制器的该做的。

当校验失败后,由控制器负责把错误页面展示给使用者。

如果校验成功,也是控制器负责把数据填充到模型,并且调用业务层实现完整的业务需求。

spring mvc框架介绍:

image-20210723092801884

二、入门案例

需求分析:

image-20210723093945488

前期配置:

1、pom.xml导坐标

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>cn.itcast</groupId>
  <artifactId>springmvc_day01_01_start</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>springmvc_day01_01_start Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <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>
    <!-- 版本锁定 -->
    <spring.version>5.0.2.RELEASE</spring.version>
  </properties>

  <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>


</project>

2、完善项目结构

image-20210723102218408

3、在web.xml中配置前端控制器

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>

  <!--前端的控制器-->
  <servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  </servlet>
<servlet-mapping>
  <servlet-name>dispatcherServlet</servlet-name>
  <url-pattern>/</url-pattern>
</servlet-mapping>


</web-app>

4、部署Tomcat

img

入门案例代码

1、先看一下整个项目结构

image-20210723120334873

index.jsp代码


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

    <h3>入门程序</h3>

    <a href="hello">哈哈哈</a>
</body>
</html>

控制器代码:

package cn.itcast.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

//控制器
@Controller
public class HelloController {

    @RequestMapping(path = "/hello")
    public String sayHello(){
        System.out.println("Hello Spring MVC");
        return "success";
    }
}

success.jsp代码


<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h3>.....入门程序success</h3>
</body>
</html>

即index.jsp跳到控制器,再根据控制器里面的视图解析跳到success.jsp

springmvc.xml配置文件代码

<?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">

    <!--注解和mvc的空间约束-->

    <!--要先在web.xml中配置加载springmvc.xml,不然不加载,扫描无效,注解都没有用-->
    <!--告诉spring要扫描的包-->
    <context:component-scan base-package="cn.itcast"></context:component-scan>

    <!--配置视图解析对象-->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--告诉文件目录-->
        <property name="prefix" value="/WEB-INF/pages/"></property>
        <!--告诉文件的后缀名-->
        <property name="suffix" value=".jsp"></property>
    </bean>

    <!--开启springmvc框架注解的支持-->
    <mvc:annotation-driven/>
</beans>

web.xml文件配置

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>

  <!--前端的控制器-->
  <servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

    <!--加载springmvc配置文件-->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
<servlet-mapping>
  <servlet-name>dispatcherServlet</servlet-name>
  <url-pattern>/</url-pattern>
</servlet-mapping>


</web-app>

@RequestMapping注解
作用:

用于建立请求 URL 和处理请求方法之间的对应关系。

出现位置:
类上:
请求 URL 的第一级访问目录。此处不写的话,就相当于应用的根目录。写的话需要以/开头。
它出现的目的是为了使我们的 URL 可以按照模块化管理

方法上:
请求 URL 的第二级访问目录。

属性:
value:用于指定请求的 URL。它和 path 属性的作用是一样的。
method:用于指定请求的方式。
params:用于指定限制请求参数的条件。它支持简单的表达式。要求请求参数的 key 和 value 必须和
配置的一模一样。


例如:
params = {"accountName"},表示请求参数必须有 accountName
params = {"moeny!100"},表示请求参数中 money 不能是 100。
headers:用于指定限制请求消息头的条件。


注意:
以上四个属性只要出现 2 个或以上时,他们的关系是与的关系。

使用示例:

控制器代码:

package cn.itcast.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

//控制器
@Controller
@RequestMapping(path = "/user")
public class HelloController {

    @RequestMapping(path = "/hello")
    public String sayHello(){
        System.out.println("Hello Spring MVC");
        return "success";
    }

    @RequestMapping(path = "/test")
    public String testRequestMapping(){
        System.out.println("Test RequestMapping");
        return "success";
    }
}

index.jsp


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

    <h3>入门程序</h3>

    <%--<a href="hello">哈哈哈</a>--%>
    <a href="user/test" >入门程序入口</a>
</body>
</html>

属性:value和path是一样的。

method 属性的示例:

 @RequestMapping(value = "/test",method = {RequestMethod.POST})//指定路径和请求方式为post
    public String testRequestMapping(){
        System.out.println("Test RequestMapping");
        return "success";
    }

image-20210723150606887

params 属性的示例:

控制器

 @RequestMapping(value = "/test",params = {"user"})
    public String testRequestMapping(){
        System.out.println("Test RequestMapping");
        return "success";
    }

index.jsp

<a href="user/test?user=xixi" >入门程序入口</a>

image-20210723150904273

index.jsp

 <a href="user/test?user=xixi" >入门程序入口</a>

控制器

 @RequestMapping(value = "/test",params = {"user=haha"})
    public String testRequestMapping(){
        System.out.println("Test RequestMapping");
        return "success";
    }

image-20210723151253167

入门程序的流程总结:

设置成1,是每次启动服务器都会创建DispatcherServlet对象

image-20210723152546560

image-20210723153427624

入门案例使用的组件介绍:

1、DispatcherServlet:前端控制器

用户请求到达前端控制器,它就相当于 mvc 模式中的 c,dispatcherServlet 是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet 的存在降低了组件之间的耦合性。

2、 HandlerMapping:处理器映射器

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

3 、Handler:处理器

它就是我们开发中要编写的具体业务控制器。由 DispatcherServlet 把用户请求转发到 Handler。由Handler 对具体的用户请求进行处理

4 、HandlAdapter:处理器适配器

通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。

image-20210723203539955

5、 View Resolver:视图解析器

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

< mvc:annotation-driven>说明

在 SpringMVC 的各个组件中,处理器映射器、处理器适配器、视图解析器称为 SpringMVC 的三大组件。
使 用 <mvc:annotation-driven> 自动加载 RequestMappingHandlerMapping (处理映射器) 和
RequestMappingHandlerAdapter ( 处 理 适 配 器 ) , 可 用 在 SpringMVC.xml 配 置 文 件 中 使 用<mvc:annotation-driven>替代注解处理器和适配器的配置。

三、请求参数的绑定

零散参数的传递

param.jsp


<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <a href="param/testParam?user=浩浩&pwd=root">请求绑定参数</a>
</body>
</html>

控制器

package cn.itcast.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/*请求参数绑定*/
@Controller
@RequestMapping("/param")
public class ParamController {

    /*请求绑定参数入门*/
    @RequestMapping("/testParam")
    public String testParam(String user,String pwd){
        System.out.println("zhixing.........");
        System.out.println("用户:"+ user);
        System.out.println("密码:"+ pwd);
        return "success";
    }
}

请求参数绑定实体类

<form action="param/testJavaBean" method="post">
     <%--name里面的值要和实体类的属性名保持一致--%>
     姓名:<input type="text" name="name"><br>
     密码:<input type="text" name="password"><br>
     金额:<input type="text" name="money"><br>
     <input type="submit" value="提交"><br>

 </form>

请求参数绑定实体类里面有引用类型

 <form action="param/testJavaBean" method="post">

     姓名:<input type="text" name="name"><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="提交"><br>

  </form>

实体类

在Account中引用User,生成它的get/set方法,并且重新生成Account的tostring方法。在表单name属性里面写user.它的属性名称

post请求乱码,但是get不会乱码,那怎么解决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>

这样就解决了

请求绑定集合类型

Account实体类

package cn.itcast.domain;

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

public class Account implements Serializable {
    private String name;
    private String password;
    private Double money;
    
    private List<User> list;
    private Map<String,User> map;

    public List<User> getList() {
        return list;
    }

    public void setList(List<User> list) {
        this.list = list;
    }

    public Map<String, User> getMap() {
        return map;
    }

    public void setMap(Map<String, User> map) {
        this.map = map;
    }
    /* private User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }*/

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Double getMoney() {
        return money;
    }

    public void setMoney(Double money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "name='" + name + '\'' +
                ", password='" + password + '\'' +
                ", money=" + money +
                ", list=" + list +
                ", map=" + map +
                '}';
    }
}

param.jsp

 <%--把数据封装Account中,类中存在list和Map集合--%>
   <form action="param/testJavaBean" method="post">

       姓名:<input type="text" name="name"><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="提交"><br>

   </form>

控制器类

package cn.itcast.controller;

import cn.itcast.domain.Account;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/*请求参数绑定*/
@Controller
@RequestMapping("/param")
public class ParamController {

    /*请求绑定参数入门*/
    @RequestMapping("/testParam")
    public String testParam(String user,String pwd){
        System.out.println("zhixing.........");
        System.out.println("用户:"+ user);
        System.out.println("密码:"+ pwd);
        return "success";
    }

    /*请求绑定javabean类*/
    @RequestMapping("/testJavaBean")
    public String testJavaBean(Account account){
        System.out.println("zhixing.........");
        System.out.println(account);
        return "success";
    }
}

自定义类型转换器

只要是页面提交的数据都是字符串,

日期格式的2020/11/11,框架会自动帮你转换。但是2020-11-11这样的日期,框架就没法帮你转换了

1、创建一个类,让其实现类型转换功能

package cn.itcast.utils;

import org.springframework.core.convert.converter.Converter;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/*
* 将字符串转换为日期
* */
public class StringToDateConverter implements Converter<String, Date> {

    //String s 代表传入进来的字符串
    @Override
    public Date convert(String s) {
        if (s == null) {
            throw new RuntimeException("请传入数据");
        }
        try {
            DateFormat df=new SimpleDateFormat("yyyy-MM-dd");
            //将字符串解析为日期
          return   df.parse(s);
        } catch (Exception e) {
            throw new RuntimeException("数据格式有误");
        }

    }
}

2、在springmvc.xml中配置自定义的转换器


    <!--配置自定义类型转换器-->
    <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/>   <!--适配器,处理器,映射器生效-->

    <!--让类型转换器生效-->
    <mvc:annotation-driven conversion-service="conversionService"/>

想拿到servlet原生的API

param.jsp

 <a href="param/testServlet">获取servlet原生的API</a>

控制器代码

 /*获得原生的API*/
    @RequestMapping("/testServlet")
    public String testServlet(HttpServletRequest request, HttpServletResponse response){
        System.out.println("zhixing.........");
        System.out.println(request);

        HttpSession session = request.getSession();
        System.out.println(session);

        ServletContext servletContext = session.getServletContext();
        System.out.println(servletContext);

        System.out.println(response);
        return "success";
    }

输出的都是一些地址值

四、常用注解

@RequestParam

先写jsp发送请求,再写控制器,之后再去配jsp中的路径

anno.jsp


<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <a href="anno/testRequestParam?uname=哈哈哈">RequestParam</a>

</body>
</html>

控制器代码

package cn.itcast.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
@RequestMapping("/anno")
public class AnnoController {

    @RequestMapping("/testRequestParam")
    public String testRequestParam(@RequestParam(name = "uname") String name){
        System.out.println("执行了。。。");
        System.out.println(name);
        return "success";

    }
}

注意:jsp中的参数名称为uname,而控制器设置的参数名称为name,两者不匹配,这个时候会报错,所以我们可以在参数前面使用@RequestParam注解,将里面的name属性定义为jsp中的参数名称,这样就ok了

@RequestBody

get请求没有请求体,他把参数都封装到了地址栏上

超链接是属于get请求

表单jsp

<form action="anno/testRequestBody" method="post">
        用户名称:<input type="text" name="uname"><br>
        用户年龄:<input type="text" name="age"><br>
        <input type="submit" value="提交"><br>

    </form>

控制器

/*testRequestBody*/
    @RequestMapping("/testRequestBody")
    public String testRequestBody(@RequestBody String body){
        System.out.println("执行了。。。");
        System.out.println(body);
        return "success";

    }

运行结果:

uname=gg&age=2

@PathVariable

restful编程风格:

注意占位符的使用,是斜杠后面加上数值

anno.jsp

<a href="anno/testPathVariable/99">testPathVariable</a>

控制器

 @RequestMapping("/testPathVariable/{sid}")
    public String testPathVariable(@PathVariable(name = "sid") Integer id){
        System.out.println("执行了。。。");
        System.out.println(id);
        return "success";

    }

@ModelAttribute

带返回值的控制器写法

  @RequestMapping(value = "/testModelAttribute")
    public String testModelAttribute(User user){
        System.out.println("执行了。。。");
        System.out.println(user);
        return "success";

    }

/*
    该方法先执行,带返回值的
    * */
    @ModelAttribute
    public User test(String uname){
        System.out.println("test执行了。。。");
        User user=new User();
        //通过用户查询数据库(模拟)
        user.setUname(uname);
        user.setAge(1);
        user.setBirthday(new Date());
        return user;
    }

运行结果:

image-20210724090836981

不带返回值的控制器写法

@RequestMapping(value = "/testModelAttribute")
    public String testModelAttribute(@ModelAttribute("aaa") User user){
        System.out.println("执行了。。。");
        System.out.println(user);
        return "success";

    }

    /*
    该方法先执行,不带返回值的
    * */
    @ModelAttribute
    public void test(String uname, Map<String,User> map){
        System.out.println("test执行了。。。");
        User user=new User();
        //通过用户查询数据库(模拟)
        user.setUname(uname);
        user.setAge(1);
        user.setBirthday(new Date());
        map.put("aaa",user);

    }

运行结果:

image-20210724091400138

jsp写法


    <form action="anno/testModelAttribute" method="post">
        用户名称:<input type="text" name="uname"><br>
        用户年龄:<input type="text" name="age"><br>
        <input type="submit" value="提交"><br>

    </form>

@SessionAttributes

存到requestion域中

 @RequestMapping(value = "/testSessionAttributes")
    public String testSessionAttributes(Model model){
        System.out.println("testSessionAttributes");
        //底层会存储到request域对象中
        model.addAttribute("msg","哈哈");
        return "success";

    }

image-20210724092504093

存到requestion域和session域中

image-20210724093219238

image-20210724093045226

从session域中获取值

控制器

 /*从session中获取值*/
    @RequestMapping(value = "/getSessionAttributes")
    public String getSessionAttributes(ModelMap modelMap){
        System.out.println("getSessionAttributes");
        //从session域中取值
        String msg =(String) modelMap.get("msg");
        System.out.println(msg);


        return "success";

    }

从session域中清除值

控制器

  /*从session中清除值*/
    @RequestMapping(value = "/deleteSessionAttributes")
    public String deleteSessionAttributes(SessionStatus status){
        System.out.println("deleteSessionAttributes");
        //从session域中清除值
        status.setComplete();
        return "success";

    }

jsp


    <a href="anno/testSessionAttributes">testSessionAttributes</a>

    <br>
    <a href="anno/getSessionAttributes">getSessionAttributes</a>

    <br>

    <a href="anno/deleteSessionAttributes">deleteSessionAttributes</a>

    <br>

点击先存值,再获取,再清除的运行结果:发现清除后再点击获取值就位null

image-20210724094221913

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值