菜鸟学SpringMVC之——SpringMVC执行原理、向前端返回数据的三种方式
SpringMVC
SpringMVC原理
SpringMVC请求发送到返回请求的全过程
流程:
-
客户端发送一个请求,Tomcat获得这个请求后将其做了一个映射判断(
<url-pattern>/*</url-pattern>
,如果访问地址符合/*,则交给DisPatcherServlet )传给DispatcherServlet(前端控制器) -
DispatcherServlet会根据请求去 HandlerMapping查找Handler(可以根据xml配置、注解@RequestMapping进行查找 )生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet.
-
DispatcherServlet调用HandlerAdaptor执行对应的处理器(Handler/Controller),并将请求传来的参数传给Handler,
-
Handler(后端控制器)通过Service调用数据库找到对应的信息然后返回给Handler
-
Handler将ModelAndView返回给HandlerAdaptor
当Handler在完成逻辑处理后,通常会产生一些信息,这些信息就是需要返回给用户并在浏览器上显示的信息,它们被称为模型(Model)。仅仅返回原始的信息时不够的——这些信息需要以用户友好的方式进行格式化,一般会是 HTML,所以,信息需要发送给一个视图(view),通常会是 JSP。
Handler所做的最后一件事就是将模型数据打包,并且表示出用于渲染输出的视图名(即代码中:
mav.setViewName("index")
的index,就是逻辑视图名)(逻辑视图名)。它接下来会将请求连同ModelAndView发送回HandlerAdaptor。 -
HandlerAdaptor再将ModelAndView返回给DispatcherServlet,
-
但是DispatcherServlet不会处理这个ModelAndView,所以将其传给ViewResolver进行解析,ViewResolver根据逻辑视图名称解析真正的视图 ,并返回给DispatcherServlet。
这样以来,控制器就不会和特定的视图相耦合,传递给 DispatcherServlet 的视图名并不直接表示某个特定的 JSP。(实际上,它甚至不能确定视图就是 JSP)相反,它传递的仅仅是一个逻辑名称,这个名称将会用来查找产生结果的真正视图(给逻辑视图名拼接前缀和后缀, 进而确定一个 Web 应用中视图资源的物理路径 )
-
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() + "";
}
}
所以这里报错的原因就是:
代码中返回一个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() + "";
}
}
也可以返回一个对象。它会将这个对象当作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文件夹
然后修改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
,看地址栏变化了,重定向
向前端返回数据的方式三:接下来实现转发
转发是有可能携带数据的,那么怎么把数据带入到对应的页面中去,在对应的页面又怎么填充这些数据呢。一般转发都用的是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>
,这时它就不会被视图解析器解析
注意地址栏地址没有改变,所以是转发
但是在开发过程中出于某种原因,就是不能设置为只过滤*.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
上一篇: 微服务部署:蓝绿部署、滚动部署、灰度部署(金丝雀部署)
下一篇: 五十三.查询库存列表功能完善