[TOC]

springMVC

在controller中获取web元素

我们这里所说的web是:HttpServletRequest,HttpServletResponse,HttpSession,ServletContext 目前我们使用Controller中没有传入这些元素,其实spring不太建议使用这些元素,提供了专门的API。 当然也提供了非常简单的获取这些元素的方式,
如果我们希望在Handler中使用request,response,session这三个元素,直接作为形参即可。

}
}
return “web-element”;//逻辑视图
14
15
16
17
10

  1. //这里的request和response同样的可以做重定向和转发
  2. //request.getRequestDispatcher(“WEB-INF/jsp/web- element.jsp”).forward(request,response);
  3. //如果在这里手动的做了重定向或者转发,则直接return即可。 当前的方法就不需要返

回值。
@RequestMapping
public String getWebElement(HttpServletRequest request, HttpServletResponse response, HttpSession session) throws ServletException, IOException {
request.setAttribute(“reqAttr”,”request的属性值”);
session.setAttribute(“sessAttr”,”session 的 属 性 值 “); session.getServletContext().setAttribute(“appAttr”,”application的属
性值”);
7
8
9

  1. @Controller
  2. @RequestMapping(“/web”)
  3. public class WebElementController { 4

5
6

handler的各种不同的返回值处理

问题

我们这里说的handler就是controller中的有@RequestMapping注解的方法。已经知道的知识点:

  1. 如果注解了@ResponseBody,那么返回的任何内容都会以字符串形式输出到客户端。

如果返回的不是字符,而是对象,则自动转换为json格式输出。当然需要对应的jackson包和开启MVC 注解驱动。

  1. 没有@ResponseBody注解。 返回字符串,这是字符串就是视图。

springMVC的视图解析会自动判断是转发还是重定向。 还有哪些类型没说?
void?? 没有@ResponseBody,但是返回了一个对象??一个集合???

ModelAndView

模型和视图 这里的模型指的是数据模型。 这里的模型就是数据对象。所以,其实可以翻译为:数据和视图
案例: 我们要查询所有学生的列表,并且展示在JSP中:

@RequestMapping(“/queryAll”) public ModelAndView queryAll(){
ModelAndView modelAndView = new ModelAndView();
//查询所有学生
List students = studentService.queryAll();
//request.setAttribute(“students”,students); modelAndView.addObject(“students”,students);
//request.getRequestDispatcher(“WEB-INF/jsp/student- list.jps”).fxxxxx
modelAndView.setViewName(“student-list”); return modelAndView;
}
}
15
16
17
18

  1. @Controller
  2. @RequestMapping(“/student”)
  3. public class StudentController {
  4. @Autowired
  5. private StudentService studentService; 6

7
8
9
10
11
12
13
14

说明:

//2 如果传入的是一个对象,则request的属性的key是 对象类型的类名首字母小写。
addAllObjects(传入一个Map);// 自己思考一下。
List st studentList
//
10
11
12

  1. ModelAndView对象包含了数据和视图信息。
  2. 并非数据和视图都是必须的,都是非必须。
  3. 添加数据的方式有多种:
  4. [1]创建对象的同时传入视图的名称和数据
  5. new ModelAndView(视图名称,数据属性名称,数据对象);
  6. [2]创建好对象之后,在使用addXXXAPI添加数据
  7. addObject(“attrName”,data);
  8. addObject(data);
  9. //1 如果传入的是一个List或者Set,则request属性的key是集合泛型类型的类名的首字母小写—+List,比如

返回逻辑使用,使用Model绑定数据

我们正常的返回一个逻辑视图,但是需要将数据放在request的作用域中。
上一小节,使用了ModelAndView。如果返回一个逻辑视图,可有其他的操作。 我们使用Model(这是我最常用的方式)

}
model.addAttribute(“students”,students);
return “student-list”;
这里的model是作为形参传递的
//给model添加属性
@RequestMapping(“/queryAll”)
public String queryAll(Model model){
List students = studentService.queryAll();
1
2
3
4
5
6
7

说明:

spring自动会将这些数据放在request的作用域。
model对象不是自己创建的,是spring创建并且传递进来的。
model有4个对应的添加属性的API:
Model addAttribute(String attributeName, @Nullable Object attributeValue); Model addAttribute(Object attributeValue);
Model addAllAttributes(Collection<?> attributeValues);
Model addAllAttributes(Map attributes);
1
2
3
4
5
6
7
8

返回void

当一个handler方法返回void的时候,也同样会经过视图解析器进行转发。
void没有任何值,默认会直接直接转发到当前handler的url:

1 /*
2
@author 张双虎
3 */

  1. @Controller
  2. public class TeacherController {
  3. @Autowired
  4. private TeacherService teacherService;
  5. @RequestMapping(“/teacher-lst”)
  6. public void teacherList(Model model){ 10

model.addAttribute(“teachers”,teacherService.queryAllByLimit(0,100));

  1. //没有返回值,则转发到当前的handler对应的url
  2. //也就相当于这里返回了 return “teacher-list” 其实就是进入 WEB- INF/jsp/teacher-list.jsp

13 }
14 }

有同学会感觉是在循环的跳转。

  1. 你请求的url : /teacher-list 这个url不会经过视图解析器
  2. 处理完成之后,转发的url teacher-list 但是这个url会经过视图解析器。—> WEB- INF/jsp/teacher-list.jsp

返回值是Map或者ModelMap

我们使用void的时候,如果要在request的作用域中设置数据。我们要使用Model。其实我们可以直接返回ModelMap或者Model
案例:

  1. //没有传递参数Model
  2. //返回值不是void
  3. @RequestMapping(“/teacher-list”)
  4. public ModelMap teacherList(){
  5. ModelMap modelMap = new ModelMap();
  6. //ModelMap添加属性的API
  7. modelMap.addAttribute(“teachers”,teacherService.queryAllByLimit(0,5));
  8. return modelMap;
  9. //我们没有指定视图,这时转发的效果和void一致。
  10. //转发到当前的handler的url 11 }

说明:

  1. ModelMap本身就是集成了LinkedHashMap,所以可以直接使用Map的API添加数据。
  2. 当然ModelMap也有自己的API,它的API和上面的Model的API一模一样。

既然ModelMap是继承了LinkedHashMap,我们可以考虑直接使用HashMap:

  1. //没有传递参数Model
  2. //返回值不是void
  3. @RequestMapping(“/teacher-list”)
  4. public Map teacherList(){
  5. Map map = new HashMap();
  6. //ModelMap添加属性的API
  7. map.put(“teachers”,teacherService.queryAllByLimit(0,5));
  8. return map;
  9. //我们没有指定视图,这时转发的效果和void一致。
  10. //转发到当前的handler的url 11 }

经过测试,会发现,直接使用HashMap,也是一样的。

返回Collection集合

上面的案例中,都是可以同时在request的作用域中设置多个属性值的。
有时,我可能就只是查询一个列表,只要放一个就可以的,这时可以考虑直接放回一个列表
(colleaction)
当我们返回一个collection的时候,转发的情况和void一模一样。
①直接返回List集合

  1. //没有传递参数Model
  2. //返回值不是List
  3. @RequestMapping(“/teacher-list”)
  4. public List teacherList(){
  5. return teacherService.queryAllByLimit(0,100);
  6. //我们没有指定视图,这时转发的效果和void一致。
  7. //转发到当前的handler的url 8 }

我们返回集合,spring会将这个集合直接设置到request的作用域中。没有指定request中属性的名字。 默认有个名字 集合泛型类型的类名的首字母小写+List

springMVC - 图1
②直接返回set

  1. //没有传递参数Model
  2. //返回值不是List
  3. @RequestMapping(“/teacher-list”)
  4. public Set teacherList(){
  5. return new HashSet<>(teacherService.queryAllByLimit(0,100));
  6. //我们没有指定视图,这时转发的效果和void一致。
  7. //转发到当前的handler的url 8 }

tips:当使用set的时候,默认的key已然是 xxxList 而不是 xxxSet

返回一个对象

返回一个对象,并且没有注解@ResponseBody 转发情况和void一致。
并且spring会将这个对象设置到request的作用域,key 自己思考。

  1. @RequestMapping(“/teacher-detail”)
  2. public Teacher queryById(long teacherId){
  3. return teacherService.queryById(teacherId); 4 }

JSP中获取属性值:
springMVC - 图2

高级参数绑定

路径中的正则表达式

restFul风格:
springMVC - 图3
RESTFUL特点包括: 1、每一个URI代表1种资源; 2、客户端使用GET、POST、PUT、DELETE4个表示操作方式的动词对服务端资源进行操作:GET用来获取资源,POST用来新建资源(也可以用于更新资 源),PUT用来更新资源,DELETE用来删除资源; 3、通过操作资源的表现形式来操作资源; 4、资源的表现形式是XML或者HTML; 5、客户端与服务端之间的交互在请求之间是无状态的,从客户端到服务端的每个请求都必须包含理解请求所必需的信息。

传统的url restFul
POST请求:userSave?id=xx POST: user/save/id/name
GET请求: queryById?id=9527 GET: query/9527
POST请求: update?id=9527&.. PUT: update/9527/kakaxi
GET请求: delete?id=9527 DELETE: delete/9527

昨天已经讲过了在路径中携带参数。
今天来看看路径中携带一个正则表达式的参数:

1 /*
2
@author 张双虎
3 */

  1. @Controller
  2. public class RestFullController {
  3. //路径中的额参数是一个正则表达式
  4. @RequestMapping(“/update/{userBirth:\d{4}-\d{2}-\d{2}}/birth”)
  5. @ResponseBody
  6. public String updateBirth(@PathVariable(“userBirth”) String birth){
  7. System.out.println(birth);
  8. return “传递的参数是:”+birth; 12 }

13 }

springMVC - 图4只要符合这个正则表达式要求的,都可以进入这个handler。

高级参数绑定

  1. 绑定一个数组

当我们提交的数据是个数组的时候,就可以使用数字直接接受。

  1. LOL
  2. DNF
  3. CF
  4. DOTA
  5. WOW 7

8
9


10

后端接受:

public String paramBindArray(int [] enjoy){ return Arrays.toString(enjoy);
}
//使用数组接收
@ResponseBody
@RequestMapping(“/param-bind-array”)
1
2
3
4
5

  1. 复杂类型的请求参数

  2. 学科名称:
  3. 学科描述:
  4. 学科名称:
  5. 学科描述:
  6. 学科名称:
  7. 学科描述:

14


15 16


17

从表单中可以看出,提交的数据中有一个subjects集合,这个集合的泛型就是Subject,集合中的每个对 象都有属性subjectname和subjectdesc。
绑定这种类型的数据,我们需要定义一个VO对象。

  1. POJO(Plain Ordinary Java Object)简单的Java对象。实体类。
  2. DTO 数据传输对象(DTO)(Data Transfer Object)。
  3. VO(View Object) 也是用来传输数据的,但是主要使用在View层。

我们定义一个SubjectVO。

1 /*
2
@author 张双虎
3 */

  1. public class SubjectVO {
  2. //当然也可以有其他的属性
  3. //必须有一个集合
  4. private List subjects;
  5. public List getSubjects() {
  6. return subjects; 10 }
  7. public void setSubjects(List subjects) {
  8. this.subjects = subjects; 13 }

14 }

我们就可以是用这个VO对象来接收这些复杂参数:

public SubjectVO paramBindVo(SubjectVO subjectVO){ return subjectVO;
}
//使用VO对象接收
@ResponseBody
@RequestMapping(“/param-bind-vo”)
1
2
3
4
5

springMVC - 图5测试:

springMVC的文件上传和下载

文件上传

springMVC是使用commons-fileupload完成文件的上传的。 前端还是跟之前一致的。

引入对应的依赖:

  1. commons-fileupload
  2. commons-fileupload
  3. 1.4

在springMVC的配置文件中添加相关配置:

—>




1
2

3
4
5
6

①必须配置
②id必须multipartResolver。后端处理:

1 @PostMapping(“/upload”)

2
3
@ResponseBody
//使用MultipartFile接收文件
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public String upload(@RequestParam(“head”) MultipartFile head, HttpSession session) throws IOException {
//获取文件名
String fileName = head.getOriginalFilename();
//获取指向临时文件的输入流
InputStream in = head.getInputStream();
//这里就常规的文件拷贝
String realPath = session.getServletContext().getRealPath(“/“); File f = new File(realPath+”temp\“+fileName);
OutputStream out = new FileOutputStream(f); int len = -1;
byte [] buff = new byte[1024]; while((len = in.read(buff))!=-1){
out.write(buff,0,len); out.flush();
};
in.close();
out.close(); return “ok”;
}

我们可以使用CommonsMultipartFile接收文件,API会更加简单。

  1. @RequestMapping(“/upload”)
  2. @ResponseBody //使用MultipartFile接收文件
  3. public String upload(@RequestParam(“head”) CommonsMultipartFile head, HttpSession session) throws Exception {
  4. //获取文件名
  5. String fileName = head.getOriginalFilename();
  6. //这里就常规的文件拷贝
  7. String realPath = session.getServletContext().getRealPath(“/“);
  8. File f = new File(realPath+”temp\“+fileName);
  9. //直接可以使用对应的api拷贝文件
  10. if(!head.getFileItem().isFormField()){//不是表单域,我就开始拷贝
  11. head.getFileItem().write(f); 12 }

13 return “ok”; 14 }

tips:我们使用CommonsMultipartFile接收文件时,要指定表单域的名字。
如果有多个文件,可以写多个参数。如果是一组同名的文件,也可以使用数组接收。

文件下载

完全可以直接使用response得到输出流,直接将文件输出即可 ,跟spring没有关系。
spring也提供了一些解决方案。
我们可以将返回值设置为ResponseEntity。

  1. @RequestMapping(“/download”)
  2. public ResponseEntity downLoad(HttpSession session) throws IOException {
  3. String realPath = session.getServletContext().getRealPath(“/“);
  4. File f = new File(realPath+”temp\msyql数据库隔离界别的弊端.png”);
  1. FileInputStream in = new FileInputStream(f);
  2. //准备和文件长度一致的缓冲区
  3. byte [] buff = new byte[in.available()];
  4. //一次性将文件数据读取到缓冲区中
  5. in.read(buff);
  6. //准备一个MultiValueMap 在其中设置header
  7. MultiValueMap map = new LinkedMultiValueMap();
  8. map.put(“content-disposition”,new ArrayList(Arrays.asList(new String[]{“attachment;filename=msyql数据库隔离界别的弊端.png”})));
  9. //创建ResponseEntity 传入参数(数据,headermap,状态码)
  10. ResponseEntity responseEntity = new ResponseEntity (buff,map, HttpStatus.OK);
  11. return responseEntity; 16 }

这个方法有点弊端:文件会一次性被读入内容。所以,我们还是建议直接使用response。
1 /*
2
@author 张双虎
3 */

  1. @WebServlet(“/download”)
  2. public class DownLoadServlet extends HttpServlet {
  3. @Override
  4. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  5. this.doPost(req,resp); 9 }
  6. @Override
  7. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  8. String serverPath = this.getServletContext().getRealPath(“/“);
  9. //设置响应头
  10. response.setContentType(“image/jpeg”);
  11. // attachment表示内容是一个附件, filename配置文件的名称
  12. response.addHeader(“content- disposition”,”attachment;filename=car123.jpg”);
  13. //通过response获取输出流(这个输出流就是指向客户端的)
  14. ServletOutputStream outputStream = response.getOutputStream();
  15. //读取文件
  16. FileInputStream inputStream = new FileInputStream(serverPath+”upload\car.jpg”);
  17. //文件拷贝的流程
  18. int len = -1;
  19. byte [] buff = new byte[1024];
  20. while((len = inputStream.read(buff))!=-1){
  21. outputStream.write(buff,0,len); 26 }

27 inputStream.close(); 28 }
29 }
30

spring对A JAX的支持

返回json格式的数据:略。接收json格式的数据:
前端提交json格式的数据:

  1. 后端接受:

    }
    public String doAjax(@RequestBody Subject subject){ System.out.println(subject);
    return “ok”;
    }
    //接受json格式的数据,要注解@RequestBody
    @ResponseBody
    /*
    @author 张双虎
    */ @Controller
    public class AjaxController {
    @RequestMapping(“/doAjax”)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12