ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

SSM Chapter 09 SpringMVC体系结构和处理请求控制器

2020-12-06 11:03:59  阅读:202  来源: 互联网

标签:Chapter 控制器 请求 SpringMVC Spring 09 视图 Controller MVC


SSM Chapter 09 SpringMVC体系结构和处理请求控制器 笔记

本章重点:

  • 理解MVC设计模式
  • 了解Spring MVC的架构以及请求流程
  • 掌握Spring MVC开发环境搭建
  • 掌握Controller和View之间的映射
  • 掌握参数传递(View-Controller)

本章简介 :

随着Web应用复杂度的不断提升,单纯使用JSP技术完成Web应用程序开发的弊端越来越明显,在应用程序中引入控制器(Servlet 或者 Filter),可以有效的避免在JSP页面编写大量的业务和页面跳转代码,而JSP则专门用于展示内容,这种程序设计模式就是MVC设计模式。

了解了MVC设计模式之后,我们就会开始学习Controller层的框架产品Spring MVC。本章将学习Spring MVC 环境的搭建,掌握Contorller 和 View 之间的映射和参数传递,并理解Spring MVC 的架构及其请求处理流程。

1 . MVC 设计模式

Web项目的架构模式基本都一致,都进行了分层设计:

  • 数据访问接口 : DAO 层
  • 处理业务逻辑 : Service 层
  • 数据实体 : POJO
  • 负责前端请求的接受并处理 : Servlet
  • 负责前端页面的展示 : JSP

这种架构模式就是MVC设计模式,它是软件工程中的一种软件架构模式. 它强制性的使软件系统的输入,处理 和 输出分开,把软件系统分为三个部分:模型(Model), 视图(View),控制器(Controller),如图所示:
在这里插入图片描述
根据图1所示 综合 超市订单管理系统,分析出该系统的分层设计与MVC设计模式的对应关系:

  • (1) 视图 (View) : 负责格式化数据并把它们呈现给用户,包括数据展示,用户交互,数据验证,界面设计等功能.对应组件:JSP 或者 HTML 文件(如超市系统一一JSP页面);

  • (2) 控制器(Controller) : 负责接收并转发请求,对请求处理后指派视图并将响应结果发送给客户端.对应组件:Servlet(如超市系统一一Servlet);

  • (3) 模型(Model) : 模型对象拥有最多处理任务,是应用程序的主体部分,它负责数据逻辑(业务规则)的处理 和 实现数据操作(即在数据库中存取数据).
    对应组件:JavaBean(如超市系统中处理业务逻辑的Service层,与数据库操作相关的DAO层,贯穿与各层之间的数据模型,即数据实体POJO);

通过以上分析,我们发现超市订单管理系统所采用的设计模式 ------- JSP+Servlet+JavaBean,其实就是最经典的MVC.

下面详细介绍MVC的两种模式:

1.1 JSP Model 1

当业务流程较为简单的时候,可以把控制器的功能交给视图来实现,这种模式称为 JSP Model1,故Model1 模式只有视图和模型,没有控制器(即 JSP + JavaBean),如图:
在这里插入图片描述

通过上图,我们可以发现Model1 的基础是JSP,它由JSP和JavaBean组成,JSP从HTTP Request 中获得所需的数据,并进行业务逻辑的处理,然后将结果通过HTTP Response 返回给前端浏览器.

从中可见, Model1在一定程度上实现了MVC,即JSP将控制层和视图层合二为一,JavaBean为模型层。其中JSP身兼多职,既要负责视图层的数据展示,又要负责业务流程的控制,架构较为混乱,并且也不是我们所希望的松耦合架构模式,所以当业务流程复杂的时候并不推荐使用。

1.2 JSP Model2

相比于 JSP Model1 , 当业务流程复杂的时候,就需要把业务流程控制交给控制器来实现,JSP专注于视图的展现即可.这种模式就是 JSP Model2(即 JSP + Servlet + JavaBean).如图:
在这里插入图片描述
从上图可以看出, 相比 Model1,Model2 是将控制层(Servlet)单独划分出来负责业务流程的控制,接收请求,创建所需的JavaBean实例,并将处理后的数据在返回给视图层(JSP)进行界面数据展示.这样的结构清晰,效果明显优化很多,并且也是一个松耦合的架构模式,所以除非项目非常简单,一般情况下建议使用 JSP Model2.

1.3 小结

1. MVC处理过程:

MVC整体结构图:
在这里插入图片描述

从图中,我们可以分析出MVC整体的处理过程:

  • (1) 首先视图提供系统与用户交互的界面,并发送用户输入给控制器

  • (2) 控制器接收用户请求,并决定应该调用哪个模型来进行处理

  • (3) 模型根据用户请求进行相应的业务逻辑处理,并返回处理结果(数据)

  • (4) 控制器根据返回的处理结果,调用相应的视图格式化模型返回的数据,并通过视图呈现给用户结果

2. MVC优缺点:

在这里插入图片描述

2 . Spring MVC 介绍及其环境搭建

2.1 Spring MVC 框架介绍

有了以上对MVC设计模式的认识,将有助于我们理解Spring MVC的理念与原理。

Spring MVC是Spring家族中应用于Web应用的一个模块,是Spring提供的一个基于MVC设计模式的Web开发框架,可以将它理解为Servlet。

在MVC模式中,Spring MVC作为控制器(Controller)来建立模型与视图的数据交互,是结构最清晰的JSP Model2实现,可以说是一个典型的MVC框架。如图所示
在这里插入图片描述
除此之外,Spring MVC框架采用松耦合、可插拔的组件结构,具有高度可配置性,比起其他的MVC框架更具有扩展性和灵活性。此外,SpringMVC 的注解驱动和对REST风格的支持,也是它最具有特色的功能. 并且它本身就是Spring家族的一部分,与Spring框架整合更是天衣无缝。

在Spring MVC框架中,Controller替换Servlet来担负控制器的职责。Controller接收请求,调用相应的Model进行处理,Model处理完之后将结果返回给Controller,Conreoller再指派相应的View对处理结果进行渲染,最终响应给客户端进行展示。

由于Spring MVC结构较复杂,以上只是简单的介绍,接下来我们以一个例子,来搭建Spring MVC环境,从而更深入的了解它的架构模型及请求处理流程。万变不离其宗,依然以编程界的圣经——“Hello World”为例。

2.2 Spring MVC 环境搭建

IDEA中创建maven web项目,使用 Spring MVC 框架步骤如下:

  • (1) 引入jar文件(添加maven依赖)

  • (2) Spring MVC 配置

    • 在 web.xml 中 配置 Servlet,定义DispatcherServlet
    • 创建 Spring MVC 的配置文件
  • (3) 创建 Controller (处理请求的控制器)

  • (4) 创建 View(本书中我们使用的是JSP作为视图)

  • (5) 部署运行

1. 下载需要的jar文件

在之前Spring项目jar文件的基础上,加入Spring MVC 的两个 jar 文件

  • Spring-web.jar:在Web应用开发时使用Spring框架的核心类
  • Spring-webmvc.jar:Spring MVC 框架相关的所有类,包含框架的Servlets,Web MVC框架,以及对控制器和视图的支持.

IDEA中只需要加入如下一个依赖,就可以将Spring以及Spring MVC的jar包都导入

<!-- Spring 以及 Spring mvc的jar -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.1.5.RELEASE</version>
</dependency>
<!-- servlet jar -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
</dependency>
<!-- jsp jar -->
<dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.1</version>
    <scope>provided</scope>
</dependency>
<!-- log4j2-->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-web</artifactId>
    <version>2.11.1</version>
</dependency>

编写log4j2.xml文件,内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout
                    pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </Console>
    </Appenders>
    <Loggers>
        <Logger name="org.springframework.web" level="debug" />
        <Logger name="org.springframework.context" level="debug" />
        <Logger name="cn.smbms.controller" level="debug" />
        <Root level="error">
            <AppenderRef ref="Console" />
        </Root>
    </Loggers>
</Configuration>

具体配置可参考官网http://logging.apache.org/log4j/2.x/manual/usage.html

或者使用slf4j记录日志:

在maven项目的pom.xml文件加入slf4j的坐标 , 内容如下:

<!-- slf4j-simple -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId>
    <version>1.7.26</version>
</dependency>

在resources目录下创建log4j.properties文件 , 内容如下:

### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

### direct messages to file hibernate.log ###
#log4j.appender.file=org.apache.log4j.FileAppender
#log4j.appender.file.File=hibernate.log
#log4j.appender.file.layout=org.apache.log4j.PatternLayout
#log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

### set log levels - for more verbose logging change 'info' to 'debug' ###

log4j.rootLogger=warn, stdout
log4j.org.springframework=debug

2. 在 web.xml 中配置 Servlet

Spring MVC 是基于Servlet的,DispatcherServlet 是整个 Spring MVC 框架的核心,它负责截获请求并分派给相应的处理器.

跟所有的Servlet一样,DispatcherServlet也是在web.xml中配置:

参考官网:https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html
在这里插入图片描述

<?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"
         metadata-complete="true">
    <!--配置SpringMVC的核心控制器-->
    <servlet>
    	<!--配置名为 springmvc 的Servlet,该Servlet是 DispatcherServlet类型,
    		它是Spring MVC 的入口-->
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
        	<!--该contextConfigLocation 用来指定SpringMVC配置文件所在的位置-->
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc-servlet.xml</param-value>
        </init-param>
        <!--该配置标记容器在启动的时候 首先加载此 Servlet,即自动启动-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <!--该配置标记 DispatcherServlet需要截获并处理该项目的所有URL请求-->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

3. 创建Spring MVC 的配置文件(springmvc-servlet.xml)

在项目的resources目录下创建springmvc-servlet.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.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!--指定url请求:index.html,处理该URL请求的控制器是IndexController -->
    <bean name="/index.html" class="cn.smbms.controller.IndexController"/>
    <!--配置视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
     <!--通过配置prefix和suffix,将视图逻辑解析为:/WEB-INF/jsp/<viewName>.jsp -->
	<!--<constructor-arg index="0" value="/WEB-INF/jsp/"/>
        <constructor-arg index="1" value=".jsp"/>-->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
	<!--配置视图解析器第二种方式-->
    <!-- <mvc:view-resolvers>
        &lt;!&ndash;默认情况下,“/WEB-INF/”被注册为一个视图名称的前缀。
        “jsp”作为后缀&ndash;&gt;
        <mvc:jsp prefix="/WEB-INF/jsp/" suffix=".jsp" />
    </mvc:view-resolvers>-->
</beans>
1)配置处理器映射

我们在web.xml中配置DispatcherServlet时,配置了所有请求都由此Servlet来处理,那么DispatcherServlet怎么判断它将哪个请求、分发给哪个Controller(控制器)去处理呢?

DispatcherServlet需要咨询一个Bean组件,这个Bean叫做HandlerMapping,它的作用就是将一个URL请求映射到相应的Controller,类似于servlet-mapping中的url-pattern。

Spring MVC提供了多种处理器映射(HandlerMapping)的支持,如:

  • org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping;
  • org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
  • org.springframework.web.servlet.mvc.annotation.DefaultAnnontationHandlerMapping;
  • org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,等等

可以根据需求选择处理器映射,此处我们采用BeanNameUrlHandlerMapping(默认的),即在Spring容器中查找与请求URL相同名称的Bean。这个映射器不需要配置,根据请求的URL路径映射到控制器Bean的名称。如下代码:

<!--指定url请求:index.html,处理该URL请求的控制器是IndexController -->
<bean name="/index.html" class="cn.smbms.controller.IndexController"/>
2)配置视图解析器

处理请求的最后一件必要的事情就是渲染输出,返回给客户端显示,这个任务由视图(View)实现。那么就需要确定指定的请求需要哪个视图进行渲染?

DispatcherServlet会查找到一个视图解析器,将控制器返回的逻辑视图名称转换成实际视图。Spring也提供了多种视图解析器,如下:

  • org.springframework.web.servlet.view.InternalResourceViewResolver;
  • org.springframework.web.servlet.view.ContentNegotiatingViewResolve,等等
    在这里插入图片描述

此处我们使用InternalResourceViewResolver定义该视图解析器,通过配置prefix(前缀)和suffix(后缀),最后将视图逻辑名解析为/WEB-INF/jsp/xxx.jsp,即定位到/WEB-INF/jsp文件夹下的.jsp文件。

4. 创建 Controller

到这一步,Spring MVC的环境已经搭建好了,我们只需要再填入一点缺失的东西,即Controller和View。我们先创建Controller。

在com.smbms.controller目录下新建IndexController.java文件,即我们上面配置的处理器映射。如何将一个JavaBean变成可以处理前端请求的控制器?

需要继承org.springframework.web.servlet.mvc.AbstractController,并实现其handleRequestInternal方法,代码如下:

/**
 * 控制器
 */
public class IndexController extends AbstractController {
    private Logger log = LogManager.getLogger();
    @Override
    protected ModelAndView handleRequestInternal(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws Exception {
        log.info("Welcome to Spring MVC ^_^");
        System.out.println("Hello SpringMVC ~~~~");//在控制台输出信息
        return new ModelAndView("index");//返回逻辑视图名
    }

上述代码中,控制器处理方法的返回值为ModelAndView对象,该对象既包含视图信息,也包含模型数据信息,这样Spring MVC就可以使用视图对模型数据进行渲染。该示例中的 return new ModelAndView(“index”); 其中“index”就是视图的逻辑名,最后由视图解析器处理就会变成/WEB-INF/jsp/index.jsp。因为我们这个例子并不返回数据给页面,所以Model为空,不进行设置。

**注意 **

ModelAndView,正如其名所示,它代表Spring MVC中呈现视图界面时所使用的Model(模型数据)和View(逻辑视图名)。由于Java一次只能返回一个对象,所以ModelAndView的作用就是封装这两个对象,以方便一次返回我们所需的Model和View。

当然,返回的模型跟视图都是可选的,有时候模型中没有任何数据,那么只返回视图即可;或者只返回模型数据,让Spring MVC根据请求URL来决定视图。在以后会对ModelAndView对象进行更详细的讲解

5. 创建View

上面刚说过,我们的Controller返回的逻辑视图名被解析之后,会转换成/WEB-INF/jsp/index.jsp,所以我们需要在WEB-INF下创建jsp文件夹,jsp文件夹下创建真正的JSP视图-----index.jsp,并在视图上输出 “hello Spring MVC!”,代码如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>第一个Spring MVC 示例</title>
</head>
<body>
    <h1>Hello Spring MVC ^_^ !!!!</h1>
</body>
</html>

由于控制器IndexController返回的逻辑视图名为index,那么通过视图解析器,会将视图逻辑名解析为/WEB-INF/jsp/index.jsp,得到真正的视图名.

6. 部署运行

部署你的项目到Tomcat容器,启动项目,浏览器中输入请求路径:http://localhost:8080/ssm_ch09/index.html,浏览器正确返回相应页面.

通过上述示例,我们简单总结一下Spring MVC的处理流程:

当用户发起URL请求http://localhost:8080/ssm_ch09/index.html时,根据web.xml中对于DispatcherServlet的配置,该请求被DispatcherServlet所截获,并根据HandlerMapping找到处理此次请求的相应的Controller(WelcomeController),Controller处理完成之后返回ModelAndView对象,该对象告诉DispatcherServlet需要通过哪个视图来进行数据模型的渲染展示,然后DispatcherServlet拿着这个返回的视图名称去找视图解析器,转换成真正的View(即/WEB-INF/jsp/index.jsp),返回给浏览器客户端。

7. 更改HandlerMapping(处理器映射)

在以上的示例中,我们通过BeanNameUrlHandlerMapping的方式实现请求与Controller之间的映射,但是如果有多种请求,岂不是要在spring-mvc-servlet中配置多个映射关系?针对这种问题,Spring MVC提供了一键式配置方法:<mvc:annotation-driven />,通过注解的方式定义Controller,下面就对以上示例进行优化,结合实例来理解。

修改springmvc-servlet.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.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <context:component-scan base-package="cn.smbms.controller"/>
    <mvc:annotation-driven/>
    <!--配置视图解析器-->
     <mvc:view-resolvers>
        <!--默认情况下,“/WEB-INF/”被注册为一个视图名称的前缀。
        “jsp”作为后缀-->
         <!--通过配置prefix和suffix,将视图逻辑解析为:/WEB-INF/jsp/<viewName>.jsp-->
        <mvc:jsp prefix="/WEB-INF/jsp/" suffix=".jsp" />
	</mvc:view-resolvers>

</beans>

我们更改了Spring MVC的处理器映射的配置为支持注解式处理器——配置<mvc:annotation-driven />标签。

它是Spring MVC提供的一种配置方法,配置此标签之后Spring MVC会帮我们做一些注册组件之类的事,它会根据注解定义的Controller中的处理方法所定义的URL请求路径,自动为我们做URL和Controller之间的映射。

具体的实现是,这个标签会自动注册DefaultAnnotationHandlerMapping(处理器映射)和AnnotationMethodHandlerAdapter(处理器适配器),从3.1.x版本开始对应实现类改为了RequestMappingHandlerMappingRequestMappingHandlerAdapter

Spring MVC会通过这两个实例完成对@Controller和@RequestMapping等注解的支持,从而找出URL与handler method的关系并予以关联。

然后我们添加了<context:component-scan>标签来扫描我们的controller包,找出使用注解定义的Bean组件,加载到Spring容器.

修改完了配置文件,再去我们的控制器做修改,将IndexController.java修改为以下内容:

/**
 * 控制器
 */
@Controller
public class IndexController {
    private Logger logger = Logger.getLogger(this.getClass());
    //@RequestMapping 表示 方法与 哪个 URL请求对应,此处对应的是"/index"
    @RequestMapping("/index")
    public String index(){
        logger.info("Hello,Spring mvc=====>");
        return "index";
    }

}

在之前讲解使用注解定义Bean组件的时候说过,@Controller用于定义控制层的组件。

这里我们使用@Controller注解将我们的WelcomeController类定义为一个控制器,使其成为一个可以处理HTTP请求的控制器,再使用@RequestMapping注解对它的方法进行标注,确定welcome()方法对应的请求URL,即说明URL为/welcome的请求都将由这个方法进行处理。

这样一来,如果还有其他的业务需求,若不是很复杂,那么就可以直接在该类下增加相应的方法即可,然后再为方法标注@RequestMapping。这样无须创建很多个JavaBean作为Controller,这也是我们经常用的一种方式,支持注解式的处理器。

重新运行项目,与之前的结果是一样的。

注:<mvc:annotation-driven />的原理实现在以后会详解,这里只需要会用就行。

解释<mvc:annotation-driven />如下:

  • <mvc:annotation-driven />配置开启的两个常用的组件,RequestMappingHandlerMapping和RequestMappingHandlerAdapter。
  • RequestMappingHandlerMapping是HandlerMapping的实现类,它会在容器启动的时候,扫描容器内的bean,解析带有@RequestMapping 注解的方法,并将其解析为url和handlerMethod键值对方式注册到请求映射表中。
  • RequestMappingHandlerAdapter是HandlerAdapter的实现类,它是处理请求的适配器,说白了,就是确定调用哪个类的哪个方法,并且构造方法参数,返回值。

其实<context:component-scan/>标签是告诉Spring容器来扫描指定包下的类,并注册被@Component@Controller@Service@Repository等注解标记的组件。

<mvc:annotation-driven/>是告知Spring容器,我们启用注解驱动,支持@RequestMapping注解,这样我们就可以使用@RequestMapping来配置处理器。

> 扩展:使用编码的方式注册DispatcherServlet

在Servlet 3.0+环境中,您可以选择以编程方式配置Servlet容器作为备选方案,或者与web.xml文件结合使用。下面的例子注册了一个DispatcherServlet:

/**
 * 编程方式配置Servlet容器
 */
public class MyWebAppInitializer implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        XmlWebApplicationContext appContext = new XmlWebApplicationContext();
        appContext.setConfigLocation("classpath:springmvc-servlet.xml");
        ServletRegistration.Dynamic registration = servletContext.addServlet(
                "dispatcher",new DispatcherServlet(appContext));
        registration.setLoadOnStartup(1);
        registration.addMapping("/");
    }
}

WebApplicationInitializer是Spring MVC提供的一个接口,它可以确保检测到您的实现并自动用于初始化Servlet 3容器。AbstractDispatcherServletInitializerWebApplicationInitializer的实现抽象基类 , 继承它可以使得注册DispatcherServlet更加容易,方法是覆盖指定servlet映射和DispatcherServlet配置位置的方法。

对于使用基于java的Spring配置的应用程序,建议这样做,如下面的示例所示:

/**
 * 注册DispatcherServlet
 */
public class MyWebAppInitializer extends
        AbstractAnnotationConfigDispatcherServletInitializer {
    /**
     * 指定spring配置类所在的位置
     * @return
     */
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return null;
    }

    /**
     * 指定springmvc配置类所在的位置
     * @return
     */
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{MyWebConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}

MyWebConfig.java代码如下:

/**
 * 使用配置类的方式
 */
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "cn.smbms.controller")
public class MyWebConfig {
    /*@Bean(name = "/index.html")
    public IndexController indexController(){
        return new IndexController();
    }*/
    @Bean
    public InternalResourceViewResolver internalResourceViewResolver(){
        InternalResourceViewResolver internalResourceViewResolver =
                new InternalResourceViewResolver();
        //        InternalResourceViewResolver internalResourceViewResolver =
//                new InternalResourceViewResolver("/WEB-INF/jsp/",".jsp");
        internalResourceViewResolver.setPrefix("/WEB-INF/jsp/");
        internalResourceViewResolver.setSuffix(".jsp");
        return internalResourceViewResolver;
    }
}

如果使用基于xml的Spring配置,应该直接从AbstractDispatcherServletInitializer扩展,如下面的示例所示:

/**
 * 注册DispatcherServlet
 */
public class MyWebAppInitializer
extends AbstractDispatcherServletInitializer{

    /**
     * 指定spring配置文件所在的位置
     * @return
     */
    @Override
    protected WebApplicationContext createRootApplicationContext() {
        return null;
    }

    /**
     * 指定springmvc配置文件所在的位置
     * @return
     */
    @Override
    protected WebApplicationContext createServletApplicationContext() {
        XmlWebApplicationContext appContext = new XmlWebApplicationContext();
        appContext.setConfigLocation("classpath:springmvc-servlet.xml");
        return appContext;
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}

注意:

更多注册DispatcherServle方法可参考官方文档:https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-multipart**

2.3 Spring MVC 框架的请求处理流程以及体系结构

1. Spring MVC 框架的请求

通过上面的一个小示例,我们了解了Spring MVC环境的搭建,接下来再深入了解一下它的处理流程。
在这里插入图片描述
分析这张图,可以发现Spring MVC也是一个基于请求驱动的Web框架,并且也使用了前端控制器模式来进行设计,再根据请求映射规则分发给相应的页面控制器(处理器)来进行处理。

下面具体分析一下它的处理步骤:

  • (1) 首先用户发送请求到前端控制器(DispatcherServlet),前端控制器根据请求信息(比如URL)决定将请求分发给哪个页面控制器(Controller)来处理。对应上图中的步骤1、2。

  • (2) 页面控制器接收到请求之后,进行业务处理,处理完毕之后返回一个ModelAndView。对应上图中的步骤3、4、5。

  • (3) 前端控制器将控制权收回,然后根据返回的逻辑视图名,通过视图解析器选择真正的视图,将模型数据传入供其渲染展示。对应上图中的步骤6、7。

  • (4) 前端控制器再次收回控制权,将响应结果返回给浏览器客户端,至此整个流程结束。对应上图中的步骤8

2. Spring MVC框架的体系结构

根据上述的请求处理流程,我们继续了解下Spring MVC的体系结构。
在这里插入图片描述

通过上图我们发现,从接收请求到返回响应,Spring MVC框架的众多组件通力配合、各司其职地完成整个流程工作。

在整个框架中,Spring MVC通过一个前端控制器接收所有的请求,并将具体工作委托给其他组件进行处理,所以说DispatcherServlet处于核心地位,它负责协调组织不同组件完成请求处理并返回响应结果。

根据Spring MVC的请求处理过程,我们具体分析一下每个组件所负责的工作内容:

  • (1) 客户端发出HTTP请求,Web应用服务器接收此请求。如匹配DispatcherServlet的请求映射路径,则Web容器将该请求转交给DispatcherServlet处理;

  • (2) DispatcherServlet拿到请求之后,根据请求的信息(URL、请求参数、HTTP方法等)及HandlerMapping的配置找到处理请求的处理器(Handler);

  • (3) 当DispatcherServlet找到相应的Handler之后,通过HandlerAdapter对Handler进行封装,再以统一的适配器接口调用Handler。HandlerAdapter可以理解为真正使用Handler来干活的人。

  • (4) 在请求信息真正到达调用Handler的处理方法之前的这段时间,Spring MVC还完成了很多工作,它会将请求信息以一定的方式转换并绑定到请求方法的入参,对于入参的对象会进行数据转换、数据格式化以及数据校验等。这些都做完以后,最后才真正调用Handler的处理方法进行相应的业务逻辑处理。

  • (5) 处理器完成业务处理之后,将一个ModelAndView对象返回给DispatcherServlet,其中包含了逻辑视图名和模型数据信息。

  • (6) DispatcherServlet通过ViewResolver将逻辑视图名解析为真正的视图对象View,可以是JSP、HTML、XML、PDF、JSON等等,Spring MVC均可灵活配置。

  • (7) 得到真正的视图对象之后,DispatcherServlet会根据ModelAndView对象中的模型数据对View进行视图渲染。

  • (8) 最终客户端获得响应消息。

以上是关于Spring MVC的体系结构的分析,我们可以体会到其设计的精妙之处。

3. Spring MVC 框架的特点

通过上文的演示示例以及对Spring MVC的处理流程、体系结构的介绍,我们可以总结一下它的特点:

  • (1) 角色划分清晰。Model、View、Controller各司其职,耦合度较低。
  • (2) 灵活的配置功能。Spring的核心是IoC,统一可以实现在MVC上,把各种类当作Bean组件配置在Spring容器中。
  • (3) 提供了大量的接口和实现类,方便各种场景的开发。
  • (4) 真正做到与View层的实现无关。
  • (5) 结合Spring的依赖注入,更好地应用了面向接口编程的思想。
  • (6) 可以与Spring天衣无缝的整合
  • … 等等

总之,关于Spring家族的框架,对开发者来说都是一种不错的选择。

总结

Spring MVC结构比较复杂,学习的时候也要掌握方法。首先要明确Spring MVC是一个工具,既然是工具,那么就先学会它的使用方法,不要深陷其细节中,不建议刚开始学习就研究源码,深入浅出,慢慢地在使用过程中加深理解,最后看透源码自然会水到渠成。

3 . 参数传递

我们学习了Spring MVC的原理、处理流程及其体系结构,完成了请求与处理器之间的映射。接下来继续深入学习Spring MVC的一些知识,主要是参数传递(View到Controller、Controller到View)、视图解析器。

3.1 参数传递(View to Controller)

参数传递在项目中是必不可少的,只要是动态页面,肯定会涉及数据传递,想都不用想。接下来我们就学习一下View与Controller之间的参数传递。

页面往控制器传参,最粗暴的方式是直接入参,改造我们之前的IndexController.java如下:

/**
 * 控制器
 */
@Controller
public class IndexController {
    private Logger logger = Logger.getLogger(this.getClass());
    /**
     *  参数传递 View to Controller
     */
    @RequestMapping("/welcome")
    public String welcome(@RequestParam String username){
    	//如果方法的入参名跟传入的参数是一致的,可省略@RequestParam注解
        logger.info("welcome,"+username);
        return "index";
    }

}

运行项目,浏览器输入url:http://localhost:8080/ssm_ch09/welcome?username=admin,控制台正确输出日志信息.

以上示例中使用了@RequestParam在处理方法的的入参进行了标注,说明这是一个从View传来的参数(这里只简单的使用此注解,不进行任何设置,下文会讲述)。

这种情况下我们必须保证方法的入参名与View传来的参数名一致,可以发现,如果我们传来的参数名不是name而是userName,那么会报错400,错误信息是“请求中参数name不存在”。

但是在实际开发中,由于业务需求,可能有些参数并不是必需的,那么我们该如何解决这个问题呢?

这就需要详细了解@RequestMapping@RequestParam,两者结合使用,实现灵活的参数传递。

1. @RequestMapping

通过之前的学习,我们知道@RequestMapping的主要职责就是将不同的请求映射到对应的控制器处理方法。HTTP请求信息除了最基本的URL地址之外,还包括请求方法、HTTP协议及版本、报文头、报文体等,所以我们除了使用URL进行映射,还可以应用其他的HTTP信息来完成更精确的映射。

使用@RequestMapping完成映射,具体包含4个方面的信息:请求URL、请求参数、请求方法、请求头。

1)通过请求URL进行映射

我们之前的写法就是通过请求URL映射,即@RequestMapping("/welcome"),等同于@RequestMapping(value="/welcome")

此外,@RequestMapping可以在控制器的类定义处指定URL,即是这种的:

@Controller
@RequestMapping("/user")
public class IndexController{
    @RequestMapping(value = "/welcome")
    public String welcome(@RequestParam String name){
        //如果方法的入参名与传来的参数名一致,可省略@RequestParam注解
        System.out.println(name);
        return "welcome";
    }
}

在以上代码中,我们使用@RequestMapping注解在WelcomeController的类定义处进行了标注,定义URL为“/user”,此时这个URL相对于Web应用的部署路径,而welcome()方法处指定的URL则相对于类定义处的URL而言的,什么意思呢?

就是说,最后我们发过来的请求得是 http://localhost:8088/工程名/user/welcome?username=admin。需要注意的是在整个Web项目中,@RequestMapping映射的请求信息必须保证全局唯一.

这种是很常用的方式,在不同的Controller的类定义处指定相应的@RequestMapping,以便区分不同的请求,不易出错,也更规范。比如/user/add,/user/update,/user/delete,等等。

此外,分析源码可以看出来,@RequestMapping的返回值是一个String类型的数组,也就是说,我们还可以这样写:@RequestMapping({"/index","/welcome"})

这句话说明URL请求为/index的、/welcome的都可以进入该处理方

2 ) 通过请求参数、请求方法、请求URL进行映射

@RequestMapping除了可以使用请求URL进行映射请求之外,还可以使用请求参数、请求方法加以限制,通过多条件可以让请求映射更加准确。改造IndexController.java,如下:

@RequestMapping(value = "/welcome",method = RequestMethod.GET,params = "username")
public String welcome(String username){
    //如果方法的入参名与传来的参数名一致,可省略@RequestParam注解
    logger.info("welcome,"+username);
    return "welcome";
}

在上述代码中,@RequestMapping的value表示请求的URL,method表示请求方法,此次设置了GET,所以POST请求是进不来的,param表示请求参数,此处我们的参数名为name。运行我们的项目,同样可以访问此方法,下面分析一下:

首先,value(请求路径)匹配,method(超链接是GET请求)匹配,请求参数(name)也匹配,所以进入了处理方法。

下面分析几种错误情况。

  • 如果对地址:http://localhost:8080/ssm_demo_war/welcome?username=admin,则访问会出现400,控制台也会报出异常;

  • 如果将处理方法的入参改为String userCode,即这种的,与传参名不匹配:此时不会报错,只是后台会获取不到参数值,输出null。所以我们若选择方法参数直接入参,方法入参名必须与请求中的参数名一致。

简单的学习@RequestMapping之后,一开始的问题依然没有解决,如果没有传递参数依然报错。接下来学习@RequestParam,两者结合完成灵活的请求传参。

2. @RequestParam

在方法入参处使用@RequestParam注解指定其对应的请求参数。@RequestParam有以下三个参数:

  • value:参数名
  • required:是否是必需的,默认为true,表示请求中必须包含对应的参数名,否则抛出异常
  • defaultValue:默认参数名,不推荐使用,比较混乱

接下来利用@RequestParam注解中的第二个参数required 来解决上个示例中存在的参数非必需的问题,修改代码如下:

@RequestMapping(value = "/welcome")
public String welcome(@RequestParam(value="username",required = false) String username){
    logger.info("welcome,"+username);
    return "index";
}

测试运行,没问题。

这种情况下不要求方法的入参名与请求中的参数名一致,保证@RequestParam注解能够将二者关联上即可。但是为了规范起见,建议一致。

3.2 参数传递(Controller to View)

了解完从View到Controller的传参,接下来学习Controller到View的传参。

这就需要模型数据的处理了,对于MVC框架来说,模型数据是最重要的一部分,因为控制层是为了产生模型数据(Model),而视图(View)最终也是为了渲染模型数据并进行输出。

那么如何将模型数据传递给视图?Spring MVC提供了多种方式,介绍一下比较常用的:

1. ModelAndView

顾名思义,控制器的处理方法的返回值若是ModelAndView,即既包含Model,也包含View。那么拿到该对象之后,Spring MVC就可以使用视图对模型数据进行渲染。改造以上示例,拿到参数之后,再返回给页面:

@RequestMapping("/index1")
public ModelAndView index(String username){
    logger.info("welcome,"+username);
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.addObject("username",username);
    modelAndView.setViewName("index");
    return modelAndView;
}

通过以上代码可以看出在welcome处理方法中,返回了ModelAndView对象,并通过addObject()方法添加模型数据,通过setViewName()方法设置逻辑视图名。

ModelAndView对象的常用方法如下:

  • addObject(String attributeName,Object attributeValue); 该方法的第一个参数为key值,第二个参数为key对应的value。其中key的值可以随便指定,只要保证在该Model内唯一即可。
  • addAllObjects(Map<String,?> modelMap); 从此方法可以看出,模型数据也是一个Map对象,我们可以添加Map到Model。
  • setView(View view); 指定一个具体的视图对象。
  • setViewName(String viewName); 指定一个逻辑视图名。

然后在index.jsp页面使用EL表达式展现从Controller返回的ModelAndView对象中的Model(至于返回其他格式的数据,在以后介绍)。

<h1>Hello Spring MVC !!!!</h1>
<h1>username(key:username)---->${username}</h1>

上述代码中,通过EL表达式从Controller返回的ModelAndView对象中接收参数username的值.部署并运行测试,浏览器输入地址:http://localhost:8080/ssm_ch09/index1?username=admin. 运行正确,页面正确显示了username的参数值,并且控制台日志输出正确.

2. Model

除了可以使用ModelAndView对象来返回模型数据外,我们还可以使用Spring MVC提供的Model对象来完成模型数据的传递。

其实,Spring MVC在调用方法前会创建一个隐含的模型对象,作为模型数据的存储容器,一般称为“隐含模型”。

若处理方法的入参为Model类型,Spring MVC会将这个隐含模型的引用传递给这些入参,这样开发者就可以通过一个Model类型的入参,访问到模型中的所有数据,当然也可以做修改、添加等。

继续修改上面的示例,通过传入Model参数的方式完成参数返回。

IndexController关键代码如下:

/**
 * 参数传递 controler to view -(Model)
 * @param username
 * @param model
 * @return
 */
@RequestMapping("/index2")
public String index(String username, Model model){
    logger.info("welcome,"+username);
    model.addAttribute("username",username);
    return "index";
}

3. Map

我们之前说过,Spring MVC的Model其实就是一个Map数据结构,故我们使用Map作为处理方法的入参也是可行的.

/**
 * 参数传递 controler to view -(map)
 * @param username
 * @param map
 * @return
 */
@RequestMapping("/index3")
public String index3(String username, Map<String,Object> map){
    logger.info("welcome,"+username);
    //map = new HashMap<>();
    map.put("username",username);
    return "index";
}

这种方式依然可以达到我们想要的结果。

Spring MVC控制器的处理方法若有Map或者Model类型的入参,就会将请求内的隐含模型对象传递给这些入参,因此可以在方法体内对模型数据进行读写。

推荐使用Model。

4. @ModelAttribute

若希望将入参的数据对象放入数据模型中,就需要在相应入参前使用此注解。在后续的章节在详细讲解,此处做了解即可

5. @SessionAttributes

此注解可以将模型中的属性存入HttpSession中,以便在一次会话中共享该属性。以后会讲解。

4 . 视图解析器

请求处理方法执行完成之后,最终返回一个ModelAndView对象。对于那些返回String类型的处理方法,Spring MVC内部也会将它们装配成一个ModelAndView对象,它包含逻辑视图名和数据模型,那么接下来的工作就交给视图解析器(ViewResolver)了。

ViewResolver是Spring MVC处理视图的重要接口,通过它可以将控制器返回的逻辑视图名解析成一个真正的视图对象。

Spring MVC默认提供了多种视图解析器,所有的视图解析器都实现了ViewResolver接口,如图:
在这里插入图片描述
对于JSP这种常见的视图技术,通常使用InternalResourceViewResolver作为视图解析器。那么它是如何工作的?

InternalResourceViewResolver是最常用的视图解析器,通常用于查找JSP和JSTL等视图。

通过源码就可以看出,它是URLBasedViewResolver的子类,会将返回的逻辑视图名都解析成InternalResourceViewResolver对象,该对象会把Controller的处理方法返回的模型属性都放在对应的请求作用域中,然后通过RequestDispatcher在服务器端把请求转发到目标URL。

备注: 快捷键 Alt+7就能显示当前类中的所有方法、全局常量,方法还包括形参和返回值

我们只需要在配置文件中进行如下配置:

<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <!--通过配置prefix和suffix,将视图逻辑门解析为:/WEB-INF/jsp/<viewName>.jsp -->
<!--<constructor-arg index="0" value="/WEB-INF/jsp/"/>
        <constructor-arg index="1" value=".jsp"/>-->
    <property name="prefix" value="/WEB-INF/jsp/"/>
    <property name="suffix" value=".jsp"/>
</bean>

如果控制器的处理方法返回字符串“index”,那么经过此视图解析器的解析,即为:/WEB-INF/jsp/index.jsp

视图解析器的第二种配置如下:

<!--配置视图解析器第二种方式-->
 <mvc:view-resolvers>
    <!--默认情况下,“/WEB-INF/”被注册为一个视图名称的前缀。
    “.jsp”作为后缀-->
     <!--通过配置prefix和suffix,将视图逻辑解析为:/WEB-INF/jsp/<viewName>.jsp-->
    <mvc:jsp prefix="/WEB-INF/jsp/" suffix=".jsp" />
</mvc:view-resolvers>

5 . 扩展Maven插件

maven安装tomcat插件官网:https://tomcat.apache.org/maven-plugin-2.2/

Web项目 pom.xml文件中加入以下内容:

<build>
    <plugins>
        <!--定义tomcat插件-->
        <plugin>
            <groupId>org.apache.tomcat.maven</groupId>
            <artifactId>tomcat7-maven-plugin</artifactId>
            <version>2.2</version>
            <configuration>
                <!--端口号-->
                <port>8080</port>
                <!-- 路径 -->
                <path>/</path>
                 <!-- 解决get乱码 -->
                <uriEncoding>utf-8</uriEncoding>
            </configuration>
        </plugin>
        <!-- jrebel热部署插件 -->
        <plugin>
            <groupId>org.zeroturnaround</groupId>
            <artifactId>jrebel-maven-plugin</artifactId>
            <version>1.1.9</version>
            <executions>
                <execution>
                    <id>generate-rebel-xml</id>
                    <phase>process-resources</phase>
                    <goals>
                        <goal>generate</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

本章总结如下 :

最后,我们对Spring MVC做一下简单的总结:

  • MVC设计模式在各种成熟框架中都得到了良好的运用,它将Model、View、Controller三层清晰地分开,搭建一个松耦合、高可用性、高可适用性的完美架构。
  • Spring MVC框架是典型的MVC框架,是一个结构清晰的JSP Model2实现。它基于Servlet,核心是DispatcherServlet。
  • Spring MVC的处理器映射(HandlerMapping)可配置为注解式的处理器,只需配置<mvc:annotation-driven />标签即可。
  • Spring MVC的控制器的处理方法返回的ModelAndView对象,包括模型数据和视图信息。
  • Spring MVC通过视图解析器来完成视图解析工作,把控制器的处理方法返回的逻辑视图名解析成一个真正的视图对象。

对于Spring MVC框架的更多内容,以及控制器返回JSON数据、HTML字符串等,会在后续的章节中讲到

标签:Chapter,控制器,请求,SpringMVC,Spring,09,视图,Controller,MVC
来源: https://blog.csdn.net/weixin_40154786/article/details/110732028

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有