欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

菜鸟学SpringMVC之——SpringMVC执行原理、向前端返回数据的三种方式

程序员文章站 2024-02-27 20:49:15
...

SpringMVC

SpringMVC原理

SpringMVC请求发送到返回请求的全过程

菜鸟学SpringMVC之——SpringMVC执行原理、向前端返回数据的三种方式

菜鸟学SpringMVC之——SpringMVC执行原理、向前端返回数据的三种方式

流程:

  1. 客户端发送一个请求,Tomcat获得这个请求后将其做了一个映射判断(<url-pattern>/*</url-pattern>,如果访问地址符合/*,则交给DisPatcherServlet )传给DispatcherServlet(前端控制器)

  2. DispatcherServlet会根据请求去 HandlerMapping查找Handler(可以根据xml配置、注解@RequestMapping进行查找 )生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet.

  3. DispatcherServlet调用HandlerAdaptor执行对应的处理器(Handler/Controller),并将请求传来的参数传给Handler,

  4. Handler(后端控制器)通过Service调用数据库找到对应的信息然后返回给Handler

  5. Handler将ModelAndView返回给HandlerAdaptor

    当Handler在完成逻辑处理后,通常会产生一些信息,这些信息就是需要返回给用户并在浏览器上显示的信息,它们被称为模型(Model)。仅仅返回原始的信息时不够的——这些信息需要以用户友好的方式进行格式化,一般会是 HTML,所以,信息需要发送给一个视图(view),通常会是 JSP。

    Handler所做的最后一件事就是将模型数据打包,并且表示出用于渲染输出的视图名(即代码中:mav.setViewName("index")的index,就是逻辑视图名)(逻辑视图名)。它接下来会将请求连同ModelAndView发送回HandlerAdaptor。

  6. HandlerAdaptor再将ModelAndView返回给DispatcherServlet,

  7. 但是DispatcherServlet不会处理这个ModelAndView,所以将其传给ViewResolver进行解析,ViewResolver根据逻辑视图名称解析真正的视图 ,并返回给DispatcherServlet。

    这样以来,控制器就不会和特定的视图相耦合,传递给 DispatcherServlet 的视图名并不直接表示某个特定的 JSP。(实际上,它甚至不能确定视图就是 JSP)相反,它传递的仅仅是一个逻辑名称,这个名称将会用来查找产生结果的真正视图(给逻辑视图名拼接前缀和后缀, 进而确定一个 Web 应用中视图资源的物理路径 )

    菜鸟学SpringMVC之——SpringMVC执行原理、向前端返回数据的三种方式

  8. DispatcherServlet进行视图渲染,就是将Model填充到Response中显示在view上。最后在传到前端

上面的这些组件中,只有Handler和View需要程序员开发。 Handler 是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。 View是一个接口,实现类支持不同的View类型(jstlView、freemarkerView、pdfView…)

源码

HandlerMapping(处理器映射器):根据配置或注解找到最重要执行的Handler

  • HandlerMapping接口的实现类:

    SimpleUrlHandlerMapping类通过配置文件把URL映射到Controller类。

    DefaultAnnotationHandlerMapping类通过注解把URL映射到Controller类。

HandlerAdapter(处理器适配器):帮助DispatcherServlet处理映射请求处理程序的适配器,而不用考虑实际调用的是 哪个处理程序

  • AnnotationMethodHandlerAdapter:通过注解,把请求URL映射到Controller类的方法上。

ViewResolver: 它接受一个由 DispaterServlet 传递过来的逻辑视图名来拼装为物理视图名即具体的页面地址。

  • UrlBasedViewResolver类 通过配置文件,把一个视图名交给到一个View来处理。

向前端返回数据的三种方式

了解了SpringMVC的原理后接下来看一下怎么同SpringMVC给前端返回数据(有三种方式)

@Controller
@RequestMapping("/user")
public class UserController {

    @Autowired
    IUserService service;

    @RequestMapping(value = "/login.do", method = {RequestMethod.GET})
    public String login(User user) {
        User result = service.login(user);
        //登录是否成功就看result是否有返回值
//        System.out.println("判断登录是否成功" + result.getId());
        return result.getId() + "";
    }
}

菜鸟学SpringMVC之——SpringMVC执行原理、向前端返回数据的三种方式
所以这里报错的原因就是:

代码中返回一个String,这个返回的String就代表ModelAndView的View的逻辑视图名,然后交给ViewResolver,进行解析返回给DispatcherServlet,打开路径/user/1下的视图,DispatcherServlet发现并找不到这个路径,所以返回404。现在就需要让视图解析器将1当作文字内容处理,而不要当作视图解析处理。

向前端返回数据的第一种方式:直接返回字符串

加注解@ResponseBody,将返回值当作文字处理。

@Controller
@RequestMapping("/user")
public class UserController {

    @Autowired
    IUserService service;

    @RequestMapping(value = "/login.do", method = {RequestMethod.GET})
    @ResponseBody
    public String login(User user) {
        User result = service.login(user);
        return result.getId() + "";
    }
}

菜鸟学SpringMVC之——SpringMVC执行原理、向前端返回数据的三种方式

也可以返回一个对象。它会将这个对象当作json格式返回给前端

@Controller
@RequestMapping("/user")
public class UserController {

    @Autowired
    IUserService service;

    @RequestMapping(value = "/login.do", method = {RequestMethod.GET})
    @ResponseBody
    public User login(User user) {
        User result = service.login(user);
        return result;
    }
}

但是配置文件需要稍加修改,打开mvc注解驱动,添加mvc:annotation-driven标签,它的作用就是让@ResponseBody等其他注解生效

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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
">
	<!--mvc注解驱动-->
    <mvc:annotation-driven></mvc:annotation-driven>
	<!--其他配置省略-->
</beans>

导入依赖jar包

    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.62</version>
    </dependency>

所以以后向前端传json就很简单。


接下来如果要直接打开一个静态页面将不能打开,是因为DispatcherServlet将他拦截处理了,但是静态资源根本不需要他来处理,这么来修改:

  • 方式一:改web.xml文件,让他只过滤.do的请求(<url-pattern>*.do</url-pattern>),这时index.html就不会拦截。

    配置成*.do表示请求发到Tomcat后,Tomcat发现是.html请求,所以不用给DispatcherServlet,所以Tomcat直接到对应文件夹去找对应的文件返回给前端

  • 还有一种改法,这个web.xml文件不改动,在webapp下创建static文件夹

菜鸟学SpringMVC之——SpringMVC执行原理、向前端返回数据的三种方式
​ 然后修改applicationContext文件,添加mvc:resources标签,这个标签的作用就是为资源文件(html、传css、js)做映射。

比如我们当前的这个例子中配置的,DispatcherServlet将不会拦截以/pages开头的所有请求路径,并将其当作静态资源交由Servlet处理

<mvc:resources mapping="/pages/**" location="/static/"></mvc:resources><!--表示前端请求的pages下的所有内容去static下面去找-->

因为web中配置的是只要是/*的请求都会先传给DispatcherServlet,所以不管是请求页面还是接口,都将传给DispatcherServlet,DispatcherServlet拿到后发现Spring中配置了mvc:resources,所以他就不用管了,直接到对应的文件目录下找到对应文件将其返回给前端

向前端返回数据的方式二:接下来实现重定向

@Controller
@RequestMapping("/user")
public class UserController {
    @Autowired
    IUserService service;
    @RequestMapping(value = "/login.do", method = {RequestMethod.GET})

    public String login(User user) {
        User result = service.login(user);
        if (result != null) {
            return "redirect:/pages/index.html";
        } else {
            return "redirect:/pages/error.html";
        }
    }
}

发送请求:http://localhost:8080/user/login.do?username=zaq&pwd=123,看地址栏变化了,重定向

菜鸟学SpringMVC之——SpringMVC执行原理、向前端返回数据的三种方式

向前端返回数据的方式三:接下来实现转发

转发是有可能携带数据的,那么怎么把数据带入到对应的页面中去,在对应的页面又怎么填充这些数据呢。一般转发都用的是jsp页面,jsp页面展示的时候填充对应的数据。

转发的时候直接返回页面位置就行了

@RequestMapping(value = "/login2.do", method = {RequestMethod.GET})
//    @ResponseBody
    public String login2(User user) {
        User result = service.login(user);
        return "jsp/success.jsp";//这时就会做转发动作
    }

但是要注意配置DispatcherServlet不能过滤它,即配置<url-pattern>*.do</url-pattern>,这时它就不会被视图解析器解析

注意地址栏地址没有改变,所以是转发

菜鸟学SpringMVC之——SpringMVC执行原理、向前端返回数据的三种方式
但是在开发过程中出于某种原因,就是不能设置为只过滤*.do,还是要过滤所有的请求,则直接设置<url-pattern>/</url-pattern>就行了,(注意不能设置为/*,他还是会拦截.jsp文件的)

还要在Spring的配置文件中加入资源解析器。

<!--这里配置了一个 Spring MVC 内置的一个视图解析器,
    该解析器是遵循着一种约定:会在逻辑视图名上添加前缀和后缀,
    进而确定一个 Web 应用中视图资源的物理路径-->
<mvc:view-resolvers>
    <mvc:jsp prefix="/jsp/" suffix=".jsp" view-class="org.springframework.web.servlet.view.JstlView"></mvc:jsp>
</mvc:view-resolvers>
@RequestMapping(value = "/login2.do", method = {RequestMethod.GET})
//    @ResponseBody
    public String login2(User user) {
        User result = service.login(user);
        return "success";//注意这时就不要写物理路径了,只需要写逻辑视图名就行了。
    }

总结:

SpringMVC接收前端传来的数据,直接通过方法的参数直接接收

SpringMVC向前端返回数据的三种方式:

  • 返回字符串:@ResponseBody

  • 重定向:返回字符串,以redirect: 开头

  • 转发:

    • 如果返回的时候不带数据,直接返回字符串,直接写路径。

    • 如果返回的时候要带数据,则返回ModelAndView

          @RequestMapping(value = "/login2.do", method = {RequestMethod.GET})
          public ModelAndView login2(User user) {
              User result = service.login(user);
              ModelAndView mav = new ModelAndView();
              mav.addObject("result",result);//要返回的数据添加到这里,返回几个对象,则添加几个。
              mav.setViewName("success");//这就相当于原来要直接返回的String,即逻辑视图名
              return mav;
          }
      

      在JSP页面中获取

      <body>
          成功登录,这个是一个JSP页面 <%=((User) request.getAttribute("result")).getUsername()%>
      </body>
      </html>
      

部分内容参考:
https://www.iteye.com/blog/elf8848-875830
https://www.jianshu.com/p/91a2d0a1e45a
https://www.cnblogs.com/selene/p/4658554.html

相关标签: Java框架