SpringMVC: Second

张天宇 on 2020-04-21

SpringMVC第二篇,响应数据、结果视图、文件上传、异常处理。

响应数据和结果视图

返回值分类

1. 字符串

controller方法返回字符串可以指定逻辑视图名,通过视图解析器解析为物理视图地址。

1
2
3
4
5
@RequestMapping("/testReturnString")
public String testReturnString() {
System.out.println("AccountController 的 testReturnString 方法执行了。。。。");
return "success";
}
2. Void

如果控制器的方法返回值编写成void,默认会跳转到@RequestMapping(value="/initUpdate") initUpdate.jsp的页面。

可以使用请求转发或者重定向跳转到指定的页面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RequestMapping(value="/initAdd")
public void initAdd(HttpServletRequest request,HttpServletResponse response) throws Exception {
System.out.println("请求转发或者重定向");
// 请求转发
// request.getRequestDispatcher("/WEB-INF/pages/add.jsp").forward(request,
response);
// 重定向,因为重定向属于新的请求,无法前往WEB-INF文件夹
// response.sendRedirect(request.getContextPath()+"/add2.jsp");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
// 直接响应数据
response.getWriter().print("你好");
return;
}
3. ModelAndView
1
2
3
4
5
6
7
8
@RequestMapping("/testReturnModelAndView")
public ModelAndView testReturnModelAndView() {
ModelAndView mv = new ModelAndView();
mv.addObject("username", "张三");
//或者User user,mv.addObject("user",user);
mv.setViewName("success");
return mv;
}
1
2
3
4
${username}
${requestScope.username}
<!-- ${user.username}
${requestScope.user.username} -->
4. 转发和重定向

无法使用视图解析器,所以路径要写好。

1
2
3
4
5
6
7
8
9
// "forward:转发的JSP路径",不走视图解析器了,所以需要编写完整的路径
public String delete() throws Exception {
System.out.println("delete方法执行了...");
return "forward:/WEB-INF/pages/success.jsp";
}
public String count() throws Exception {
System.out.println("count方法执行了...");
return "redirect:/add.jsp";
}

ResponseBody响应Json数据

静态资源解除过滤
1
2
3
4
5
6
7
8
<!-- 在springmvc.xml配置文件添加如下配置 -->
<!-- 设置静态资源不过滤
location元素表示webapp目录下的包下的所有文件
mapping元素表示以/static开头的所有请求路径,如/static/a 或者/static/a/b
-->
<mvc:resources location="/css/" mapping="/css/**"/> <!-- 样式 -->
<mvc:resources location="/images/" mapping="/images/**"/> <!-- 图片 -->
<mvc:resources location="/js/" mapping="/js/**"/> <!-- javascript -->
@RequestBody获取请求体数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script src="js/jquery.min.js"></script>
<script>
$(function(){
$("#btn").click(function(){
// alert("sdsds");
$.ajax({
url:"user/testJson",
contentType:"application/json;charset=UTF-8",
data:'{"username":"hehe","password":123456,"age",30}',
dataType:"json",
type:"post",
success:function(data){
alert(data);
alert(data.username);
}
});
});
});
</script>
1
2
3
4
5
@RequestMapping("/testJson")
public void testJson(@RequestBody String body){
System.out.println(body);
}
//{"username":"hehe","password":123456,"age",30}
@RequestBodyjson的字符串转换成JavaBean对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency>

只要传进来的json字段属性与实体类一致,可以直接配置方法。

1
2
3
4
@RequestMapping("/testJson")
public void testJson(@RequestBody User user){
System.out.println(user);
}
@ResponseBody把JavaBean对象转换成json字符串,直接响应
1
2
3
4
5
6
7
8
@RequestMapping("/testJson")
public @ResponseBody User testJson(@RequestBody User user){
System.out.println(user);
user.setUsername("laji");
user.setAge(40);
return user;
}
//这时,script中的success的alert就会执行了

文件上传

传统方式

  1. 坐标导入

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.1</version>
    </dependency>
    <dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.4</version>
    </dependency>
  2. 前端页面

    1
    2
    3
    4
    <form action="file/fileupload1" method="post" enctype="multipart/form-data">
    <input type="file" name="upload" />
    <input type="submit" value="上传" />
    </form>
  3. 后端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    @RequestMapping("/fileupload1")
    public String fileupload1(HttpServletRequest request) throws Exception {
    System.out.println("文件上传");
    // 使用fileupload完成文件上传
    // 上传位置
    String path = request.getSession().getServletContext().getRealPath("/uploads/");
    File file = new File(path);
    if (!file.exists())
    file.mkdirs();
    // 解析request对象,获取上传文件项
    DiskFileItemFactory factory = new DiskFileItemFactory();
    ServletFileUpload upload = new ServletFileUpload(factory);
    List<FileItem> items = upload.parseRequest(request);
    for (FileItem item : items) {
    // 进行判断,当前item是否是上传文件项
    if (item.isFormField()) {
    // 普通表单项
    } else {
    // 上传文件项
    // 获取到上传文件名称
    String filename = item.getName();
    // 文件名设唯一值
    String uuid = UUID.randomUUID().toString().replace("-", "");
    filename = uuid + "_" + filename;
    // 完成文件上传
    item.write(new File(path, filename));
    // 删除临时文件
    item.delete();
    }
    }
    return "success";
    }
  4. 拦截器

    1
    2
    3
    4
    5
    6
    7
    8
    <!-- 配置文件上传解析器 id 的值是固定的-->
    <bean id="multipartResolver"
    class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!-- 设置上传文件的最大尺寸为 5MB -->
    <property name="maxUploadSize">
    <value>5242880</value>
    </property>
    </bean>

跨服务器上传

在实际开发中,我们会有很多处理不同功能的服务器。例如:

  • 应用服务器:负责部署我们的应用
  • 数据库服务器:运行我们的数据库
  • 缓存和消息服务器:负责处理大并发访问的缓存和消息
  • 文件服务器:负责存储用户上传文件的服务器
  1. 导包

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <dependency>
    <groupId>com.sun.jersey</groupId>
    <artifactId>jersey-core</artifactId>
    <version>1.18.1</version>
    </dependency>
    <dependency>
    <groupId>com.sun.jersey</groupId>
    <artifactId>jersey-client</artifactId>
    <version>1.18.1</version>
    </dependency>
  2. 前端

    1
    2
    3
    4
    <form action="user/fileupload3" method="post" enctype="multipart/form-data">
    选择文件:<input type="file" name="upload"/><br/>
    <input type="submit" value="上传文件"/>
    </form>
  3. 控制器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    @RequestMapping(value="/fileupload3")
    public String fileupload3(MultipartFile upload) throws Exception {
    System.out.println("SpringMVC跨服务器方式的文件上传...");
    // 定义图片服务器的请求路径
    String path = "http://localhost:9090/day02_springmvc5_02image/uploads/";
    // 获取到上传文件的名称
    String filename = upload.getOriginalFilename();
    String uuid = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
    // 把文件的名称唯一化
    filename = uuid+"_"+filename;
    // 向图片服务器上传文件
    // 创建客户端对象
    Client client = Client.create();
    // 连接图片服务器
    WebResource webResource = client.resource(path+filename);
    // 上传文件
    webResource.put(upload.getBytes());
    return "success";
    }

异常处理

思路

Controller调用service,service调用dao,异常都是向上抛出的,最终有DispatcherServlet找异常处理器进
行异常的处理,最终使前端返回一个友好的界面。

步骤

  1. 自定义异常类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class SysException extends Exception{
    private static final long serialVersionUID = 4055945147128016300L;
    // 异常提示信息
    private String message;
    public String getMessage() {
    return message;
    }
    public void setMessage(String message) {
    this.message = message;
    }
    public SysException(String message) {
    this.message = message;
    }
    }
  2. 自定义异常处理器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public class SysExceptionResolver implements HandlerExceptionResolver{
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
    ex.printStackTrace();
    SysException e = null;
    // 获取到异常对象
    if(ex instanceof SysException) {
    e = (SysException) ex;
    }else {
    e = new SysException("请联系管理员");
    }
    ModelAndView mv = new ModelAndView();
    // 存入错误的提示信息
    mv.addObject("message", e.getMessage());
    // 跳转的Jsp页面
    mv.setViewName("error");
    return mv;
    }
    }
  3. 配置异常处理器

    1
    2
    <!-- 配置异常处理器 -->
    <bean id="sysExceptionResolver" class="top.tyzhang.exception.SysExceptionResolver"/>

拦截器

概述

  1. SpringMVC框架中的拦截器用于对处理器进行预处理和后处理的技术。

  2. 可以定义拦截器链,连接器链就是将拦截器按着一定的顺序结成一条链,在访问被拦截的方法时,拦截器链中的拦截器会按着定义的顺序执行。

  3. 拦截器和过滤器的功能比较类似,有区别

    1. 过滤器是Servlet规范的一部分,任何框架都可以使用过滤器技术。
    2. 拦截器是SpringMVC框架独有的。
    3. 过滤器配置了/*,可以拦截任何资源。
    4. 拦截器只会对控制器中的方法进行拦截。
  4. 拦截器也是AOP思想的一种实现方式

  5. 想要自定义拦截器,需要实现HandlerInterceptor接口。

步骤

  1. 创建类,实现HandlerInterceptor接口,重写需要的方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class MyInterceptor1 implements HandlerInterceptor{
    /**
    * controller方法执行前,进行拦截的方法
    * return true放行
    * return false拦截
    * 可以使用转发或者重定向直接跳转到指定的页面。
    */
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    System.out.println("拦截器执行了...");
    return true;
    }
    }
  2. 在springmvc.xml中配置拦截器类。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <!-- 配置拦截器 -->
    <mvc:interceptors>
    <mvc:interceptor>
    <!-- 哪些方法进行拦截 -->
    <mvc:mapping path="/user/*"/>
    <!-- 哪些方法不进行拦截
    <mvc:exclude-mapping path=""/>
    -->
    <!-- 注册拦截器对象 -->
    <bean class="cn.itcast.demo1.MyInterceptor1"/>
    </mvc:interceptor>
    </mvc:interceptors>

HandlerInterceptor接口中的方法

  1. preHandle方法是controller方法执行前拦截的方法
    1. 可以使用request或者response跳转到指定的页面
    2. return true放行,执行下一个拦截器,如果没有拦截器,执行controller中的方法。
    3. return false不放行,不会执行controller中的方法。
  2. postHandle是controller方法执行后执行的方法,在JSP视图执行前。
    1. 可以使用request或者response跳转到指定的页面
    2. 如果指定了跳转的页面,那么controller方法跳转的页面将不会显示。
  3. aftercompletion方法是在JSP执行后执行
    1. request或者response不能再跳转页面了

用户登录拦截器

功能
  1. 有一个登录页面,需要写一个 controller 访问页面。
  2. 登录页面有一提交表单的动作,需要在 controller 中处理。
    1. 判断用户名密码是否正确。
    2. 如果正确向 session 中写入用户信息。
    3. 返回登录成功。
  3. 拦截用户请求,判断用户是否登录。
    1. 如果用户已经登录,放行。
    2. 如果用户未登录,跳转到登录页面。
代码
  1. 控制器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //登陆页面
    @RequestMapping("/login")
    public String login(Model model)throws Exception{
    return "login";
    }
    //登陆提交
    //userid:用户账号,pwd:密码
    @RequestMapping("/loginsubmit")
    public String loginsubmit(HttpSession session,String userid,String pwd)throws Exception{
    //向 session 记录用户身份信息
    session.setAttribute("activeUser", userid);
    return "redirect:/main.jsp";
    }
  2. 拦截器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public class LoginInterceptor implements HandlerInterceptor{
    @Override
    Public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    //如果是登录页面则放行
    if(request.getRequestURI().indexOf("login.action")>=0){
    return true;
    }
    HttpSession session = request.getSession();
    //如果用户已登录也放行
    if(session.getAttribute("user")!=null){
    return true;
    }
    //用户没有登录挑战到登录页面
    request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,response);
    return false;
    }
    }