SpringMVC框架

一.SpringMVC概述

1.1 什么是SpringMVC

它是基于MVC开发模式的框架,用来优化控制器.它是Spring家族的一员.它也具备IOC和AOP.
什么是MVC?
它是一种开发模式,它是模型视图控制器的简称.所有的web应用都是基于MVC开发.

  • M:模型层,包含实体类,业务逻辑层,数据访问层
  • V:视图层,html,javaScript,vue等都是视图层,用来显现数据
  • C:控制器,它是用来接收客户端的请求,并返回响应到客户端的组件,Servlet就是组件

1.2 SpringMVC框架的优点

1)轻量级,基于MVC的框架
2)易于上手,容易理解,功能强大
3)它具备IOC和AOP
4)完全基于注解开发

1.3 SpringMVC优化的方向

在这里插入图片描述

1.4 SpringMVC执行的流程

1.4.1 执行的流程

在这里插入图片描述
具体的流程:

  1. 用户通过浏览器发起 HttpRequest 请求到前端控制器 (DispatcherServlet)。
  2. DispatcherServlet 将用户请求发送给处理器映射器 (HandlerMapping)。
  3. 处理器映射器 (HandlerMapping)会根据请求,找到负责处理该请求的处理器,并将其封装为处理器执行链 返回 (HandlerExecutionChain) 给 DispatcherServlet
  4. DispatcherServlet 会根据 处理器执行链 中的处理器,找到能够执行该处理器的处理器适配器(HandlerAdaptor) --注,处理器适配器有多个
  5. 处理器适配器 (HandlerAdaptoer) 会调用对应的具体的 Controller
  6. Controller 将处理结果及要跳转的视图封装到一个对象 ModelAndView 中并将其返回给处理器适配器 (HandlerAdaptor)
  7. HandlerAdaptor 直接将 ModelAndView 交给 DispatcherServlet ,至此,业务处理完毕
  8. 业务处理完毕后,我们需要将处理结果展示给用户。于是DisptcherServlet 调用 ViewResolver,将 ModelAndView 中的视图名称封装为视图对象
  9. ViewResolver 将封装好的视图 (View) 对象返回给 DIspatcherServlet
  10. DispatcherServlet 调用视图对象,让其自己 (View) 进行渲染(将模型数据填充至视图中),形成响应对象 (HttpResponse)
  11. 前端控制器 (DispatcherServlet) 响应 (HttpResponse) 给浏览器,展示在页面上。

1.4.2 SpringMVC常用组件

1)DispatcherServlet

是一种前端控制器,由框架提供。
作用:统一处理请求和响应。除此之外还是整个流程控制的中心,由 DispatcherServlet 来调用其他组件,处理用户的请求

2)HandlerMapping

处理器映射器,由框架提供。
作用:根据请求的 url、method 等信息来查找具体的 Handler(一般来讲是Controller)

3)Handler(一般来讲是Controller)

处理器,注意,这个需由工程师自己开发。
作用:在 DispatcherServlet 的控制下,Handler对具体的用户请求进行处理

4)HandlerAdapter

处理器适配器,由框架提供。
作用:根据映射器找到的处理器 Handler 信息,按照特定的规则去执行相关的处理器 Handler

5)ViewResolver

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

6)View

视图,工程师自己开发
作用:View接口的职责就是接收model对象、Request对象、Response对象,并渲染输出结果给Response对象。

1.5 基于注解的SpringMVC程序

SpringMVC 的注解式开发:在代码中通过对类与方法的注解,便可完成处理器在 springmvc 容器的注册。

项目案例功能:用户提交一个请求,服务端处理器在接收到这个请求后,给出一条欢迎信息,在响应页面中显示该信息。

开发的步骤:

1)新建项目,选择webapp模板.
2)修改目录,添加缺失的test,java,resources(两套),并修改目录属性
3)修改pom.xml文件,添加SpringMVC的依赖,添加Servlet的依赖

  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.5.RELEASE</version>
  </dependency>
  <!--添加servlet的依赖-->
  <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
  </dependency>

4)添加springmvc.xml配置文件,指定包扫描,添加视图解析器.

  <!--添加包扫描-->
  <context:component-scan base-package="com.bjpowernode.controller"></context:component-scan>
  <!--添加视图解析器-->
  <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
      <!--配置前缀-->
      <property name="prefix" value="/admin/"></property>
      <!--配置后缀-->
      <property name="suffix" value=".jsp"></property>
  </bean>

5)删除web.xml文件,新建web.xml
6)在web.xml文件中注册springMVC框架(所有的web请求都是基于servlet的)

  <!--注册SpringMVC框架-->
  <servlet>
      <servlet-name>springmvc</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-mapping>
      <servlet-name>springmvc</servlet-name>
      <!--
        指定拦截什么样的请求
        http://localhost:8080/one
        http://localhost:8080/index.jsp
        http://localhost:8080/demo.action
        <a href="${pageContext.request.contextPath}/demo.action">访问服务器</a>
      -->
      <url-pattern>*.action</url-pattern>
  </servlet-mapping>

7)在webapp目录下新建admin目录,在admin目录下新建main.jsp页面,删除index.jsp页面,并新建,发送请求给服务器
8)开发控制器(Servlet),它是一个普通的类.

    @Controller  //交给Spring去创建对象
  public class DemoAction {
      /**
       * 以前的Servlet的规范
       * protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}
       * action中所有的功能实现都是由方法来完成的
       * action方法的规范
       * 1)访问权限是public
       * 2)方法的返回值任意
       * 3)方法名称任意
       * 4)方法可以没有参数,如果有可是任意类型
       * 5)要使用@RequestMapping注解来声明一个访问的路径(名称)
       *
       */
      @RequestMapping("/demo")
      public String demo(){
          System.out.println("服务器被访问到了.......");
          return "main";  //可以直接跳到/admin/main.jsp页面上
      }
  }

9)添加tomcat进行测试功能

二.SpringMVC注解式开发

2.1 @RequestMapping定义请求规则

2.1.1 指定模块名称

通过@RequestMapping 注解可以定义处理器对于请求的映射规则。该注解可以注解在方法上,也可以注解在类上,但意义是不同的。value 属性值常以“/”开始。@RequestMapping 的 value 属性用于定义所匹配请求的 URI。

举例:
1)此注解可加在方法上,是为此方法注册一个可以访问的名称(路径)
在这里插入图片描述
提取后:
2)此注解可以加在类上,相当于是包名(虚拟路径),区分不同类中相同的action的名称

@Controller
@RequestMapping("/zar")
public class HelloSpringMvc {
//相当于一个控制器处理的方法
@RequestMapping("/hello")
public String one() {
return "main";
}
@RequestMapping("/two")
public String two() {
return "main";
}
//客户端的请求:
// <form action="${pageContext.request.contextPath}/zar/hello.action">
    // <form action="${pageContext.request.contextPath}/zar/two.action">
}

2.1.2 对请求提交方式的定义

对于@RequestMapping,其有一个属性 method,用于对被注解方法所处理请求的提交
方式进行限制,即只有满足该 method 属性指定的提交方式的请求,才会执行该被注解方法

Method 属性的取值为 RequestMethod 枚举常量。常用的为 RequestMethod.GET
RequestMethod.POST,分别表示提交方式的匹配规则为 GET 与 POST 提交。

@Controller
public class ReqAction {
    @RequestMapping(value = "/req",method = RequestMethod.GET)
    public String req(){
        System.out.println("我是处理get请求的........");
        return "main";
    }
    @RequestMapping(value = "/req" ,method = RequestMethod.POST)
    public String req1(){
        System.out.println("我是处理post请求的........");
        return "main";
    }
}

2.2 五种数据提交的方式

前述:前四种数据注入的方式,会自动进行类型转换。但无法自动转换日期类型。

2.2.1 单个提交数据

页面:

<form action="${pageContext.request.contextPath}/one.action">
    姓名:<input name="myname"><br>
    年龄:<input name="age"><br>
    <input type="submit" value="提交">
</form>

action:

@RequestMapping("/one")
public String one(String myname,int age){  ===>自动注入,并且类型转换
    System.out.println("myname="+myname+",age="+(age+100));
    return "main";
}

2.2.2 对象封装提交数据

在提交请求中,保证请求参数的名称与实体类中成员变量的名称一致,则可以自动创建对象,则可以自动提交数据,自动类型转换,自动封装数据到对象中.

实体类:

 public class Users {
  private String name;
  private int age;
 }

页面:

<form action="${pageContext.request.contextPath}/two.action" method="post">
    姓名:<input name="name"><br>
    年龄:<input name="age"><br>
    <input type="submit" value="提交">
</form>

action:

 @RequestMapping("/two")
 public String two(Users u){
     System.out.println(u);
     return "main";
 }

2.2.3 映射名称不一致

提交请求参数与action方法的形参的名称不一致,使用注解 @RequestParam 来解析

   /**
    *  姓名:<input name="name"><br>
    *  年龄:<input name="age"><br>
    */
   @RequestMapping("/four")
   public String four(
           @RequestParam("name")  ===>专门用来解决名称不一致的问题
           String uname,
           @RequestParam("age")
           int uage){
       System.out.println("uname="+uname+",uage="+(uage+100));
       return "main";
   }

2.2.4 动态占位符提交

仅限于超链接或地址拦提交数据.它是一杠一值,一杠一大括号,使用注解@PathVariable来解析.

页面:

<a href="${pageContext.request.contextPath}/three/张三/22.action">动态提交</a>    

action:

@RequestMapping("/three/{uname}/{uage}")
public String three(
        @PathVariable("uname")  ===>用来解析路径中的请求参数
        String name,
        @PathVariable("uage")
        int age){
    System.out.println("name="+name+",age="+(age+100));
    return "main";
}

2.2.5 手工提取数据

/**
   *  姓名:<input name="name"><br>
   *  年龄:<input name="age"><br>
   */
@RequestMapping("/five")
  public String five(HttpServletRequest request){
      String name = request.getParameter("name");
      int age = Integer.parseInt(request.getParameter("age"));
      System.out.println("name="+name+",age="+(age+100));
      return "main";
}  

2.3 中文乱码解决方案

web.xml 中注册字符集过滤器,即可解决 Spring 的请求参数的中文乱码问题。不过,最好将该过滤器注册在其它过滤器之前。因为过滤器的执行是按照其注册顺序进行的。

在web.xml中配置过滤器:

  <filter>
        <filter-name>encode</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <!--
          配置参数
            private String encoding;
            private boolean forceRequestEncoding;
            private boolean forceResponseEncoding;
        -->
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceRequestEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>forceResponseEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encode</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping> 

2.4 处理器action方法的返回值

2.4.1 String

处理器方法返回的字符串可以指定逻辑视图名,通过视图解析器解析可以将其转换为物理视图地址。
在这里插入图片描述
在这里插入图片描述
当然,也可以直接返回资源的物理视图名。不过,此时就不需要再在视图解析器中再配置前辍与后辍了。
在这里插入图片描述

2.4.2 Object

需要使用 @ResponseBody 注解。返回json格式的对象.自动将对象或集合转为json.使用的jackson工具进行转换,必须要添加jackson依赖.一般用于ajax请求.

项目案例:使用ajax请求返回一个JSON结构的学生.

实现步骤:
A.在pom.xml文件中添加依赖

   <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.8</version>
    </dependency>

B.添加jQuery的函数库,在webapp目录下,新建js目录,拷贝jquery-3.6.0.js到目录下
C.在页面添加jQuery的函数库的引用

   <script src="js/jquery-3.6.0.js"></script>

D.发送ajax请求

 function show() {
          $.ajax({
              url:"${pageContext.request.contextPath}/ajax.action",
              type:"post",
              dataType:"json",
              success:function (stu) {
                  $("#oneStu").html(stu.name+"------"+stu.age);
              }
          });
      }

E.开发action

@Controller
public class AjaxDemo {
    @RequestMapping("/ajax")
	@ResponseBody  //此注解用来解析ajax请求
    public Object ajax(){
        Student stu = new Student("张三",22);
        return stu;
    }
}

F.在springmvc.xml文件中添加注解驱动

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

G.index.jsp页面

  <a href="javascript:show()">ajax访问服务器,返回一个学生</a>
  <br>
  <div id="oneStu"></div>

2.4.3 void

对于处理器方法返回 void 的应用场景,应用在AJAX 响应处理。若处理器对请求处理后,无需跳转到其它任何资源,此时可以让处理器方法返回 void。

2.4.4 基本数据类型

用于ajax请求.

2.4.5 ModelAndView

返回数据和视图对象,现在用的很少.

2.5 SpringMVC的四种跳转方式

本质还是两种跳转:请求转发和重定向,衍生出四种是请求转发页面、转发action、重定向页面、重定向action

  @RequestMapping("/one")
    public String one(){
        System.out.println("这是请求转发页面跳转.........");
        return "main";  //默认是请求转发,使用视图解析器拼接前缀后缀进行页面跳转
    }

    @RequestMapping("/two")
    public String two(){
        System.out.println("这是请求转发action跳转.........");
        //  /admin/  /other.action  .jsp
        //forward: 这组字符串可以屏蔽前缀和后缀的拼接.实现请求转发跳转
        return "forward:/other.action";  //默认是请求转发,使用视图解析器拼接前缀后缀进行页面跳转
    }

    @RequestMapping("/three")
    public String three(){
        System.out.println("这是重定向页面.......");
        //redirect:  这组字符串可以屏蔽前缀和后缀的拼接.实现重定向跳转
        return "redirect:/admin/main.jsp";
    }

    @RequestMapping("/four")
    public String four(){
        System.out.println("这是重定向action.......");
        //redirect:  这组字符串可以屏蔽前缀和后缀的拼接.实现重定向跳转
        return "redirect:/other.action";
    }

    @RequestMapping("/five")
    public String five(){
        System.out.println("这是随便跳.......");

        return "forward:/fore/login.jsp";
    }

2.6 SpringMVC默认的参数类型

不需要去创建,直接拿来使用即可.
1)HttpServletRequest
2)HttpServletResponse
3)HttpSession
4)Model
5)Map
6)ModelMap

示例:

@Controller
public class ParamAction {
    @RequestMapping("/param")
    public String param(HttpServletRequest request,
                        HttpServletResponse response,
                        HttpSession session,
                        Model model,
                        ModelMap modelMap,
                        Map map){
        //Map ,Model,ModelMap,request都使用请求作用域进行传值,
        //所以必须使用请求转发的方式进行跳转,否则丢失数据
        Student stu = new Student("张三",22);
        request.setAttribute("requestStu",stu);
        session.setAttribute("sessionStu",stu);
        modelMap.addAttribute("modelMapStu",stu);
        model.addAttribute("modelStu",stu);
        map.put("mapStu",stu);
        return "main"; //切记请求转发跳
       // return "redirect:/admin/main.jsp";//会丢失数据
    }
}

注意Model,Map,ModelMap都使用的是request请求作用域,意味着只能是请求转发后,页面才可以得到值

2.7 日期处理

2.7.1 日期的提交处理

A.在方法的参数上使用@DateTimeFormat注解

要使用注解 @DateTimeFormat,此注解必须搭配springmvc.xml文件中的<mvc:annotation-driven/>标签

B.@InitBinder注解解决类中日期问题

注册一个注解,用来解析本类中所有的日期类型,自动转换.

@InitBinder
public void initBinder(WebDataBinder dataBinder) {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
	dataBinder.registerCustomEditor(Date.class, new CustomDateEditor(sdf, true));
}

2.7.2 日期的显示处理

A.JSON中的日期显示

需要在类中的成员变量的getXXX方法上加注解.

@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")
public Date getDate() {
	return date;
}
B.JSP页面的日期显示

步骤:
1.需要使用国际化标签,先添加依赖

<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>

2.导入国际化的标签库
核心库:<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
日期库:<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

3.再使用标签显示日期

<h2>学生集合</h2>
<table border="2" width="800px">
    <tr>
        <th>姓名</th>
        <th>年龄</th>
    </tr>
    <c:forEach items="${list}" var="teacher">
        <tr>
            <td>${teacher.name}</td>
            <td>${teacher.date}-------------<fmt:formatDate value="${teacher.date}" pattern="yyyy-MM-dd"></fmt:formatDate> </td>
        </tr>
    </c:forEach>
</table>

2.8 mvc:annotation-driven/标签的使用

<mvc:annotation-driven/>自动注册两个bean,分别为DefaultAnnotationHandlerMappingAnnotationMethodHandlerAdapter。是springmvc为@controller分发请求所必须的。除了注册了这两个bean,还提供了很多支持。
1)支持使用ConversionService 实例对表单参数进行类型转换;
2)支持使用 @NumberFormat 、@DateTimeFormat;
3)注解完成数据类型的格式化;
4)支持使用 @RequestBody 和 @ResponseBody 注解;
5)静态资源的分流也使用这个标签;

2.9 资源在WEB-INF目录下

此目录下的动态资源,不可直接访问,只能通过请求转发的方式进行访问 .
重定向也无法访问!!!!!!!!!

@Controller
public class WebInfAction {
    @RequestMapping("/showIndex")
    public String showIndex(){
        System.out.println("访问index.jsp");
        return "index";
    }
    @RequestMapping("/showMain")
    public String showMain(){
        System.out.println("访问main.jsp");
        return "main";
    }
    @RequestMapping("/showLogin")
    public String showLogin(){
        System.out.println("访问login.jsp");
        return "login";
    }
    //登录的业务判断
    @RequestMapping("/login")
    public String login(String name, String pwd, HttpServletRequest request){
        if("zar".equalsIgnoreCase(name) && "123".equalsIgnoreCase(pwd)){
            return "main";
        }else{
            request.setAttribute("msg","用户名或密码不正确!");
            return "login";
        }
    }
}

三.SpringMVC拦截器

3.1 拦截器的介绍

3.1.1 什么是拦截器

针对请求和响应进行的额外的处理.在请求和响应的过程中添加预处理,后处理和最终处理.

拦截的时间点:在“处理器映射器根据用户提交的请求映射出了所要执行的处理器类,并且也找到了要执行该处理器类的处理器适配器,在处理器适配器执行处理器之前”。

当然,在处理器映射器映射出所要执行的处理器类时,已经将拦截器与处理器组合为了一个处理器执行链,并返回给了中央调度器。

3.1.2 拦截器的执行原理

在这里插入图片描述

3.1.3 拦截器执行的时机

1)preHandle():在请求被处理之前进行操作

2)postHandle():在请求被处理之后,但结果还没有渲染前进行操作,可以改变响应结果

3)afterCompletion:所有的请求响应结束后执行善后工作,清理对象,关闭资源

3.1.4 拦截器实现的两种方式

1)继承HandlerInterceptorAdapter的父类

2)实现HandlerInterceptor接口,实现的接口,推荐使用实现接口的方式

3.2 HandlerInterceptor接口分析

自定义拦截器,需要实现 HandlerInterceptor接口。而该接口中含有三个方法

(1) preHandle

该方法在处理器方法执行之前执行其返回值为 boolean,若为 true,则紧接着会执行处理器方法,且会将 afterCompletion()方法放入到一个专门的方法栈中等待执行。

(2) postHandle

该方法在处理器方法执行之后执行。处理器方法若最终未被执行,则该方法不会执行。由于该方法是在处理器方法执行完后执行,且该方法参数中包含 ModelAndView,所以该方法可以修改处理器方法的处理结果数据,且可以修改跳转方向。

(3)afterCompletion

当preHandle()方法返回 true 时,会将该方法放到专门的方法栈中,等到对请求进行响应的所有工作完成之后才执行该方法。即该方法是在中央调度器渲染(数据填充)了响应页面之后执行的,此时对 ModelAndView 再操作也对响应无济于事。afterCompletion 最后执行的方法,清除资源,例如在 Controller 方法中加入数据等。

3.3 自定义拦截器实现权限验证

1.修改web.xml文件中请求路径

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

2.将所有的页面放入WEB-INF目录下
3.开发登录action

@RequestMapping("/login")
public String login(String name, String pwd, HttpServletRequest request){
    if("zar".equalsIgnoreCase(name) && "123".equalsIgnoreCase(pwd)){
        //在session中存储用户信息,用于进行权限验证
        request.getSession().setAttribute("users",name);
        return "main";
    }else{
        request.setAttribute("msg","用户名或密码不正确!");
        return "login";
    }
}

4.开发拦截器的功能.实现HandlerInterceptor接口,重写preHandle()方法

public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        //是否登陆过的判断
        if(request.getSession().getAttribute("user")==null){
            //此时就是没有登录,转发到登陆页面,并给出提示
            request.setAttribute("msg","您还没有登录,请重新登录!");
            request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,response);
            return false;
        }
        return true;
    }
}

5.在springmvc.xml文件中注册拦截器

    <!--注册拦截器-->
    <mvc:interceptors>
        <mvc:interceptor>
            <!--要拦截的请求-->
            <mvc:mapping path="/**"/>
            <!--设置放行的请求-->
            <mvc:exclude-mapping path="/showLogin"/>
            <mvc:exclude-mapping path="/login"/>
            <!--配置具体的拦截器实现功能的类-->
            <bean class="com.jm.interceptor.LoginInterceptor"></bean>
        </mvc:interceptor>
        <!--拦截器链.......-->
    </mvc:interceptors>

写在最后.Thanks

感谢动力节点荣姐:https://www.bilibili.com/video/BV1oP4y1K7QT/?spm_id_from=333.999.0.0&vd_source=8eb2541fa8bcbc1754472a89a927e94b

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jm呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值