上周公司让我面了个32岁的Java程序员,各方面都挺好,问啥都会,对于Spring问题,答得还行,最后问了个亿级流量Spring的实战题,就答不上来了,走时几乎落泪...唉!
小编这里有一套Spring面试宝典,可助你拿到高薪
揭示内幕,深入浅出:笔者对Spring的源码进行了彻底分析,深刻揭示了Spring框架的技术内幕,让读者知其然,更知其所以然。Spring 中的许多设计经验、技巧、模式具有很高的借鉴性,在透彻学习Spring 体系结构的同时,读者可以直接将这些方法借用到具体的应用开发中。
同步更新,与时俱进:虽然在2013年12月就发布Spring 4.0的第一个候选版本,后来又发布了多个RC版本,并最终于2015年8月发布了Spring 4.2 的正式版本,但新功能的添加及旧功能的调整从来就没有停止过。
突出重点,淡化边缘:虽然全书篇幅达800 多页,但本书没有片面追求内容的面面俱到,相反,我们特别注意内容的剪裁和取舍:对于实用性强的知识点深入分析、深度挖掘,而对于不常用的知识点则点到为止,甚至不纳入本书的范围。
理论透彻,面向实践:本书在透彻分析原理、讲解技术知识点的同时,特别注意与实际应用的结合
代码简洁,图例丰富:全书代码在排版布局及内容剪裁上颇费心思,实例代码重点关注当前知识点涉及的内容,弱化边缘代码,并采用特殊的排版方式适时添加简明扼要的注释,方便程序代码的阅读和重点内容的把握。
相关知识,网打尽: Spring 不但本身涉及众多Java 技术,其集成的第三方技术本身也涵盖了丰富的知识。
面试文档领取方式
关注我然后发送 666 就可以领取啦
当 http 请求被自定义的 controller 处理时,如何指定响应的页面呢?
这个就是我们本文需要讨论的问题。
在 controller 中响应页面有很多种方式,稍后我们会一一介绍,大家需要掌握每种方式的用法以及这些方式之间的区别,以后能够灵活使用。
本文用到的页面都以 jsp 为例,其他页面模板技术,比如 freemarker、velocity、thymeleaf、enjoy,这些我们后面专门再开篇讲解。
SpringMVC 底层是依靠 servlet 来实现的,所以我们先回顾下 servlet 中响应页面是如何实现的。
servlet 中响应页面有 2 种常见的方式,而 springmvc 中通常也是依靠这 2 种方式实现的。
request.getRequestDispatcher(path).forward(request,response);
1、path 为转向的地址
2、发生在服务器端,浏览器的地址栏不会发生变化
3、path 指定的页面,可以共享 request 请求中的数据
4、path 必须是服务器端的资源
response.sendRedirect(location);
1、location 为重定向的地址
2、重定向发生在客户端(浏览器端),所以会导致浏览器地址栏发生变化,变为 location 指定的地址
3、重定向会导致浏览器重新向服务器端发生一次请求,请求地址为 location 指定的地址
4、location 可以为本服务器端的资源,也可以为外网可以访问的任意资源,比如:http://www.baidu.com
下面来详解 springmvc 中响应页面的 5 种方式。
通过 springmvc 实现用户列表功能,如下图
我们先来看一下如果用 servlet 是如何实现的,代码如下:
1、List userList = new ArrayList();
2、request.setAttribute("userList",userList);
3、request.getRequestDispatcher("/WEB-INF/view/user/list.jsp").forward(request,response);
关键代码就这几行,相当简单。
对应的 jsp(/WEB-INF/view/user/list.jsp)关键代码如下,一个循环遍历用户列表 userList
id
name
age
${user.id}
${user.name}
${user.age}
@Controller
public class UserController {
/**
* 用户列表(用户id->用户信息)
*/
Map userDtoMap = new ConcurrentHashMap<>();
{
userDtoMap.put(1L, new UserDto(1L, "路人", 30));
userDtoMap.put(2L, new UserDto(2L, "张三", 20));
userDtoMap.put(3L, new UserDto(3L, "李四", 18));
}
/**
* 用户列表
*
* @return
*/
@RequestMapping("/user/list.do")
public ModelAndView list() {
//1.创建ModelAndView
ModelAndView modelAndView = new ModelAndView();
//2.将所有用户信息放到Model中
modelAndView.addObject("userList", userDtoMap.values());
//3.设置显示的页面
modelAndView.setViewName("/WEB-INF/view/user/list.jsp");
//4.返回ModelAndView
return modelAndView;
}
}
这里主要看 list()这个方法,当调用这个方法的时候,效果和上面 servlet 的效果一样,这里用到了ModelAndView。
通常我们的页面都是动态的,客户端看到的页面,基本上都是模板(视图)+数据(数据模型),经过组装之后输出到客户端的。
所以响应客户端的请求,需要指定 2 个关键的信息:页面、页面中需要的数据。
springmvc 中使用 ModelAndView 来存放这 2 个信息,通过modelAndView.addObject方法添加页面中用到的数据,通过modelAndView.setViewName("视图名称")来设置显示的页面。
添加页面中需要用到的数据,效果同:request.setAttribute("key","value");
指定需要显示的视图命名,比如 jsp 地址
如果页面中需要用到一些动态的数据,此时可以使用 ModelAndView 作为返回值,将动态数据放到 ModelAndView 中。
当页面不需要用到后端的数据的时候,就只是显示一个页面,此时可以直接将视图的名称作为返回值就可以了,比如
/**
* 跳转到新增页面
*
* @return
*/
@RequestMapping("/user/add.do")
public String add() {
//直接返回视图的名称(页面的路径)
return "/WEB-INF/view/user/add.jsp";
}
大家看下上面 2 种方式,返回的视图名称,都以/WEB-INF/view/开头,以.jsp结尾对不对。
如果项目中我们规定所有的视图都符合这种规则,即都放在/WEB-INF/view/目录中,都是 jsp 文件,那么我们可以将视图的名称是不是可以简化一下,怎么做的呢?
具体 2 个步骤。
在 springmvc 配置文件中添加下面配置,来指定视图解析器。
这个 bean 会对视图的名称进行处理,有 2 个参数需要指定
prefix:视图文件前缀
suffix:视图文件后缀
最终视图的名称 = prefix+controller 中指定的 viewname+suffix
viewName 旧值viewName 新值/WEB-INF/view/user/add.jspuser/add/WEB-INF/view/user/list.jspuser/list
@RequestMapping("/user/add.do")
public String add() {
//直接返回视图的名称(页面的路径)
return "user/add";
}
此时代码是不是简单多了。
有时候,请求之后,需要做重定向操作,比如发送删除用户信息的请求/user/del/{用户id}.do,后端处理成功之后,需重定向到用户列表页面/user/list.do
这里就需要用到重定向的操作了,在 servlet 的中对应代码是
response.sendRedirect(location);
springmvc 中有好几种实现,这里我们主要掌握 2 种。
springmvc 中实现重定向比较简单,视图的名称必须需要以redirect:开头,比如下面代码,处理删除用户的请求,删除成功之后,重定向到用户列表页面
/**
* 删除用户信息,删除成功之后重定向到用户列表页
*
* @param userId 用户id
* @return
*/
@GetMapping("/user/del/{userId}.do")
public String del(@PathVariable("userId") Long userId, HttpServletRequest request) {
//删除用户信息
this.userDtoMap.remove(userId);
//重定向到用户列表页面,此时浏览器地址会发生变化,变为http://localhost:8080/chat05/user/list.do
return "redirect:/user/list.do";
}
如果重定向的时候,我们需要向重定向的页面携带参数,一般我们可以这么做,代码如下:
return "redirect:/user/list.do?在这里拼参数";
比如
return "redirect:/user/list.do?p1=v1&p2=v2";
如果遇到了这种请求,参数比较少的情况,按照上面拼接是可以的。
springmvc 中提供了更简单的方式,代码如下,最终 springmv 会指定将 ModelAndView 中添加的数据,拼接到重定向的 url 中
@GetMapping("/user/del1/{userId}.do")
public ModelAndView del1(@PathVariable("userId") Long userId) {
//删除用户记录
this.userDtoMap.remove(userId);
/**
* 重定向到用户列表页面,此时浏览器地址会发生变化,
* 变为http://localhost:8080/chat05/user/list.do?p1=v1&p2=v2
*/
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("p1", "v1");
modelAndView.addObject("p2", "v2");
modelAndView.setViewName("redirect:/user/list.do");
return modelAndView;
}
https://gitee.com/javacode2018/springmvc-series
案例中实现了用户信息的增删改查,用到了上面讲到的所有技术。
http://localhost:8080/chat05/user/list.do
http://localhost:8080/chat05/user/add.do
删除用户信息之后,会被重定向到用户列表页,案例中列出了 2 种删除,用来模拟 2 种重定向的效果。