JavaWeb学习笔记-part10-SpringMVC

1 概述

springmvc是基于spring的一个servlet升级版,实际上是spring的一个模块,专门做web开发的。

web开发底层是servlet,框架是在servlet基础上面增加一些功能,让你做web开发方便。

SpringMVC就是一个spring容器,放的是控制器对象(Controller)。我们要做的就是使用@Controller创建控制器对象,把对象放入springmvc容器中,把创建的对象作为控制器使用,这个控制器对象能接受用户的请求,显示处理结果,就当作是一个servlet使用。

使用@Controller创建的是一个普通类对象,不是servlet。springmvc赋予了控制器对象一些额外的功能。

web开发底层是servlet,springmvc中有一个对象是servlet:DispatcherServlet。

DispatcherServlet:负责接收用户的所有请求,用户吧请求给了DispatcherServlet,之后DispatcherServlet把请求转发给我们的Controller对象,最后是Controller对象处理请求。

1.1 简介

SpringMVC又称Spring Web MVC。是spring框架的一部分。

1.2 优点

  1. 基于MVC架构:功能分工明确。解耦合

  2. 容易理解,上手快,使用简单:SpringMVC是轻量级的,可以快速开发一个注解的SpringMVC项目。

  3. 作为Spring框架的一部分,能够使用Spring的IoC和Aop。方便整合其他框架

  4. SpringMVC强化注解的使用,在控制器,Service,Dao都可以使用注解,方便灵活。

1.3 第一个注解的SpringMVC项目

1.3.1 新建web maven工程

这次我们创建的maven-apache-webapp骨架的项目

1.3.2 加入依赖

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
        <scope>test</scope>
    </dependency>
​
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency>
​
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>
</dependencies>

1.3.3 *调度器(DispatcherServlet)

  1. DispatcherServlet叫做*调度器,是一个servlet,它的父类是集成HttpServlet

  2. DispatcherServlet也叫做前端控制器(front controller)

  3. 负责创建springmvc容器对象,读取xml配置文件,创建文件中的Controller对象

  4. DispatcherServlet负责接收用户提交的请求,调用其他的自定义控制器对象,并把请求的处理结果显示给用户

*调度器(DispatcherServlet)的配置:

在web.xml中配置servlet信息

<?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">
​
    <!-- 声明,注册springMvc的核心对象DispatcherServlet -->
    <!--
        需要在tomcat服务器启动时,创建DispatcherServlet对象的实例
        为什么要创建实例:
        因为DispatcherServlet在它创建过程中,会同时创建springMvc的容器对象,
        读取springMvc的配置文件,把这个配置文件中的对象都创建好,当用户发起请求时就可以直接使用对象
​
        servlet的初始化会执行init()方法,DispatcherServlet在init()中 {
            //创建容器,读取配置文件
            WebApplicationContext ctx = new ClassPathXmlApplicationContext("springmvc.xml");
            //把容器对象放入ServletContext中
            getServletContext().serAttribute(key,ctx);
        }
​
        springMvc创建对象时,读取的配置文件默认是/WEB-INF/<servlet-name>-servlet.xml
        我们可以自定义读取配置文件的位置
     -->
    <servlet>
        <servlet-name>myweb</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
​
        <!-- 自定义读取配置文件的位置 -->
        <init-param>
            <!-- springMvc的配置文件的属性 -->
            <param-name>contextConfigLocation</param-name>
            <!-- springMvc的配置文件的自定义位置 -->
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
​
        <!-- 在tomcat启动后,创建Servlet对象 -->
        <!--
            load-on-startup:表示tomcat在启动后就创建对象的顺序
            它的值是大于等于0的整数,值越小创建时间越早。
         -->
        <load-on-startup>1</load-on-startup>
    </servlet>
​
    <servlet-mapping>
        <servlet-name>myweb</servlet-name>
        <!--
            使用框架时,url-pattern可以使用两种值
            1. 使用扩展名方式,语法*.xxx, xxx是自定义的扩展名。常用方式*.do, *.action, *.mvc等等
               凡是使用这种扩展名的请求都交给*调度器管理
            2. 使用斜杠"/"
         -->
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
​
</web-app>

1.3.4 创建发送请求的页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <p>第一个springmvc项目</p>
    <p><a href="some.do">发起some.do请求</a></p>
</body>
</html>

1.3.5 创建控制器类(Controller)

  1. 在类的上面加入@Controller注解,创建对象,并放入到springmvc容器中

  2. 在类中的方法上面加入@RequestMapping注解

package com.pjh.controller;
​
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
​
/**
 * @Controller 创建处理器对象,对象放在springmvc容器中
 * 位置:类之上
 * 和spring中讲的@Service,@Component差不多
 *
 * 能处理请求的都是控制器(处理器)
 */
@Controller
public class MyController {
​
    /*
    处理用户提交的请求,springmvc中是使用方法来处理的。
    方法是自定义的,可以有多种返回值,多种参数,方法名称自定义
     */
​
    /**
     * 准备使用doSome方法处理some.do请求
     * @RequestMapping 请求映射,作用是把一个请求地址和一个方法绑定在一起。一个请求指定一个方法处理
     * 属性:1. value是一个String,表示请求的uri地址
     *         value值必须是唯一的,不能重复。在使用时,推荐地址以"/"开头
     *         value值可以用花括号括起多个地址,在访问这些地址时,都是请求的该方法
     * 位置:1. 在方法的上面,常用
     *      2. 在类上面使用
     * 说明:使用RequestMapping修饰的方法叫做处理器方法或者控制器方法
     *      使用@RequestMapping是可以处理请求的,类似于Servlet中的doGet和doPost
     * @return ModelAndView
     * Model:数据,请求处理完成后,要显示给用户的数据
     * View:视图,比如jsp
     */
    @RequestMapping(value = {"/some.do", "/first.do"})
    public ModelAndView doSome() {
        //创建返回mv对象
        ModelAndView mv = new ModelAndView();
        //添加数据,框架在请求最后把数据放入到request作用域
        //request.setAttribute()
        mv.addObject("msg", "some.do请求已处理,来自SpringMVC");
        mv.addObject("fun", "执行的doSome方法");
​
        //指定视图,完整路径
        //框架对视图执行的forward操作,即
        //request.getRequestDispather().forward()
        //mv.setViewName("/show.jsp");
​
        //当配置视图解析器后,可以使用逻辑名称(文件名字),指定视图路径
        //框架会使用视图解析器的前缀+逻辑名称+后缀 组成完整路径,这里是字符串连接操作
        mv.setViewName("show");
        //返回mv
        return mv;
    }
}

1.3.6 创建显示结果的页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h3>show.jsp获得的结果</h3><br/>
    <h3>msg:${msg}</h3><br/>
    <h3>fun:${fun}</h3>
</body>
</html>

1.3.7 创建springmvc的配置文件(spring的配置文件一样)

  1. 声明组件扫描器,指定@Controller注解所在的包名

  2. 声明视图解析器,帮助处理视图的。

<?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: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 https://www.springframework.org/schema/context/spring-context.xsd">
​
    <!-- 声明组件扫描器 -->
    <context:component-scan base-package="com.pjh.controller"/>
    <!-- 声明视图解析器,帮助开发人员设置视图文件的路径 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 前缀:视图文件的路径 -->
        <property name="prefix" value="/WEB-INF/view/"/>
        <!-- 后缀:视图文件的拓展名 -->
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

1.3.8 分析springmvc请求的发送过程

  1. 发起some.do

  2. tomcat ( Web.xml ---- url-pattern知道*.do的请求发送给DispatcherServlet )

  3. DispatcherServlet ( 根据springmvc.xml配置知道some.do ---- doSome方法 )

  4. DispatcherServlet把some.do转发给MyController.doSome()方法

  5. 框架执行doSome()把得到ModelAndView进行处理,转发到show.jsp

1.3.9 分析springmvc执行过程

  1. tomcat启动,创建springmvc容器:核心在于DispatcherServlet的init方法,他会创建一个spring容器并且把它加入到ServletContext中成为全局变量

  2. 请求的处理过程:核心方法是DispatcherServlet的doDispatch方法,它会调用请求对应控制器中的对应方法

2 注解式开发

2.1 @RequestMapping

2.1.1 在类的上方使用

属性value中,写所有请求地址的公共部分,叫做模块名称。

2.1.2 method属性

可以设置请求的方式:GET、POST等

它的值是RequestMethod的枚举值,例如:

表示get请求方式,RequestMethod.GET; 表示post请求方式,RequestMethod.POST;

@RequestMapping(value = {"/some.do", "/first.do"}, method = RequestMethod.GET)
@RequestMapping(value = {"/other.do", "/second.do"}, method = RequestMethod.POST)

没有设置该属性时,访问目标方法没有限制。

2.2 控制器处理用户参数

  1. 为方法增加上HttpServletRequest、HttpServletResponse、HttpSession的形参

  2. 直接为方法增加上请求中携带的请求参数

2.2.1 逐个接收参数

package com.pjh.controller;
​
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
​
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
​
@Controller
public class MyController {
    /**
     * 逐个接收请求参数:
     *      要求:处理器方法的形参名与请求中的参数名必须一致,
     *           同名参数赋值给同名形参
     * 框架接收请求参数:
     *      1. 使用request对象接收请求参数
     *         String strName = request.getParameter("name");
     *         String strAge = request.getParameter("age");
     *      2. springMVC框架通过DisPatcherServlet 调用 MyController的doReceiveProperty()方法
     *         调用方法时,按名称对应,把接收的参数赋值给形参
     *         doReceiveProperty(strName, Integer.parseInt(strAge))
     *         框架会提供类型转换的功能,能把字符串转为int,long,float等
     */
    @RequestMapping(value = "/receiveproperty.do")
    public ModelAndView doReceiveProperty(String name, int age) {
        //可以在方法中直接使用name,age
        ModelAndView mv = new ModelAndView();
        mv.addObject("myname", name);
        mv.addObject("myage", age);
        mv.setViewName("show");
​
        return mv;
    }
}

注意:

  1. 在框架进行类型转换时,可能出现无法转换的情况,例如age为空时和age不为整数时,这时服务器会阻止请求调用方法,可以用Integer替换形参int,来避免为空的情况

  2. 如果采用post请求方式,输入中文参数会出现乱码,这时需要手动设置response的encoding为utf-8,如果存在多个方法,可能就要设置多次,这时可以采用过滤器来解决重复代码。 过滤器可以自定义,也可以使用框架自带的 CharacterEncodingFilter。 在web.xml中声明字符编码过滤器:

    <!-- 注册声明过滤器,解决post乱码问题 -->
    <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>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>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

  3. 如果请求中的参数名和形参名称不一致,就不能得到对应的参数值,从而抛出异常。所以为了解决请求中参数名形参名不一样的问题,我们引入@RequestParam注解。

    /**
    * 请求中参数名和形参名不一样
    * @RequestParam 解决请求中参数名形参名不一样的问题
    * 		属性:
    *		1. value 请求中的参数名
    * 		2. required 是一个boolean,默认是true
    * 					表示请求中必须包含此参数
    * 		位置:在处理器方法的形参定义的前面
    */
    @RequestMapping(value = "/receiveparam.do")
    public ModelAndView doReceiveParam(@RequestParam(value="rname", required = false) String name,@RequestParam(value = "rage", required = false) int age) {
        //可以在方法中直接使用name,age
        ModelAndView mv = new ModelAndView();
        mv.addObject("myname", name);
        mv.addObject("myage", age);
        mv.setViewName("show");
        return mv;
    }

2.2.2 对象参数接收

package com.pjh.controller;

import com.pjh.vo.Student;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class MyController {
    /**
     * 处理器方法形参是java对象,这个对象的属性名和请求中的参数名一样
     * 框架会创建形参的java对象,给属性赋值,请求中的参数是name,框架会调用setName()
     * 形参可以有多个对象或者值,框架会根据请求参数,自动为形参对象创建对象并赋值
     */
    @RequestMapping(value = "/receiveobject.do")
    public ModelAndView doReceiveObject(Student student) {
        ModelAndView mv = new ModelAndView();
        mv.addObject("myname", student.getName());
        mv.addObject("myage", student.getAge());
        mv.addObject("mystudent", student);
        mv.setViewName("show");

        return mv;
    }
}

注意:使用对象参数接收,@RequestParam没有任何效果

2.3 处理器方法的返回值

2.3.1 ModelAndView

Model储存要返回的数据,最后存到resquest里面;View储存要转发的页面,使用forward。

如果当前请求既要返回数据,又要跳转页面,使用MV是非常合适的。

但是如果只需要其中一部分(Model或者View),就有些多余了。

2.3.2 String

如果只是要跳转页面,返回String是最恰当的。

如果没有@ResponseBody注解,字符串表示视图名称。

package com.pjh.controller;

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

import javax.servlet.http.HttpServletRequest;

@Controller
public class MyController {

    /**
     * 处理器方法返回String——表示逻辑视图名称,需要配置视图解析器
     */
    @RequestMapping("/returnString-view.do")
    public String doReturnview(HttpServletRequest request, String name, Integer age) {
        System.out.println("myName: " + name + " myAge: " + age);
        //可以自己手动添加数据到request
        request.setAttribute("myname", name);
        request.setAttribute("myage", age);
        //show 逻辑视图名称,需要项目中配置了视图解析器
        //框架对视图执行forward转发操作
        return "show";
    }

    /**
     * 处理器方法返回String——表示完整视图名称,此时不能配置视图解析器
     */
    @RequestMapping("/returnString-view.do")
    public String doReturnview2(HttpServletRequest request, String name, Integer age) {
        System.out.println("myName: " + name + " myAge: " + age);
        //可以自己手动添加数据到request
        request.setAttribute("myname", name);
        request.setAttribute("myage", age);
        //完整视图路径,项目中不能配置视图解析器
        //框架对视图执行forward转发操作
        return "/WEB-INF/view/show.jsp";
    }
}

如果有@ResponseBody注解,字符串表示数据。

文件的格式datatype应该是text/plain,此时要自行设置响应头的编码集:

@RequestMapping(value = "...", produces="text/plain;charset=utf-8")

2.3.3 void

不能表示数据,也不能表示视图。

在处理ajax的时候可以使用void返回值。通过Responce输出数据。响应ajax请求。

ajax请求服务端返回的就是数据,和视图无关。

2.3.4 对象Object

这个Object可以是Integer、String、自定义对象、Map、List等,但是不能是逻辑视图。可以使用这个对象表示的数据,响应ajax请求。

ajax主要接收json的数据格式:

  1. 首先需要加入依赖,springmvc默认使用jackson

  2. 在springmvc的配置文件中加入<mvc:annotation-driven>注解驱动

    注意:此处的annotation-driven一定选择mvc的

  3. 在处理器上面加上@ResponseBody注解

    /**
     * @ResponseBody
     *      作用:把处理器方法返回对象转为json后,通过httpServletResponse输出给浏览器
     *      位置:方法的定义上面。和其他注解没有顺序先后关系
     * 返回对象框架的处理流程:
     *      1. 框架会把返回的对象,调用框架中的ArrayList<HttpMessageConverter>中的每个类的canWrite()方法
     *         来检查哪个HttpMessageConverter接口的实现类能处理该类型的数据
     *      2. 框架会调用实现类的write(),MappingJackson2HttpMessageConverter的write()方法
     *         把对象转为json,调用jackson的ObjectMapper实现转为json
     *      3. 框架会调用@ResponseBody把2的结果数据输出到浏览器,ajax请求处理完成
     */
    @RequestMapping("/returnObject.do")
    @ResponseBody
    public Student doReturnObject(String name, Integer age){
        System.out.println("myName: " + name + " myAge: " + age);
        //处理ajax,使用json做数据的格式
        Student student = new Student();
        student.setAge(age);
        student.setName(name);
    
        return student;
    }

内部原理:

  1. <mvc:annotation-driven>注解驱动。

    注解驱动实现的功能是完成java对象到json,xml,text,二进制等数据格式的转换。

    该标签加入配置文件后,会自动创建HttpMessageConverter接口的7个实现类对象,包括JavaWeb学习笔记-part10-SpringMVC

     HttpMessageConverter接口:消息转换器

    功能:

    定义了java转为json,xml等数据格式的方法;这个接口有很多的实现类,这些实现类完成java对象的转换。

    查看HttpMessageConverter源码,可以找到下面两个方法,是给控制器类把结果输出给浏览器时使用:

    //canWrite:作用检查处理器方法的返回值,能不能转为mediaType表示的数据格式 
    boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
    
    //write:把处理器方法的返回值对象,调用jackson中的ObjectMapper转为json字符串
    void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)			throws IOException, HttpMessageNotWritableException;

    常用的实现类有:StringHttpMessageConverter和MappingJackson2HttpMessageConverter。

  2. @ResponseBody注解

    放在处理器方法上面,通过Responce输出数据,响应ajax请求的。

注意:

  1. 返回List对象时,是转换成json数组再传递给浏览器

2.4 *调度器中的url-pattern

tomcat本身能够处理静态资源的访问,例如html、image、js文件等。

这源于tomcat的web.xml文件有一个servlet名称是default,在服务器启动时自动创建。

它能被所有web应用使用,主要服务于静态资源文件,能够处理所有未被映射(mapping)的请求。

<servlet>
    <servlet-name>default</servlet-name>
    <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
    <init-param>
        <param-name>debug</param-name>
        <param-value>0</param-value>
    </init-param>
    <init-param>
        <param-name>listings</param-name>
        <param-value>false</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

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

注意,default的mapping用的是斜杠"/",这就表示静态资源文件和未被映射的请求都由这个default处理。

如果,在自己的项目中的*调度器中url-pattern标签中使用斜杠,会替代tomcat提供的default servlet。这就导致所有对静态资源的访问都交给*调度器处理,但是一般情况下,它不能够处理静态资源,没有控制器对象能够处理静态资源的访问。

如果使用了斜杠,那么我们需要处理静态资源的访问请求,有两种方式:

  1. 需要在springmvc配置文件中加入<mvc:default-servlet-handler/>。

    原理是,在加入这个标签之后,控制器会创建一个DefaultServletRequestHandler的处理器对象。

    DefaultServletRequestHandler处理器对象:可以把接收的请求转发给tomcat的default这个servlet。

    但是这个标签和@RequestMapping注解有冲突,所以需要加入注解驱动(见2.3.4)来解决冲突。

  2. 在配置文件中加入<mvc:resources/>。

    原理是,加入后会创建一个ResourceHttpRequestHandler这个处理器对象。

    ResourceHttpRequestHandler处理器对象:可以处理对静态资源的访问

    标签有两个属性:

    mapping:访问静态资源的uri地址,使用通配符**

    location:静态资源在你的项目中的目录位置

    例如:<mvc:resources mapping="/static/**" location="/static/"/>

2.5 绝对路径与相对路径

地址分类:

  1. 绝对地址:带有协议名称的是绝对地址,例如百度一下,你就知道

  2. 相对地址:没有协议开头的,例如user/some.do,/user/some.do。

    相对地址不能独立使用,必须有一个参考地址。通过参考地址+相对地址本身才能指定资源。

  3. 参考地址:在你的页面中,访问地址不加"/"

斜杠的使用:

"/"表示上一级目录

在前端中"/"表示服务器的根目录(localhost),在后端中表示项目的根目录

快记:前端不加"/",后端加"/"。

参考路径:

html中有一个标签<base> 可以指定当前页面中所有请求地址的参考地址,其中设置href属性为参考地址即可;

范例:<base href= "http://localhost:8080/path/">

但是由于项目的根目录可能不同,可以使用一段java代码来获取这个路径:

String basePath = request.getScheme() + "://" +
    			  request.getServerName() + ":" + request.getServerPort() +
    			  request.getContextPath() + "/";

3 SSM整合开发

终于来到了重头戏。

SSM:即SpringMVC + Spring + MyBatis

SpringMVC:视图层,界面层,负责接收请求,显示处理结果的。 Spring:业务层,管理Service,Dao,工具类对象的 MyBatis:持久层,访问数据库的

流程图: 用户发起请求<=>SpringMVC接收<=>Spring中的Service对象<=>MyBatis处理数据

3.1 SSM中容器的关系

SSM整合也叫SSI(IBatis),整合中有容器:

  1. 第一个SpringMVC容器,管理Controller控制器对象的。

  2. 第二个Spring容器,管理Service,Dao,工具类对象

我们需要把使用的对象交给合适的容器创建、管理: 把Controller还有Web开发相关的对象交给SpringMVC容器,这些Web用的对象写在Spring配置文件中。 把Service,dao对象定义在Spring的配置文件中,让Spring管理这些对象。

SpringMVC容器和Spring容器是由关系的,关系已经确定好了。 SpringMVC容器时Spring容器的子容器,类似于继承。 在子容器中的Controller可以访问父容器中的Service对象,就可以实现Controller使用Service对象了

3.2 实现一个整合项目

3.2.1 数据库准备

一张名为student的表JavaWeb学习笔记-part10-SpringMVC

 

3.2.2 新建Maven Web项目

3.2.3 加入依赖

<dependencies>
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
    <scope>test</scope>
  </dependency>
  <!-- Servlet -->
  <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
  </dependency>
  <!-- Jsp -->
  <dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.2.1-b03</version>
  </dependency>
  <!-- Spring and SpringMVC -->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>5.2.5.RELEASE</version>
  </dependency>

  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.5.RELEASE</version>
  </dependency>

  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>5.2.5.RELEASE</version>
  </dependency>

  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.5.RELEASE</version>
  </dependency>
  <!-- Mysql Driver -->
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.19</version>
  </dependency>
  <!-- Mybatis -->
  <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.3.1</version>
  </dependency>

  <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.1</version>
  </dependency>
  <!-- jackson -->
  <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.9.0</version>
  </dependency>

  <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.0</version>
  </dependency>
  <!-- Connection Pool Druid -->
  <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.12</version>
  </dependency>
</dependencies>

3.2.4 写web.xml的文件

  1. 注册*调度器DispatcherServlet

    目的: 创建SpringMVC容器对象,才能创建Controller类对象 创建的是Servlet,才能接收用户的请求

  2. 注册spring的监听器ContextLoaderListener

    目的: 创建Spring的容器对象,才能创建Service,Dao等对象

  3. 注册字符集过滤器

    目的: 解决post请求乱码问题

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

    <!-- *调度器 -->
    <servlet>
        <servlet-name>myWeb</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:conf/dispatcherServlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>myWeb</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>

    <!-- Spring监听器 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:conf/applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- post乱码解决方案,设置过滤器修改编码集 -->
    <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>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>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

3.2.5 创建包

  1. Controller包

  2. Service包

  3. Dao包

  4. 实体类(domain)包

3.2.6 配置文件

  1. SpringMVC的配置文件

    <?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:context="http://www.springframework.org/schema/context"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
    
        <!-- SpringMVC配置文件 -->
    
        <!-- 组件扫描器 -->
        <context:component-scan base-package="com.pjh.controller"/>
    
        <!-- 视图解析器 -->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="prefix" value="/WEB-INF/view"/>
            <property name="suffix" value=".jsp"/>
        </bean>
    
        <!-- 注解解析器 -->
        <mvc:annotation-driven/>
    
    </beans>
  2. Spring的配置文件

    <?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: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 https://www.springframework.org/schema/context/spring-context.xsd">
    
        <!-- Spring配置文件 -->
    
        <!-- 声明属性文件位置 -->
        <context:property-placeholder location="classpath:conf/jdbc.properties"/>
    
        <!-- 声明druid数据源 -->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
            <property name="maxActive" value="${jdbc.max}"/>
        </bean>
    
        <!-- 声明sqlFactory -->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource"/>
            <property name="configLocation" value="classpath:conf/mybatis.xml"/>
        </bean>
    
        <!-- Mybatis的映射文件扫描器,用于创建dao对象 -->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
            <property name="basePackage" value="com.pjh.dao"/>
        </bean>
    
        <!-- 声明service的注解所在的包名位置 -->
        <context:component-scan base-package="com.pjh.service"/>
    
        <!-- 声明事务配置 -->
    </beans>
  3. MyBatis的主配置文件

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <!-- 提示日志 -->
        <settings>
            <setting name="logImpl" value="STDOUT_LOGGING"/>
        </settings>
        <!-- 别名 -->
        <typeAliases>
            <!-- name:实体类所在包名 -->
            <package name="com.pjh.domain"/>
        </typeAliases>
        <!-- sql mapper的位置 -->
        <mappers>
            <package name="com.pjh.dao"/>
        </mappers>
    </configuration>
  4. 数据库的属性配置文件

3.2.7 写代码

3.2.8 写页面

3.2.9 项目结构

JavaWeb学习笔记-part10-SpringMVC

4 SpringMVC核心技术

4.1 请求重定向和转发

在SpringMVC中,对请求转发和重定向进行了简化,只需要在setViewName()指定的视图前添加forward(转发)或redirect(重定向)的字段。他们有一个共同的特点:不和视图解析器合作。

总所周知:

请求转发是在服务器内进行请求的转发,只有一次请求响应过程,因此可以访问WEB-INF中被保护的资源; 重定向是将请求发回给用户浏览器再访问资源,是两次请求响应过程,因此不能访问WEB-INF中的资源。

关于重定向:

框架会把Model中的简单类型数据,转成String使用,作为get请求参数使用。目的是在重定向之间的两个请求之间传递数据;

因此可以在第二个请求目标使用参数集合对象,得到请求中的参数。

4.2 异常处理

以前我们使用try/catch来处理异常,往往会出现大量的冗余的try/catch代码段,操作繁琐且代码凌乱。

现在框架为你提供了简介的处理异常的方式:

框架采用的是统一,全局的异常处理。把controller中的所有异常处理都集中到一个地方。采用的是aop的思想。把业务逻辑和异常处理代码分开。解耦合。

使用两个注解:

  1. @ExceptionHandler

  2. @ControllerAdvice

    package com.pjh.handler;
    
    import com.pjh.exception.AgeException;
    import com.pjh.exception.NameException;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.servlet.ModelAndView;
    
    /**
     * @ControllerAdvice 控制器增强(给控制器类增加功能--异常处理功能)
     *          位置:在类的上面
     *          特点:需要在配置文件中加入组件扫描器
     * @author yueyinghaibao
     * @date 2021/10/23
     */
    @ControllerAdvice
    public class ClobalExceptionHandler {
        /**
         * @ExceptionHandler (value = 异常的class) 表示异常的类型,当发生这个类型的异常时,由当前方法处理
         */
    
        @ExceptionHandler(value = NameException.class)
        public ModelAndView doNameException(Exception e) {
            /*
            异常发生处理逻辑:
            1. 需要把异常记录下来,到数据库,日志文件
            2. 发送通知,把异常信息通过邮件,短信,微信发送给相关人员
            3. 给用户友好的提示
             */
    
            ModelAndView mv = new ModelAndView();
            mv.addObject("msg", "姓名不能为空");
            mv.addObject("ex", e);
            mv.setViewName("error");
            return mv;
        }
    
        @ExceptionHandler(value = AgeException.class)
        public ModelAndView doAgeException(Exception e) {
            ModelAndView mv = new ModelAndView();
            mv.addObject("msg", "年龄不能为空");
            mv.addObject("ex", e);
            mv.setViewName("error");
            return mv;
        }
    
        /**
         * 处理其他不知名的异常
         */
        @ExceptionHandler
        public ModelAndView doOtherException(Exception e) {
    
            ModelAndView mv = new ModelAndView();
            mv.addObject("msg", "发生错误");
            mv.addObject("ex", e);
            mv.setViewName("error");
            return mv;
        }
    }

4.3 拦截器

  1. 拦截器是SpringMVC中的一种,需要实现HandlerIntercepter接口

  2. 拦截器给过滤器类似,功能方向侧重点不同。过滤器是用来过滤请求参数的,设置编码字符集等工作。 拦截器是拦截用户的请求,做请求要做判断处理的。

  3. 拦截器是全局的,可以对多个Controller做拦截。 一个项目种可以由0个或多个拦截器,一起拦截用户的请求。 拦截器常用在:用户登陆处理,权限检查,记录日志。

    一个过滤器可以拦截多个用户请求,也是aop思想

使用步骤:

  1. 定义类实现HandlerInterceptor接口

  2. 在springmvc配置文件中,声明拦截器,让框架知道拦截器的存在

拦截器的执行时间:

  1. 在请求处理之前,也就是controller类中的方法执行之前先被拦截。

  2. 在控制器方法执行之后也会执行拦截器

  3. 在请求处理完成后也会执行拦截器

上一篇:2021-09-12


下一篇:网站架构和servlet架构