Spring 全家桶之 Spring Web MVC(十)- Spring、Spring MVC 整合
一、Spring MVC 运行流程
在spring-mvc-handler项目的controller包中新增一个HalloContrller,增加hallo方法,Debug Spring MVC的运行流程,在index页面增加/hallo的超链接,断点打在 doDispatch 方法上,Debug模式启动应用并点击首页的hallo超链接
第一步:前端控制器DispatcherServlet收到请求,调用doDispatch()方法处理

第二步:根据HandlerMapping中保存的请求映射信息找到处理当前请求的处理器执行链,包含拦截器

第三步:根据当前处理器找到他的适配器HandlerAdapter

第四步:拦截器的preHandler()方法执行

第五步:适配器执行目标方法,返回ModelAndView对象

第六步:处理器的postHandler()方法执行

第七步:处理结果;页面渲染
- 如果有异常使用异常解析器进行处理,处理之后返回ModelAndView
- 调用render()方法进行页面渲染
- 视图解析器根据视图名得到视图对象
- 视图对象调用render()方法
 
- 执行拦截器的afterCompletion()方法
 

二、Spring、Spring MVC 整合
IDEA创建Maven工程spring-mvc-spring,添加Spring、Spring MVC、Servlet、Jackson、文件上传依赖
<properties>
    <spring-version>5.3.13</spring-version>
</properties>
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>${spring-version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</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>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>javax.servlet.jsp.jstl</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.13.1</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.13.1</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-annotations</artifactId>
        <version>2.13.1</version>
    </dependency>
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.4</version>
    </dependency>
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.11.0</version>
    </dependency>
</dependencies>
选中项目,点击Add Framework,选择添加 Web Application

选中项目,点击顶部菜单中的File -> Project Structure -> Artifacts,在右侧WEB-INF下新建lib文件夹,将Available Element下的Jar包全部选中导入lib文件夹下
 点击 Apply 并关闭该窗口。
点击 Apply 并关闭该窗口。
配置WEB-INF下的 web.xml 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
        
    <!--Spring-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring.xml</param-value>
    </context-param>     
    <!--DispatchServlet-->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <!--
            DispatcherServlet是Spring MVC最核心的对象
            DispatcherServlet用于拦截Http请求,
            并根据请求的URL调用与之对应的Controller方法,来完成Http请求的处理
        -->
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
        <!--
            在Web应用启动时自动创建Spring IOC容器,
            并初始化DispatcherServlet
        -->
        <load-on-startup>0</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <!--"/" 代表拦截所有请求,/*拦截所有请求包括jsp页面这些请求-->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    <!--Rest支持-->
    <filter>
        <filter-name>hiddenHttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>hiddenHttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>
在resources目录下新建 spring-mvc.xml 和 spring.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:util="http://www.springframework.org/schema/util"
       xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util
        http://www.springframework.org/schema/util/spring-util-4.3.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <context:component-scan base-package="com.citi">
    </context:component-scan>
    <!--配置试图解析器,自动拼接页面地址,自动在jsp页面前增加/WEB-INF/pages/-->
    <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>
    <!-- 默认前端控制器是拦截所有资源(除过jsp),js文件就404了;要js文件的请求是交给tomcat处理的
http://localhost:8080/7.SpringMVC_crud/scripts/jquery-1.9.1.min.js -->
    <!-- 告诉SpringMVC,自己映射的请求就自己处理,不能处理的请求直接交给tomcat -->
    <!-- 静态资源能访问,动态映射的请求就不行 -->
    <mvc:default-servlet-handler/>
    <!-- springmvc可以保证动态请求和静态请求都能访问 -->
    <mvc:annotation-driven conversion-service="conversionServiceFactory">
    </mvc:annotation-driven>
    <bean id="conversionServiceFactory" class="org.springframework.context.support.ConversionServiceFactoryBean">
    </bean>
</beans>
这是spring.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:util="http://www.springframework.org/schema/util"
       xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util
        http://www.springframework.org/schema/util/spring-util-4.3.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <context:component-scan base-package="com.citi">
    </context:component-scan>
</beans>
在java包下新建controller包和service包,增加HalloController和HalloService
@Controller
public class HalloController {
    // 添加构造方法,实例化是打印日志
    public HalloController(){
        System.out.println(this.getClass().getName() + "被实例化了...");
    }
}
@Service
public class HalloService {
    public HalloService(){
        System.out.println(this.getClass().getName() + "被实例化了...");
    }
}
在WEB-INF下新增pages目录,用来保存jsp页面,新增success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h2>SUCCESS</h2>
</body>
</html>
web.xml中需要添加一个Listener
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
配置Tomcat,选择顶部的Add Configuration,添加本地的Tomcat
 点击Fix
点击Fix

点击Apply,之后启动Tomcat

根据控制台输出的日志,可以确定创建了两个容器,并且两个容器中都实例化了HalloController和HalloService组件,这会导致在Autowire的时候不知道导入的是Spring容器实例化的Bean还是Spring MVC容器实例化的Bean
Spring 和 Spring MVC能够分工明确,Spring MVC的配置文件就负责配置和网站转发逻辑以及网站功能相关的,如视图解析器,文件上传解析器,Ajax等
Spring的配置文件只负责配置和业务有关的组件,如事务控制、数据源等
所以Spring和Spring MVC配置文件中配置包扫描的时候就各自扫描自己的组件;将Spring MVC 配置文件修改为
<context:component-scan base-package="com.citi" use-default-filters="false">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
将Spring 配置文件修改为
<context:component-scan base-package="com.citi">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
保存配置并重新启动应用
 Spring 启动时创建了 HalloService, Spring MVC 容器启动时实例化了 HalloController,并且这两个组件只实例化了一次
Spring 启动时创建了 HalloService, Spring MVC 容器启动时实例化了 HalloController,并且这两个组件只实例化了一次
在HalloController中增加方法
@Autowired
private HalloService halloService;
@RequestMapping("/hallo")
public String hallo(){
    System.out.println("halloService组件为:" + halloService);
    return "success";
}
重新启动应用
 页面可以正常跳转
页面可以正常跳转
 控制台中的HalloService组件成功导入
控制台中的HalloService组件成功导入
上面提到的Spring容器和Spring MVC容器是一对父子容器,Controller中可以装配Service,Service中不能装配Controller, 有点像继承,子类可以用弗雷的,父类不能用子类的
新建一个HiController,在HalloService中注入HiController
@Service
public class HalloService {
    @Autowired
    private HiController hiController;
    public HalloService(){
        System.out.println(this.getClass().getName() + "被实例化了...");
    }
}
再次重新启动

控制台出现报错,Spring MVC 容器中的的组件不可以被带入 Spring 容器中

异常处理注解@ControllerAdvice标注的类也应该由Spring MVC容器扫描,修改Spring MVC 配置文件
<context:component-scan base-package="com.citi" use-default-filters="false">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>
转载自:https://juejin.cn/post/7087587549417308190




