Spring学习笔记(5)一SpringMVC处理请求原理

2022-04-14 16:05:06 浏览数 (1)

学习技术方面由浅入深的层次步骤: 了解:入门,如何去使用这门技术 掌握:具体,它的原理是什么 熟悉:规则实践,在理解原理的基础上,如何去模仿, 精通:解决问题 专家:觉悟,扩展创新,如何去进一步演化

我们开发springmvc 的web,主要代码就是controller,使用《Spring学习笔记(3)一SpringMVC快速入门》例子:

package com.demo.springmvc.controller;

@Controller

@RequestMapping("/user")

public class UserController {

代码语言:txt复制
 @RequestMapping("/abc")
代码语言:txt复制
 @ResponseBody
代码语言:txt复制
 public String test() {
代码语言:txt复制
     System.out.println("------------TestController-----------");
代码语言:txt复制
     return "Hello test";
代码语言:txt复制
 }

}

我们看看web请求是怎么交给AppController处理的?

一、SpringMVC框架基本流程

SpringMVC框架主要由四个接口组成:

1)、DispatcherServlet前端控制器、

2)、HandlerMapping请求处理器映射器、

3)、Controller、

4)、ViewResolver

处理请求流程

  1. 将客户端请求提交给DispatcherServlet
  2. 根据<servlet-name>servlet.xml的配置,查找HandlerMapping
  3. HandlerMapping根据请求URL找到处理请求的具体Controller(Handler)
  4. DispatcherServletd调用处理器适配器去执行Controller,Controller调用业务逻辑处理(DispatchServlet会在调用选定的Controller的handlerRequest方法)
  5. 处理完成之后,返回ModelAndView对象给DispatcherServlet
  6. 框架通过ViewResolver找到负责显示的具体View
  7. 由View的render方法将结果渲染到客户端

二、DispatcherServlet处理请求详解

1、DispatcherServlet简述

https://docs.spring.io/spring/docs/5.0.0.RC3/spring-framework-reference/web.html#mvc-servlet

是整个SpringMVC框架的核心。从名称来看,它是一个Servlet的是一个具体类,负责统一分发所有请求的控制器。

主要职责:

  • 初始化DispatcherServlet上下文对应的WebApplicationContext,并与业务层、持久化层建立联系
  • 初始化SpringMVC的各个组件,并装配到DispatcherServlet中
  • 拦截符合特定格式的URL请求

在web.xml文件中进行配置,负责接收HTTP请求、组织协调SpringMVC的各个组成部分。

代码语言:javascript复制
<servlet>
		<servlet-name>apiserver</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
                <!--
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:apiserver-servlet.xml</param-value>
		</init-param> -->
		<load-on-startup>1</load-on-startup>
	</servlet>


	<servlet-mapping>
		<servlet-name>apiserver</servlet-name>
		<url-pattern>/apiserver/*</url-pattern>
	</servlet-mapping>

如果不指定<param-value>的值,则默认配置文件为/WEB-INF/<servlet-name>-servlet.xml。<load-on-startup>是启动顺序,通常让Servlet跟随Servlet容器一起启动。<url-pattern>定义要拦截的URL请求。

拦截规则:

  • *.xxx,指定要拦截的特点后缀类型,最简单实用的方式,并且不会拦截静态文件
  • /,使用REST风格进行拦截,但是会导致静态文件被拦截不能正常显示
  • /*,不能像Struts那样使用,会导致不能访问jsp

如果使用/进行拦截,并且希望正常访问静态文件,可以在DispatcherServlet之前,使用DefaultServlet先拦截特定类型的请求(如:*.js、*.css等)。

2、DispatchServlet是如何工作

DispatchServlet虽然继承自抽象基类FrameworkServlet,但本质是HttpServlet的具体实现(The DispatcherServlet is an actual Servlet (it inherits from the HttpServlet base class), and as such is declared in your web application. )

DispatcherServlet继承关系是这样的:GenericServlet <- HttpServlet <- HttpServletBean <- FreamworkServlet <- DispatcherServlet

1、Servlet是Java Web应用的基石

《Spring学习笔记(4)一SpringMVC快速入门》例子:我们给出的是一个简单的spring-mvc应用,并放入了tomcat中(springboot 内嵌tomcat启动其实也是一样的)。

我们都知道Tomcat 是一个servlet容器,没有spring-mvc之前,就是使用servlet jsp来开发web应用。详解《Spring学习笔记(2 )一JAVA Web发展历》《Spring学习笔记(3)-tomcat运行机制》

由于Tomcat是一个web容器,每一个发送给Tomcat服务器的HTTP请求自然会被一个Java Servlet处理。所以,SpringMvc 必定有一个servlet,SpringWeb应用的入口必定是一个Servlet。

简单来说,Servlet是任何Java Web应用的核心组件(除非你不用servlet规范,比如你使用netty)。Servlet它是低层次的,并且不会像MVC那样强加于特定的编程模式。它只是可以让你写一个偶一个Servlet,一个HTTP Servlet可以接受一个HTTP请求,然后处理它,并返回一个响应。而springmvc 就是使用了一个大的servlet。

2、GenericServlet

GenericServlet是Servlet规范的一部分,它并不直接关注HTTP。它定义了一个service()方法用来接收传递过来的请求,并产生响应。(这里的请求和响应不是指HTTP请求)

代码语言:javascript复制
public abstract void service(ServletRequest req, ServletResponse res) 
 throws ServletException, IOException;

注意,这里的参数中的ServletRequest,ServletResponse并不是和HTTP协议绑定的,Http有具体协议Servlet。

3、HttpServlet

顾名思义,HttpServlet类就是规范中定义的基于HTTP的Servlet实现。更实际的是,HttpServlet是一个具有service()方法实现的抽象类,它通过HTTP方法类型分割请求,大致如下所示:

代码语言:javascript复制
protected void service(HttpServletRequest req, HttpServletResponse resp)
  throws ServletException, IOException {
 
  String method = req.getMethod();
  if (method.equals(METHOD_GET)) {
    // ...
    doGet(req, resp);
  } else if (method.equals(METHOD_HEAD)) {
    // ...
    doHead(req, resp);
  } else if (method.equals(METHOD_POST)) {
    doPost(req, resp);
    // ...
  }

根据请求的不同, get,post方法会分别被不同方法处理。

4、HttpServletBean

在这个继承链中,HttpServletBean是进入spring的第一个层次。从它所在的包名就知道:

从HttpServletBean开始往下的几个servlet都是spring中的类。HttpServletBean 就是一个servlet,它继承自HttpServlet,就像是我们在使用servlet jsp开发时候定义的servlet一样。

根据servlet的生命周期我们知道,servlet会被容器初始化,初始化时候,其init()方法会被调用。在springmvc框架中 HttpServletBean使用从web.xml或WebApplicationInitializer收到的servlet init-param值来注入bean的属性。

在请求应用程序的情况下,为这些特定的HTTP请求调用doGet(),doPost()等方法。

5、FrameworkServlet是基本servlet实现类

FrameworkServlet是Spring web框架的基本servlet实现类,通过JavaBean的方式集成了Application context,所有新实现的servlet最好都继承于该类。该类提供了HttpServlet的所有接口实现,自带了一个web容器,它实现WebApplicationContextAware接口,所以能根据指定的容器配置文件,来初始化自己管理的容器。

特别的, FrameworkServlet的initFrameworkServlet()这个方法是控制器的初始化方法,用来初始化HandlerMappings之类的对象,这也是延迟到子类实现的.其实就是一个Template模式的实现. 总的看来,Spring就是通过这样来实现它的控制反转的:用框架来控制流程,而不是用户.

FrameworkServlet的超类HttpServletBean将init-param注入为bean属性。所以,如果servlet的contextClass init-param中提供了context类的名字,那么这个context类的实例将会被创建,用作应用的context。否则,将会使用XmlWebApplicationContext作为默认的。

FrameworkServlet提供两个功能:

1)、为每个servlet管理一个WebApplicationContext实例(即每个servlet会自己的一个web容器),每个servlet都有自己的配置空间,相互独立。(每一个<servlet> tag之间的配置属于一个namespace, 配置一个application context。)

2)、对每一个请求,无论是否成功处理,都会在每个请求上发布事件。

6、DispatcherServlet是Spring MVC的核心

DispatchServlet控制器继承自抽象基类FrameworkServlet,它的属性webApplicationContext就代表着这个web程序上下文,而这个上下文对象默认实现就是从一个web.xml文件读取配置信息. WebApplicationContext其实是beans包的东西,这个包提供了这个Spring整个框架的基础结构.

即DispatcherServlet统一请求处理。在Springmvc的项目中,我们通常会在web.xml配置一个servlet,如下:

代码语言:javascript复制
<servlet>
		<servlet-name>apiserver</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:apiserver-servlet.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>

DispatcherServlet继承关系,其父类中正儿八经的servlet类是HttpServletBean这个servlet类是应用启动入口。其生命周期的第一阶段init()方法完成初始化工作。

DispatchServlet由于继承自抽象基类FrameworkServlet,而FrameworkServlet里的doGet(),doPost()方法里有调用processRequest(),跳到processRequest()里去看,结果发现它有把具体实现委托给了doService(request, response);方法.FrameworkServlet的doService方法是抽象方法, 由DispatchServlet的doService() 具体实现。

跳到doService()一看究竟,就会发现真正工作的又是另一个助手函数doDispatch(request, response):

如果在程序里面抛出一个异常,就能清晰的看到执行栈了:

doDispatch方法:

通过doDispatch(request, response)源码,我们可以清晰看到拦截器和controler具体实现原理和流程:

1)、获取HandlerExecutionChain,HandlerExecutionChain是对Controller和它的Interceptors的进行了包装。

2)、获取HandlerAdapter,即Handler对应的适配器。

3)、执行拦截器preHandle() 方法

4)、由HandlerAdapter的handle方法执行Controller的handleRequest,并且返回一个ModelAndView

5)、执行拦截器postHandle方法

6)、执行ModelAndView的render(mv, request, response)方法把合适的视图展现给用户;

7)、执行拦截器afterCompletion() 方法

代码语言:javascript复制
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
       //HandlerExecutionChain是对Controller和它的Interceptors的进行了包装;
        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;
	            //getHandler是从HandlerMappings中取出对应的handlerMapping对象, 
		     //每个HandlerMapping对象代表一个Controller和URL的映射
                    mappedHandler = this.getHandler(processedRequest);
                    if(mappedHandler == null || mappedHandler.getHandler() == null) {
                        this.noHandlerFound(processedRequest, response);
                        return;
                    }
		    //获取HandlerAdapter
                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                    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;
                        }
                    }

		   //执行Controller的拦截器方法preHandle
                    if(!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }
		    //由HandlerAdapter的handle方法执行Controller的handleRequest,并且返回一个ModelAndView
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
					
					
                    if(asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }
                    
                    this.applyDefaultViewName(processedRequest, mv);
		//执行Controller的拦截器方法postHandle
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                } catch (Exception var19) {
                    dispatchException = var19;
                }
		//实际执行ModelAndView的render(mv, request, response)方法把合适的视图展现给用户;
                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()) {
		//执行Controller的拦截器方法afterCompletion 
                if(mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            } else if(multipartRequestParsed) {
                this.cleanupMultipart(processedRequest);
            }

        }
    }

doDispatch方法主要作用就是找到合适的 handler 来处理请求。handler通常是一个某个类型的对象,并且不限定特定的接口。因此spring需要找到这个handler的适配器。这个Handler通常是一个HandlerMethod实例,

为了找到与请求匹配handler,spring需要从已注册的HandlerMapping接口实现类里边去找。这个查找过程就是在上面的getHandler() 方法完成得到的是一个HandlerExecutionChain。 这里体现了责任链模式。

这个getHandler() 会遍历一个HandlerMapping的map。由于我们一般都使用注解形式:@Controller,@RequestMapping注解。因此这里找到HandlerMapping实现就是RequestMappingHandlerMapping

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

getHandlerAdapter()方法找到最终的handler适配器,找到的适配器就是RequestMappingHandlerAdapter,(因为我们使用的是@RequestMapping注解形式)。

本例中,我们定义了UserController 的test()方法,并用@Controller,@RequestMapping对其分别进行注解,因此这里得到的适配器RequestMappingHandlerAdapter所适配HandlerMethod就是 UserController 的test()方法的 。

HandlerAdapter处理请求

上面通过 确定了HandlerAdapter之后,就要执行handle() 方法了,即上面代码中,try语句块里边的ha.handle()。handle()方法定义为:

代码语言:javascript复制
ModelAndView handle(HttpServletRequest request,
HttpServletResponse response,Object handler) throws Exception;

这个方法有两种处理方式:

  • 自己将数据写到response,然后return null
  • 返回一个ModelAndView 对象,让DispatcherServlet自己来处理后面渲染工作。

三、HandlerMapping具体说明

1、作用:负责完成请求到控制器的映射

通过使用HandlerMapping,控制器可以用URL和某一个Controller进行标准的映射,而实现URL映射的具体子类UrlHandlerMapping.同时还可以对控制器进行拦截。

Spring还允许我们自定义映射,比如通过Session,cookie或者用户状态来映射.而这一切仅仅只需要实现HandlerMapping接口而已.不过URL映射已经能满足大部分的要求。

HandlerMapping接口只有一个方法,getHandler,但是返回的是HandlerExecutionChain,一个执行链。其主要作用是将Http请求的URL映射到对应的handler上,返回的执行链中同时包含了handler本身和对应的拦截器链。handler的类型是多样的,在源码中handler类型为Object,可以是实现了Controller接口的对象,也可以是某个方法。因此,也有多种实现类,有直接以类作为handler的,有以方法作为handler的。

2、常见的handlerMapping

1)、注解RequestMapping的handlerMapping:

代码语言:txt复制
  在web开发中在方法上常见的RequestMapping注解handleMethod类型的handler,对应的handlerMapping为RequestMappingHandlerMapping。继承AbstractHandlerMethodMapping,持有请求URL与HandlerMethod之间的映射表。

2)、BeanNameUrlHandlerMapping:通过url名字,找到对应的bean的name的控制器

3)、SimpleUrlHandlerMapping:SimpleUrlHandlerMapping 【简单得URL处理映射】 通过key找到bean的id的控制器

4)、ControllerClassNameHandlerMapping 【控制器的类名处理映射】 不用配置访问路径,

代码语言:txt复制
      默认的访问路径就是类名首字母大写变小写,加.do后缀

3、配置多映射处理器handlerMapping

请求映射到具体的Handler中的方法,在springmvc内置了很多映射处理器,而且我们也可以自定义映射处理器。

方案一:基于xml配置映射,可以利用SimpleUrlHandlerMapping、BeanNameUrlHandlerMapping、ControllerClassNameHandlerMapping 进行Url映射和拦截请求。具体配置详见:《SpringMVC入门2:实现Controller接口》

方案二:基于注解映射,可以使用DefaultAnnotationHandlerMapping。

1)web.xml配置DefaultAnnotationHandlerMapping

<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">

若web.xml配置了<mvc:annotation-driven />,会自动注册这个bean,就不须要我们显示的注册这个bean了.

2)并在Controller类上使用:

代码语言:txt复制
  @Controller
代码语言:txt复制
  @RequestMapping("/user")

4、可以对控制器进行拦截,声明拦截器:

代码语言:javascript复制
    <mvc:interceptors>
        <!--<bean class="com.ma.interceptor.CustomeInterceptor" />-->
        <!--拦截器1-->
        <mvc:interceptor>
            <!--配置拦截器的作用路径-->
            <mvc:mapping path="/**"/>
            <mvc:exclude-mapping path=""/>
            <!--定义在<mvc:interceptor>下面的表示匹配指定路径的请求才进行拦截-->
            <bean class="com.demo.interceptor.Intercptor1"/>
        </mvc:interceptor>
        <!--拦截器2-->
        <mvc:interceptor>
            <mvc:mapping path="/hello"/>
            <bean class="com.demo.interceptor.Interceptor2"/>
   </mvc:interceptor>

四、处理器适配器HandlerAdapter

1、作用:按照特定规则(HandlerAdapter要求的规则)去执行Handler

通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。

由于handler的多样性,需要为框架提供更好的可拓展性,使用了适配器模式,通过handlerAdapter来调用handler的handle方法。在handlerMapping中获取到HandlerExecutionChain后,从中取出handler本身,遍历已经DispatcherServlet中初始化过的handlerAdapter找到可以适配的HandlerAdapter。

2、HandlerAdapter类型

1)、RequestMappingHandlerAdapter:使用注解@Controller和@RequestMapping注解对应的Adapter为RequestMappingHandlerAdapter,该Adapter知道handler类型为HandlerMethod,

2)、SimpleControllerHandlerAdapter:SimpleControllerHandlerAdapter处理spring mvc 的Controller接口实例(注意,不要把这里的controller实例和@Controller注解POJO混淆,这里controller 指的是org.springframework.web.servlet.mvc.Controller接口 ),并返回ModelAndView,代码如下:

代码语言:txt复制
   详解《[Spring学习笔记(7)一SpringMVC快速入门](https://guisu.blog.csdn.net/article/details/107129933)》
代码语言:javascript复制
public class DemoController implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse response) throws Exception {
        ModelAndView mav = new ModelAndView("demo");
        response.setCharacterEncoding("utf-8");
        response.setContentType("text/html");
        mav.addObject("message", "spring MVC");
        return mav;
    }
}

3)、SimpleServletHandlerAdapter :SimpleServletHandlerAdapter 适配的是 Servlet作为request handler的情况,Servlet是不知道MovelAndView的,所以,它的方法并不负责渲染页面,因此没有返回ModelAndView,只是返回null:

public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { ((Servlet) handler).service(request, response); return null; }

五、Controller(Handler)

1、作用:负责处理用户请求(自己开发)。

负责处理用户请求,完成之后返回ModelAndView对象给前端控制器。因为需要考虑并发,所以必须保证线程安全并且可重用。

2、实现Controller接口或继承父类的方式编写控制器

SpringMVC中的Controller与Struts中的Action基本相同。可以通过实现Controller接口或继承父类的方式编写控制器。 Controller接口只有一个方法handleRequest(),放回一个ModelAndView对象,如同设计目标所说的那样,每个Controller都是一个java组件,所以它可以在上下文环境中任意配置,组件属性都会在初始化的时候被配置.Spring自己提供了几个具体的实现.方便我们使用

1)实现Controller接口:

代码语言:javascript复制
public class HelloController implements Controller {
  // 相当于servlet的doGet和doPost方法
  public ModelAndView handleRequest(HttpServletRequest request,HttpServletResponse response) throws Exception {
    // 接收数据
    // 调用服务层
    return new ModelAndView("success","username","sean");
  }
}

2)继承AbstractController类,该类与接口类似,需要重写里边的方法。

3)继承MultiActionController类,可以实现多个方法,处理多个请求。

代码语言:javascript复制
public class MultiController extends MultiActionController {
  // 自定义处理请求的方法
  public ModelAndView insert(HttpServletRequest request,HttpServletResponse response) throws Exception {
    return new ModelAndView("insertSuccess");
  }
  public ModelAndView update(HttpServletRequest request,HttpServletResponse response) throws Exception {
    return new ModelAndView("updateSuccess");
  }
}

相应的配置文件:

代码语言:javascript复制
<bean id="multi" class="com.demo.ssm.controller.MultiController"></bean>

<bean id="simpleUrlHandlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
  <property name="urlMap">
    <map>
      <!-- 使用通配符进行模糊匹配 -->
      <entry key="/multi-*.html" value-ref="multi"></entry>
    </map>
  </property>
</bean>

<bean id="multiActionController" class="org.springframework.web.servlet.mvc.multiaction.MultiActionController">
  <property name="methodNameResolver" ref="methodNameResolver"></property>
  <property name="delegate">
    <ref bean="multi" />
  </property>
</bean>

<bean id="methodNameResolver" class="org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver">
  <property name="mappings">
    <props>
      <prop key="/multi-insert.html">insert</prop>
      <prop key="/multi-update.html">update</prop>
    </props>
  </property>
</bean>

4)继承AbstractCommandController类,用于获取页面的参数,将参数封装到指定的对象模型中。

代码语言:javascript复制
public class CommandController extends AbstractCommandController {
  public CommandController() {
    // User是用于接收请求参数的数据模型类
    this.setCommandClass(User.class);
  }

  protected ModelAndView handle(HttpServletRequest request,HttpServletResponse response,
    Object command, BindException errors) throws Exception {
    // command参数是指定的数据模型对象
    User user = (User)command;
    return new ModelAndView("command");
  }
}

3、使用 @Controller 定义 Controller 控制器

@Controller 用于标记在一个类上,使用它标记的类就是一个SpringMVC Controller 对象。分发处理器将会扫描使用了该注解的类的方法,并检测该方法是否使用了@RequestMapping 注解。@Controller 只是定义了一个控制器类,而使用@RequestMapping 注解的方法才是真正处理请求的处理器。

单单使用@Controller 标记在一个类上还不能真正意义上的说它就是SpringMVC 的一个控制器类,因为这个时候Spring 还不认识它。那么要如何做Spring 才能认识它呢?这个时候就需要我们把这个控制器类交给Spring 来管理。

代码语言:javascript复制
@Controller
@RequestMapping("/user")
public class UserController {
    @RequestMapping("/")
    @ResponseBody
    public String index() {
        return "Hello world";
    }
    @RequestMapping("/abc")
    @ResponseBody
    public String test() {
        return "Hello test";
    }
}

这个时候有两种方式可以把UserController 交给Spring 管理,好让它能够识别我们标记的@Controller 。

第一种方式是在SpringMVC 的配置文件中定义UserController的bean 对象。

<bean class="com.demo.springmvc.controller.UserController"/>

2: 第二种方式是在SpringMVC 的配置文件中告诉Spring 该到哪里去找标记为@Controller 的Controller 控制器。

<context:component-scan base-package="com.demo"/>

八、ViewResolver

作用:进行视图解析,根据逻辑视图名解析成真正的视图(view)

Controller通常返回包含视图名字而不是视图对象的ModelAndView对象.从而彻底的解除了控制器和视图之间的耦合关系,并且在这里还可以提供国际化的支持.

在你的配置文件中你可以:

welcomeView.class = org.springframework.web.servlet.view. InternalResourceView

welcomeView.url=/welcome.jsp

也可以

welcomeView.class = org.springframework.web.servlet.view.xslt. XsltView

welcomeView.url=/xslt/default.xslt

负责对ModelAndView对象的解析,并查找对应的View对象。SpringMVC框架默认通过转发进行页面跳转,如果想通过重定向的方式进行跳转,有以下几种实现方式。

在控制器中,返回一个RedirectView对象,由对象来指定重定向的URL:

代码语言:javascript复制
View view = new RedirectView("/index.jsp");
return new ModelAndView(view);

通过配置文件设置,在控制器返回对象的方法中只需要设置一个与bean id一致的字符串即可:

代码语言:javascript复制
<bean id="viewResolver" class="org.springframework.web.servlet.view.BeanNameViewResolver"></bean>
<bean id="index" class="org.springframework.web.servlet.view.RedirectView">
  <property name="url" value="/index.jsp"></property>
</bean>

return new ModelAndView("index");

直接跳转:

代码语言:javascript复制
return "redirect:/index.jsp";

如果一个配置文件中出现多个视图解析器,可以通过设置order属性来设定优先级。值越低,优先级越高。

九、View

需要程序员开发,比如jsp。

这也是一个java组件,它不做任何请求处理或是业务逻辑,它仅仅获取模型传递的数据,并把数据显示出来.它里面的 render方法按照如下流程工作:

  1. 设置模型的数据到request作用域
  2. 取得视图的URL
  3. 转发到对应的URL

springmvc框架提供了很多的View视图类型的支持,包括:jstlView、freemarkerView、pdfView等。我们最常用的视图就是jsp。

一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。

0 人点赞