Spring MVC介绍

介绍

Java EE体系结构包括四层,从上到下分别为应用层、web层、业务层和持久。Struts和Spring MVC是web层的框架,Spring是业务层的框架,Hibernate和Mybatis是持久层框架。
Spring MVC是一种基于Java,实现了Web的MVC设计模式,请求驱动类型的轻量级web框架,即使用了MVC架构模式的思想,将Web层进行指责解耦,基于请求驱动指的就是请求响应模型。
框架存在的目的是帮助简化开发,Spring MVC简化web开发。

演进

早期的MVC模式是servlet+Jsp_JavaBean锁替代,如下图所示:
在这里插入图片描述
用户请求首先到达servlet,然后根据请求调用相应的Java Bean,将结果显示交给JSP去完成,这样的模式成为MVC模式。
M:模型(Model):数据,就是dao、bean
V:视图(View):页面、JSP用来展示模型中的数据
C:控制器(Controller):把不同的数据(Model)显示在不同的视图上(View),servlet扮演控制器的角色。

在Spring中给出的MVC的方法如下:
在这里插入图片描述
传统的模型层被差分成业务层(Service)和数据访问层(Dao),在service下可以通过Spring的声明式操作数据访问层。

特点:

  • 结构松散,几乎所有的SpringMBC使用各类视图。
  • 松耦合,各个模块分离。
  • 与Spring无缝集成。

架构

在这里插入图片描述
Spring MVC是属于Spring的一个模块。
Spring MVC和Spring通过中间整合即可以进行使用。
Spring MVC 是一个基于MVC的Web框架。

运行原理

在这里插入图片描述
具体流程:
第一步:用户发起request请求(URL)到前端控制器DispatcherServlet。
第二步:前端控制器请求处理映射器HandlerMapping查找Handler,可以通过注解或xml形式进行查找。
第三步:处理器映射器向前端控制器返回映射结果:处理器映射器链HandlerExecutionChain对象,包含一个Handler处理器对象、多个HandlerInterceptor拦截器对象。
第四步:前端控制器请求处理器处理器适配器HandlerAdapter请求执行Handler。
第五步:处理器适配器执行Handler。
第六步:处理器执行完返回给适配器ModelAndView对象,ModelAndView是一个Spring MVC底层的对象,包含了Model和View,Model是业务对象的返回的模型数据,View是逻辑视图名。
第七步:处理器适配器返回给前端控制器ModelAndView对象。
第八步:前端控制器请求视图解析器ViewResolver解析视图,视图解析器将逻辑视图名解析为具体的View(jsp…)。
第九步:视图解析器向前端控制器返回View。
第十步:前端控制器进行页面渲染,视图渲染将模型数据(ModelAndView对象)填充到response域
第十一步:前端控制器向用户响应结果。
在这里插入图片描述

各个组件的介绍

前端控制器DispatcherServlet
(由框架提供,不需要开发人员开发)
Spring MVC的入口函数,接收请求,响应结果,相当于中央处理器,有了DispatcherServlet,可以减少其他组件之间的耦合度。

处理器映射器HandlerMapping
(由框架提供,不需要开发人员开发)
根据请求URL查找Handler,即处理器(Controller),Spring MVC提供不同的映射器实现不同的映射方式,例如:配置文件方法,实现接口方式,注解方式等。
处理器适配器HandlerAdapter
(由框架提供,不需要开发人员开发)
按照HandlerAdapter要求的规则去执行Handler,进而通过适配器适配到真正执行的处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行处理。
处理器Handler
(需要开发人员进行开发)
按照HandlerAdapter的要求进行编写,这样适配器才可以正确的找到执行Handler,处理器是后端控制器。由于Handler处理用户的具体业务逻辑,一般情况下需要开发人员进行开发。

视图解析器ViewResolver
(由框架提供,不需要开发人员开发)
进行视图解析,根据逻辑视图名解析成真正的视图View。首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View的视图对象,最后将View进行渲染将处理结果通过页面展示给用户,Spring MVC框架提供的View视图类型包括:jstlView、freemarkerView、pdfView等,一般情况下需要通过页面标签或页面模板的技术将模型数据通过页面展示给用户。

视图View
(需要开发人员进行开发)
View是一个接口,实现类支持不同的View类型(jsp、freemarker、pdf…)。

Spring MVC的使用Demo

创建Spring MVC的项目

在这里插入图片描述

引入依赖

<!--Spring MVC依赖-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
</dependency>

在web.xml配置文件中配置前端控制器

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

  <!--配置前端控制器-->
  <servlet>
    <servlet-name>springMVC</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>springMVC</servlet-name>
    <!--
    第一种:*.action或者*.do:访问是以*.action或者*.do结尾的,由DispatcherServlet进行解析
    第二种:/:所有的URL都由DispatcherServlet进行解析,对于静态文件的解析需要配置不让DispatcherServlet解析(css\js),
          使用此方式可以实现RESTFul风格的URL(主要)
    第三种:/*:使用这种配置,最终要转发到一个jsp页面是:仍然会有DispatcherServlet解析jsp地址,不能通过jsp页面找到Handler,会报错
    -->
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

注意:前端控制器需要使用org.springframework.web.servlet.DispatcherServlet,即该拦截器将所有的请求统一交给DispatcherServlet处理(spring-webmvc)。

创建spring-mvc.xml配置文件

在resource资源目录下,创建spring-mvc.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: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 http://www.springframework.org/schema/context/spring-context-4.1.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">

    <!--配置Spring MVC中需要扫描注解的包路径-->
    <context:component-scan base-package="com.lql.controller"/>

    <!--处理器、映射器、适配器采用Spring MVC提供的默认的处理器处理,不需要显性的配置-->
    <mvc:annotation-driven/>

    <!--配置视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"></bean>
</beans>

注意:spring-mvc.xml文件的加载需要在web.xml文件中配置。

<servlet>
  <servlet-name>springMVC</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <init-param>
    <!--contextConfigLocation:指定SpringMVC配置的加载位置,如果不指定则默认加载
    WEB-INF/"servlet-name"+"-servlet.xml" 如:springMVC-servlet.xml-->
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring-mvc.xml</param-value>
  </init-param>
</servlet>

编写Handler(Controller层)

@Controller
public class ControllerDemo {
	@RequestMapping("/lql")
	public String lql(){
		// 返回ModelAndView对象
		// View的逻辑视图名:"WEB-INF/jsp/lql.jsp"
		return "lql";
	}
}

编写View(JSP页面)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt"  prefix="fmt"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>Hello lql</h1>
</body>
</html>

代码结构目录如下:
在这里插入图片描述

部署服务

web代码的执行需要部署到服务器上,常见的服务器有Tomcat、Jetty(轻量级的服务器,在idea中直接使用插件的形式来添加jetty服务器)。

<plugin>
    <groupId>org.mortbay.jetty</groupId>
    <artifactId>maven-jetty-plugin</artifactId>
    <version>6.1.24</version>
    <configuration>
        <connectors>
            <connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
                <!--端口号-->
                <port>8080</port>
                <maxIdleTime>30000</maxIdleTime>
            </connector>
        </connectors>
        <contextPath>/</contextPath>
    </configuration>
</plugin>

在这里插入图片描述
启动jetty服务器过程如下:
在这里插入图片描述
在这里插入图片描述
启动服务器
在浏览器上输入URL:localhost:8080/lql
在这里插入图片描述
如果显示以上信息,说明用户请求的整个过程没有问题,获取到了响应结果。

前端控制器的源码分析

URL进入前端控制器的过程
在这里插入图片描述
前端控制器的实现类为:org.springframework.web.servlet.DispatcherServlet

在前端控制器类中会执行调用doService方法

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 省略了无关代码
    try {
        this.doDispatch(request, response);
    } finally {
    // 省略了无关代码
    }
}

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

    try {
        try {
            ModelAndView mv = null;
            Exception dispatchException = null;

            try {
                // 和大文件传输有关协议校验
                processedRequest = this.checkMultipart(request);
                multipartRequestParsed = processedRequest != request;
                
                // processedRequest是HttpServletRequest类型
                // mappedHandler是HandlerExecutionChain类型(处理器执行链)
                // 发送请求到HandlerMapping(处理器映射器),获取到处理器执行链对象mappedHandler
                mappedHandler = this.getHandler(processedRequest);
                // 如果没找到,返回错误页面
                if (mappedHandler == null || mappedHandler.getHandler() == null) {
                    this.noHandlerFound(processedRequest, response);
                    return;
                }
	// 发送处理器执行链中包含的Handler对象到HandlerAdapter(处理器适配器),再由处理器适配器去调用Handler,返回一个处理器适配器对象ha
                HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                
                //和Http协议有关
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (this.logger.isDebugEnabled()) {
                        this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                    }

                    if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }

                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }
                
	//从处理器适配器对象中获取ModelAndView对象mv,通过handle方法进入后端编写的Controller类执行
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }

                this.applyDefaultViewName(request, mv);
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            } catch (Exception var19) {
                dispatchException = var19;
            }

//视图解析,将Model数据填充到request域中。进入render方法,找到用户编写的前端页面
            this.processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        } catch (Exception var20) {
            this.triggerAfterCompletion(processedRequest, response, mappedHandler, var20);
        } catch (Error var21) {
            this.triggerAfterCompletionWithError(processedRequest, response, mappedHandler, var21);
        }

    } finally {
        if (asyncManager.isConcurrentHandlingStarted()) {
            if (mappedHandler != null) {
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            }
        } else if (multipartRequestParsed) {
            this.cleanupMultipart(processedRequest);
        }

    }
}

在前端控制器中通过doService方法内部调用不同的方法和其他组件进行交互。

处理器和映射器的配置

Spring MVC大部分使用都是全注解配置,但是全注解也是由非注解发展来的。
Spring MVC的两种注解形式:注解形式和非注解形式。

功能需求:学生信息列表查询

public class Student {
	private int id;
	private String name;
	private int age;
	private String sex;
	//省略构造方法、get和set方法
}

视图JSP(studentList.jsp)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt"  prefix="fmt"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>Title</title>
</head>
<body>
<table border="1" align="center">
    <h1 align="center">学生列表</h1>
    <tr>
        <td>ID</td>
        <td>姓名</td>
        <td>年龄</td>
        <td>性别</td>
    </tr>

    <c:forEach items="${users}" var="user">
        <tr>
            <th>${user.id}</th>
            <th>${user.name}</th>
            <th>${user.age}</th>
            <th>${user.sex}</th>
        </tr>
    </c:forEach>
</table>
</body>
</html>

基于XML形式配置处理器适配器等

在spring-mvc.xml文件中配置处理器(包含处理器映射器、处理器适配器、视图解析器)

配置处理器适配器:所有处理器都需要来实现HandlerAdapter的接口

<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>

在这里插入图片描述
在SimpleControllerHandlerAdapter源码中,可以看出适配器只能实现Controller接口的Handler,即开发的Controller层,才能由SimpleControllerHandlerAdapter适配器执行。

Handler
基于XML配置文件实现,需要实现Controller接口,一个类只能实现一个方法

public class ControllerXML implements Controller {
	@Override
	public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
		List<Student> students = new ArrayList<>();
		students.add(new Student(1,"张三",15,"男"));
		students.add(new Student(1,"李四",16,"男"));
		students.add(new Student(1,"王五",17,"女"));
		students.add(new Student(1,"甲乙",15,"男"));

		ModelAndView mv = new ModelAndView();
		//填充数据
		mv.addObject("students",students);
		mv.setViewName("studentList");
		return mv;
	}
}

配置文件

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt"  prefix="fmt"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>Title</title>
</head>
<body>
<table border="1" align="center">
    <h1 align="center">学生列表</h1>
    <tr>
        <td>ID</td>
        <td>姓名</td>
        <td>年龄</td>
        <td>性别</td>
    </tr>

    <c:forEach items="${students}" var="students">
        <tr>
            <th>${students.id}</th>
            <th>${students.name}</th>
            <th>${students.age}</th>
            <th>${students.sex}</th>
        </tr>
    </c:forEach>
</table>
</body>
</html>

注意:

  • 在XML配置文件中处理器适配器和处理器映射器需要使用的两个框架类:
    org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter
    org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
  • 将配置文件spring-mcv1.xml需要加载到web.xml中。

基于注解形式配置处理器适配器等

处理器映射器

  • 在spring3.1之前使用
    org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
  • 在spring3.1之后使用org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping

处理器适配器

  • 在spring3.1之前使用
    org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
  • 在spring3.1之后使用org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

Handler
同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: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 http://www.springframework.org/schema/context/spring-context-4.1.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">


    <!--配置Spring MVC中需要扫描注解的包路径-->
    <context:component-scan base-package="com.lql.controller"/>

    <!--处理器、映射器、适配器采用Spring MVC提供的默认的处理器处理,不需要显性的配置-->
    <mvc:annotation-driven/>

    <!--配置视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--配置前缀-->
        <property name="prefix" value="/JSP/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

SSM整合

SSM(Spring+Spring MVC+Mybatis)整合以需求为导向来学习整合的思路和步骤。
需求:展示学生信息列表
在这里插入图片描述
整合dao层:
Mybatis和Spring整合,通过Spring管理Mapper接口。
使用Mapper的扫描器自动的扫描mapper接口在Spring中进行注册。
整合service层:
通过Spring管理Service接口。
使用配置方式将service接口配置在spring配置文件中。
实现事务管理。
整合SpringMVC
Spring MVC是Spring的模块。不需要整合。

Demo

引入依赖

在这里插入图片描述

开发dao层

spring-mybatis.xml
在resources下创建spring-mybatis.xml文件作为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>
    <!--配置懒加载、二级缓存 Setting配置需要该文件-->
    <!--配置数据源,在Spring整合过程中,数据源交给Spring管理-->
    <!--配置Mapper ,也是交给Spring管理-->
</configuration>

Mapper.java接口文件

public interface StudentMapper {
	public List<Student> getStudentList();
}

Mapper.xml文件
在resources的配置文件目录下创建xml文件,专门来存储SQL配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lql.dao.StudentMapper">
    <select id="getStudentList" resultType="com.lql.bean.Student">
        select * from student
    </select>
</mapper>

实体类(Student.java)
(略)

Spring的核心配置文件(Spring-core.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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <!--开启注解,通过扫描com.tulun路径下的所有注解-->
    <context:component-scan base-package="com.lql"/>

    <!--配置数据源:c3p0的配置-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.DriverManagerDataSource">
        <!--配置数据连接的核心配置-->
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/school"/>
        <property name="user" value="root"/>
        <property name="password" value="123456"/>
    </bean>

    <!--配置sqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--注入数据源-->
        <property name="dataSource" ref="dataSource"/>
        <!--读取mybatis的配置文件-->
        <property name="configLocation" value="classpath:SSM/spring-mybatis.xml"/>
        <!--读取mapper.xml文件-->
        <property name="mapperLocations" value="classpath:Mapper/*.xml"/>
    </bean>

    <!--动态代理对象-->
    <bean class="org.mybatis.spring.mapper.MapperFactoryBean">
        <!--接口-->
        <property name="mapperInterface" value="com.lql.dao.StudentMapper"/>
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>
</beans>

Service 层(StudentService.java)

Service层调用Dao层获取数据

@Service
public class StudentService {
	//通过注解将dao层对象注入
	@Resource
	private StudentMapper studentMapper;
	public List<Student> getStudentList(){
		return studentMapper.getStudentList();
	}
}

Controller层(ControllerDemo)

controller通过调用service获取响应的业务处理数据

@Controller
public class ControllerDemo {
	@Autowired
	private StudentService studentService;

	@RequestMapping("studentList") //页面地址
	public ModelAndView studentList(){
		List<Student> students = studentService.getStudentList();
		ModelAndView mv = new ModelAndView();
		mv.addObject("students",students);
		mv.setViewName("studentList"); //本地jsp地址
		return mv;
	}
}

前端页面(studentList.jsp)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt"  prefix="fmt"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>Title</title>
</head>
<body>
<table border="1" align="center">
    <h1 align="center">学生列表</h1>
    <tr>
        <td>ID</td>
        <td>姓名</td>
        <td>年龄</td>
        <td>性别</td>
    </tr>

    <c:forEach items="${students}" var="students">
        <tr>
            <th>${students.id}</th>
            <th>${students.name}</th>
            <th>${students.age}</th>
            <th>${students.sex}</th>
        </tr>
    </c:forEach>
</table>
</body>
</html>

配置Spring-mvc.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: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 http://www.springframework.org/schema/context/spring-context-4.1.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">

    <!--配置Spring MVC的需要扫描注解的包路径-->
    <context:component-scan base-package="com.lql.controller"/>

    <mvc:annotation-driven/>

    <!--配置视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--配置前缀-->
        <property name="prefix" value="/JSP/"/>
        <!--配置后缀-->
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

配置web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:SSM/spring-core.xml</param-value>
  </context-param>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <!--配置前端控制器-->
  <servlet>
    <servlet-name>springMVC</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <!--contextConfigLocation:指定SpringMVC配置的加载位置,如果不指定则默认加载
      WEB-INF/"servlet-name"+"-servlet.xml" 如:springMVC-servlet.xml-->

      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:SSM/spring-mvc.xml</param-value>

    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>springMVC</servlet-name>
    <!--
    第一种:*.action或者*.do:访问是以*.action或者*.do结尾的,由DispatcherServlet进行解析
    第二种:/:所有的URL都由DispatcherServlet进行解析,对于静态文件的解析需要配置不让DispatcherServlet解析(css\js),
          使用此方式可以实现RESTFul风格的URL(主要)
    第三种:/*:使用这种配置,最终要转发到一个jsp页面是:仍然会有DispatcherServlet解析jsp地址,不能通过jsp页面找到Handler,会报错
    -->
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

整合的项目结构及说明

在这里插入图片描述
在SSM的整合过程中各个框架的配置文件(spring-mybatis.xml、spring-core.xml、spring-mvc.xml)都是通过web.xml文件来进行识别加载的,
在spring和spring mvc的整合上,主要体现在web.xml文件中,来监听spring的配置信息。

在这里插入图片描述

Controller请求说明

在请求中@RequestMapping注解的作用有两点:

  1. 窄化请求URL
    在类上添加@RequestMapping注解,对URL进行分类管理,可以在这里定义根路径,最终的访问URL是根路径+子路径。
    在这里插入图片描述
    在这里插入图片描述
  2. 限制HTTP的请求方法
    @RequestMapping( value = “userList”,method = RequestMethod.POST)
    如果限制请求为POST,则请求GET报错

Spring MVC的参数绑定

参数绑定:客户端发送请求,而请求中包含一些数据,数据到达Controller。简单来说就是网页和后台传递参数的过程。

参数绑定过程

在Spring MVC中,提交请求的数据是通过方法形参接收,从客户端请求key-value数据,经过参数绑定,将key-value 数据绑定到controller的形参上,然后通过controller就可以直接使用该参数了。
在这里插入图片描述
这里主要涉及的是参数绑定组件,请求的数据转化为需要的数据称之为参数绑定组件,也就是参数绑定转换器,Spring MVC内置很多参数转换器,在极少数情况需要实现自定义的参数转换器。

默认支持的类型

Spring MVC支持的默认的参数类型,直接在方法形参上给出默认的类型的声明,就可以直接使用。在参数绑定过程中,遇到这种类型直接进行绑定。
HttpServletRequest:通过request对象来获取请求信息
HttpServletResponse:通过response处理响应信息
HttpSession:通过Session对象得到session信息(缓存信息的存储)
Model/ModelMap:Model是接口,ModelMap是一个接口实现,将model数据填充到request中,即使使用Model,内部绑定的还是ModelMap的实现。

//默认的类型
@RequestMapping("defaultParamType")
public void defaultParamType(HttpServletRequest request, HttpServletResponse response, HttpSession session, Model model) throws IOException {
    //获取前端请求的数据
    request.getParameter("name");
    
    //设置数据
    request.setAttribute("repsonseParam","request");
    
    //响应结果
    response.getWriter().write("response");
    
    //设置session信息
    session.setAttribute("sessionParam","session");
    
    //Model参数设置
    model.addAttribute("modelParam","model");    
}

基本的数据类型

@RequestMapping("dataTypeTest")
public String dataTypetest(){
	return "dataTypeTest";
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>

</head>
<body>
<form action="/student/getAge" method="get">
    <input name="age" value="20" type="text"/>
    <input type="submit" value="提交"/>
</form>

</body>
</html>
@RequestMapping("getAge")
public void getAge(int age){
	System.out.println("age:"+age);
}

在这里插入图片描述
注意:表单的Input的age值和controller的参数变量名保持一致就可以完成数据绑定。
如果不一致需要使用@RequestParam注解完成。

@RequestMapping("getAge")
public void getAge(@RequestParam("age") int age1){
	System.out.println("age:"+age1);
}

自定义的类型

@RequestMapping("dataTypeTest")
public String dataTypetest(){
	return "dataTypeTest";
}
<form action="/student/getStudent" method="get">
    <table width="60%" border="1" align="center">
        <h1 align="center">修改用户信息</h1>
        <tr>
            <td>学号<input type="text" name="id"></td>
            <td>姓名<input type="text" name="name"></td>
            <td>年龄<input type="text" name="age"></td>
            <td>性别<input type="text" name="sex"></td>
        <td colspan="2" align="center"><input type="submit" value="提交"/>
        </tr>
    </table>
</form>
@RequestMapping("getStudent")
public void getStudent(Student student){
	System.out.println("student:"+student);
}

在这里插入图片描述

集合类型

集合类型:数组、List、Map等施常用的数据传输的类型
以List为例
在这里插入图片描述
在这里插入图片描述

Spring MVC的请求返回值

返回ModelAndView类型

返回结果定位为ModelAndView类型,将Model和view进行分别设置
在这里插入图片描述

返回String类型

返回逻辑视图名
真正路径=前缀+逻辑视图名+后缀
在这里插入图片描述
redirect:重定向
特点:浏览器的URL地址会发生改变,修改后的request的数据无法传递给重定向的页面的,因为重定向后重新进行request(request是无法共享)。

@RequestMapping("getAge")
public String getAge(@RequestParam("age") int age1){
	System.out.println("age:"+age1);
	return "redirect:studentList";
}

forward:页面转发
特点:浏览器的URL地址不变,request共享。

@RequestMapping("getAge")
public String getAge(@RequestParam("age") int age1){
	System.out.println("age:"+age1);
	return "forward:studentList";
}

返回void类型

在controller方法的形参上可以定义HttpServletRquest和HttpServletResponse的类型的参数,使用request和response类响应结果。

使用request转向页面

public void getAge(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
	request.getRequestDispatcher("studentList").forward(request,response);
}

使用response页面重定向

public void defaultParamType(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
	response.sendRedirect("studentList");
}

通过response指定响应结果
例如通过JSON数据返回

public void defaultParamType(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
	response.setCharacterEncoding("utf-8");
	response.setContentType("application/json;charset=utf-8");
	response.getWriter().write("hello word");
}

在这里插入图片描述

图片上传功能

Spring MVC中如何实现文件的上传和下载功能
以图片上传为例介绍使用步骤

引入依赖

<!--文件操作依赖-->
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.4</version>
</dependency>
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.1</version>
</dependency>

在mvc的配置文件中配置multipart类型解析器

    <!--图片上传-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!--设置上传文件的最大尺寸大小-->
        <property name="maxUploadSize">
            <value>500000</value>
        </property>
    </bean>
@RequestMapping("fileTest")
public String fileTest() {
    return "fileupload";
}

//    文件上传操作
@RequestMapping("upload")
public void upload(MultipartFile picture) throws IOException {
    if (picture == null) return;

    //文件存储路径
    String path="E:\\";

    //获取文件的原始名称
    String imgName = picture.getOriginalFilename();
    System.out.println(imgName);

    //新存储文件路径
    File file = new File(path + imgName+"jsp");

    //将内存数据写入磁盘
    picture.transferTo(file);
}

在上传文件时,绑定的参数类型为MultipartFile,通过.transferTo方法将上传的文件进行保存

JSON

JSON的数据交互在前后端是比较通用的,在前后端分离开发中尤其重要。

下面介绍JSON在Spring MVC中的使用

添加JSON依赖

<!--JSON数据处理-->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.4.6</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.4.6</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.4.6</version>
</dependency>

使用注解@ResponseBody返回数据

@RequestMapping("responceJSON")
@ResponseBody  //将Java对象转化为json数据
public List<Student> studentList1(){
	List<Student> students = studentService.getStudentList();
	return students;
}

结果
在这里插入图片描述

异常处理机制

系统异常分为两类:预期异常和运行时异常。
系统中dao、service、controller层出现异常通过Throws Exception向上抛出,在Spring MVC中前端控制器将有异常处理器进行异常处理,SpringMVC 提供全局异常处理器进行统一处理,一个系统只有一个异常处理器。

自定义异常类
对不同的异常类型定义不同的异常类,继承Exception

/**
 * 自定义异常
 * 针对预期的异常,需要在程序中抛出
 */
public class CustomException extends Exception {
	//异常信息
	private String message;

	public CustomException(String message) {
		super(message);
		this.message = message;
	}

	@Override
	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}
}

全局异常处理器
Spring MVC提供了一个HandlerExceptionResolver接口.
处理思路:解析出异常类型,如果该类型是系统自定义的异常,直接取出异常信息,在错误页面展示,如果该异常类型不是系统自定义的类型,构造一个自定义的异常类型。

public class CustomExceptionResolver implements HandlerExceptionResolver {
	@Override
	public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
		CustomException exception = null;
		if (ex instanceof CustomException) {
			exception = (CustomException)ex;
		} else {
			//不是系统自定义类型
			exception = new CustomException("未知异常");
		}

		//错误信息
		String message = exception.getMessage();
		ModelAndView modelAndView = new ModelAndView();
		modelAndView.addObject("message", message);
		modelAndView.setViewName("error");\
		return modelAndView;
	}
}

错误页面

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

配置异常处理器
在mvc配置文件中将异常处理器交给Spring管理

<!--全局的异常处理器-->
<bean class="com.lql.Exception.CustomExceptionResolver"/>

异常的使用

@RequestMapping("{id}")
//@RequestBody 将前端的JSON数据解析绑定成Java对象
public ModelAndView editStudent(@PathVariable("id")int id) throws CustomException {
	if(id > 35){
		System.out.println("参数非法");
		throw new CustomException("参数非法");
	}
	ModelAndView modelAndView = new ModelAndView();
	modelAndView.addObject("id",id);
	modelAndView.setViewName("editStudent");
	return modelAndView;
}

结果
在这里插入图片描述

RESTFul

RESTFul是一种软件架构风格,不是标准。只是提供了一种设计原则和约束条件,主要适用于客户端和服务端交互的软件。
基于HTTP协议实现。为了提高系统的可伸缩性,降低应用之间耦合度,方便框架分布式处理程序,基于这种风格更加简单,具有层次感。
在RESTFul中,对用户请求的URL使用同一个URL,而用不同的请求方法get/post/delete/put等方式的请求进行区分,这样在前后端分离的开发中,前端开发人员不会对请求的资源地址产生混淆,形成一个统一的接口。

使用规定

在HTTP协议中,四个表示操作方式的动词:Get/Post/Put/Delete,分别代表四种基本操作。

  • GET:请求操作,对应select。
  • POST:新建立资源,对应insert。
  • PUT:更新资源,对应update操作。
  • DELETE:删除资源,对应delete操作。

非RESTFul风格的URL:http://…/queryUserList?id=12&userName=‘sdf&sdf’
Rest风格:http://…/queryUserList/12
特点:URL简洁,将参数通过URL传递给服务端

HTTP方法规范:不管是删除、修改、添加。。。使用的URL是一致的,区分不同的删除等操作是通过HTTP的方法决定的。
例:http://…/queryUserList/12 方法设置为delete是表示是删除12这个用户ID的信息,方法设置为get方法表示是查询用户ID为12 的用户。

实现RESTFul风格接口

Spring MVC对RESTFul应用提供一下支持:

  • @RequestMapping:指定要处理的请求的URL和HTTP请求的动作类型。
  • @PathVariable:注解将URL的变量映射到处理方法的参数上。

前端利用ajax,在客户端发出put,delete的动作要求
在这里插入图片描述
一般的应用格式如下:
@RequestMapping(value="/{id}",method=RequestMethod.GET)
@RequestMapping(value="/{id}",method=RequestMethod.POST)
@RequestMapping(value="/{id}",method=RequestMethod.DELETE)
@RequestMapping(value="/{id}",method=RequestMethod.PUT)
在这里插入图片描述

拦截器

Spring MVC中拦截器类似servlet开发中的拦截器
SPring MVC中的拦截器需要实现一个接口

自定义拦截器类
自定义拦截器类,实现一个接口,HandlerIntercepter

public class CustomInterceptor implements HandlerInterceptor {
	//进入Handler之前执行
	@Override
	public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
		/**
		 * true:表示放行
		 * false:表示拦截,不向下执行
		 */
		/**
		 * 应用场景:
		 * 用户身份认证,身份鉴权
		 * 如果认证不通过表示当前用户没有登录,需要次方法栏拦截不在往下执行
		 */
		System.out.println("进入Handler之前:");
		return false;
	}

	//进入Handler方法之后,返回ModelAndView之前执行
	@Override
	public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
		/**
		 * 应用场景:
		 * 应用场景是将页面的公共的模型数据(菜单导航栏)传给视图,也可以是同一的指定视图
		 */
		System.out.println("进入Handler方法之后,返回ModelAndView之前执行");
	}

	//执行Handler之后执行此方法
	@Override
	public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
		/**
		 * 统一的异常处理或者是同一日志处理
		 */
		System.out.println("执行Handler之后执行此方法");
	}
}

拦截器配置
Spring MVC拦截器针对HandlerMapping进行拦截设置

<!--拦截器的配置-->
    <mvc:interceptors>
        <!--配置具体拦截器-->
        <mvc:interceptor>
            <!--
            /** 表示所有的URL包含的子路径
            /* 表示所有的URL的根路径
            --!>
            <mvc:mapping path="/**"/>
            <bean class="com.tulun.utils.CustomInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

如果在某个HandlerMapping中配置拦截器,经过该映射器拦截成功的Handler会使用该拦截器
Spring MVC配置类似全局的拦截器,将拦截器注入到每一个映射器中。

拦截器1:身份鉴权 preHandle true:false
拦截器2:统一的日志处理 preHandle true , afterCompletion
A:先拦截器1在拦截器2 B:先拦截器2在拦截器1 C:无所谓顺序

在配置多个拦截器时,一定要注意在配置中的顺序问题。统一的日志处理拦截器,一定要在preHandle中放行,且将他放在拦截器的第一个位置。登录认证拦截器,应该放在第二个位置。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值