1、SpringMVC文件上传
1.1 文件上传jar包准备
与Servlet、Struts2文件上传一样,我们需要准备上传jar包
<!--文件上传依赖的两个jar包-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
1.2 springmvc.xml配置
SpringMVC中文件上传使用的是CommonsMultipartResolver,所以我们需要将该类注入到Spring容器
<!--注意:ID不能改变,是固定值,原因是Spring容器直接查找该固定的ID将其加入容器-->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--上传单个文件的最大值:字节,如果为-1,则表示无限制 -->
<property name="maxUploadSize" value="17367648787"></property>
<!-- 上传文件的编码 -->
<property name="defaultEncoding" value="UTF-8"></property>
</bean>
1.3 JSP表单提交
需要注意的依旧是enctype=“multipart/form-data”
<form action="/springmvc06/file/upload" method="post" enctype="multipart/form-data">
文件:
<input type="file" name="file">
<br>
描述
<input type="text" name="desc" value="">
<button name="btn">文件上传</button>
</form>
1.4 Controller对应的处理
@Controller
@RequestMapping("/file")
public class FileController {
@RequestMapping("upload")
public String upload(@RequestParam String desc ,
@RequestParam("file") MultipartFile file){
String storePath = "F:\\work\\testjava";
String fileName = file.getOriginalFilename();
File filePath = new File(storePath , fileName);
if (!filePath.getParentFile().exists()) {
// 如果目录不存在,则创建目录
filePath.getParentFile().mkdirs();
}
try {
file.transferTo(filePath);
} catch (IOException e) {
e.printStackTrace();
}
return "success";
}
}
1.5 大小限制测试
2、SpringMVC文件下载
@RequestMapping("download")
public ResponseEntity<byte[]> download() throws IOException {
//文件获取,替换成数据库查询出的路径
String fileName = "张三.png";
String filePath = "F:\\work\\testjava";
File file = new File(filePath , fileName);
//文件乱码解决
String dfileName = new String(fileName.getBytes("utf-8"), "iso8859-1");
//请求头设置
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDispositionFormData("attachment", fileName);
//将文件转化为字节数组
byte[] bytes = FileUtils.readFileToByteArray(file);
return new ResponseEntity<byte[]>(bytes , headers, HttpStatus.CREATED);
}
2.1 JSP
<a href="/springmvc06/file/download">下载</a>
3、异常处理
在开发中,不管是dao层、service层还是controller层,都有可能抛出异常,在springmvc中,能将所有类型的异常处理从各处理过程解耦出来,既保证了相关处理过程的功能较单一,也实现了异常信息的统一处理和维护。
3.1 处理流程
3.2 传统的异常处理
在开发中,不管是dao层、service层还是controller层,都有可能抛出异常,在springmvc中,能将所有类型的异常处理从各处理过程解耦出来,既保证了相关处理过程的功能较单一,也实现了异常信息的统一处理和维护。
@Repository
public class UserDao {
public Map<String,Object> getUser(){
return null;
}
}
@Service
public class UserService {
@Autowired
private UserDao userDao;
public Map<String,Object> getUser() throws Exception {
//大量的代码片段
try {
System.out.println(10/0);
} catch (Exception e) {
e.printStackTrace();
throw new Exception("查询用户订单信息失败");
}
return null;
}
}
@Controller
@RequestMapping("user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("getUser")
public ModelAndView getUser() {
ModelAndView mav = new ModelAndView("user");
Map<String, Object> user = null;
try {
user = userService.getUser();
mav.addObject("user" , user);
mav.addObject("success" , true);
} catch (Exception e) {
e.printStackTrace();
mav.addObject("success" , false);
mav.addObject("errMsg" , e.getMessage());
}
return mav;
}
}
4、异常处理实现
在SpringMVC中,所有的异常处理类,都实现了HandlerExceptionResolver接口,该接口为异常顶级接口,其下的每个实现类,都是每种异常处理的实现方式,如:ExceptionHandlerExceptionResolver、SimpleMappingExceptionResolver等。若需要自定义全局的异常处理器,需要实现该接口。
ExceptionHandlerExceptionResolver:接口主要提供了,@ExceptionHandler
4.1 @ExceptionHandler
统一处理某一类异常,从而能够减少代码重复率和复杂度。@ExceptionHandler修饰的方法参数必须是异常类型(Throwable或其子类),不能包含其他类型参数
(1)单异常捕获
/**
* 出现算术异常
* @return
*/
@RequestMapping("arithmetic")
public String arithmetic(){
System.out.println(1/0);
return "success";
}
@ExceptionHandler({ArithmeticException.class})
public ModelAndView handlerException(ArithmeticException e){
System.out.println(e + ":异常信息打印");
ModelAndView mav = new ModelAndView("error");
mav.addObject("errorObj", e);
return mav;
}
(2)多个异常捕获
若存在多个@ExceptionHandler,其捕获规则仍然和异常的范围大小进行,最大的范围为Exception,与多catch类似
/**
* 出现算术异常
* @return
*/
@RequestMapping("arithmetic")
public String arithmetic(){
System.out.println(1/0);
return "success";
}
/**
* 出现下标越界异常
* @return
*/
@RequestMapping("arrayIndexOut")
public String download() {
int[] is = new int[2];
System.out.println(is[3]);
return "success";
}
/**
* 该方法可以捕获当前类中的多个异常
*/
@ExceptionHandler({ArithmeticException.class , ArrayIndexOutOfBoundsException.class})
public ModelAndView handlerException(Exception e){
System.out.println(e + ":异常信息打印");
ModelAndView mav = new ModelAndView("error");
mav.addObject("errorObj", e);
return mav;
}
(3)修饰方法的返回值类型
- ModelAndView对象
- Model对象
- Map对象
- View对象
- String对象
- 还有@ResponseBody、HttpEntity<?>或ResponseEntity<?>,以及void
(4)缺点
- 代码有侵入性,处理情况复杂
- 进行异常处理的方法必须与出错的方法在同一个Controller里面
4.2 @ControllerAdvice
该注解为控制器增强,修饰对象包括类、接口和枚举等,在运行时有效,并且可以通过Spring扫描为bean组件。其可以包含由@ExceptionHandler、@InitBinder 和@ModelAttribute标注的方法,可以处理多个Controller类,这样所有控制器的异常可以在一个地方进行处理,被用作全局的异常捕捉。
ExceptionHandlerMethodResolver如果在当前Handler中找不到@ExceptionHandler方法来处理当前方法出现的异常, 则将去@ControllerAdvice标记的类中查找@ExceptionHandler标记的方法来处理异常。
用法
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ModelAndView handleException(Exception ex){
System.out.println("异常:"+ ex);
ModelAndView mv = new ModelAndView("error");
mv.addObject("exception",ex);
return mv;
}
}
全局异常案例
(1)自定义运行异常
public class CustomException extends RuntimeException{
private String errCode;
private String errMsg;
public CustomException(String errMsg, String errCode) {
this.errCode = errCode;
this.errMsg = errMsg;
}
}
(2)定义全局的异常处理类
package com.hliedu.controller.exception;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理所有自定义的业务异常
* @param e
* @return
*/
@ExceptionHandler(CustomException.class)
public ModelAndView customExceptionHnadler(CustomException e){
ModelAndView mav = new ModelAndView("custom_error");
mav.addObject("errCode",e.getErrCode());
mav.addObject("errMsg",e.getErrMsg());
return mav;
}
/**
* 处理所有未知的异常
* @param exception
* @return
*/
@ExceptionHandler(Exception.class)
public ModelAndView allExceptionHandler(Exception exception){
ModelAndView mav = new ModelAndView("all_error");
mav.addObject("errMsg", "this is Exception.class");
return mav;
}
}
(3)模拟异常控制器
@Controller
@RequestMapping("exception")
public class ExceptionController {
@RequestMapping("all")
public String all(){
//将会被GlobalExceptionHandler.allExceptionHandler方法捕获
System.out.println(10/0);
return "success";
}
@RequestMapping("custom")
public String custom(){
if(true){
//将会被GlobalExceptionHandler.customExceptionHnadler方法捕获
throw new CustomException("异常" , "-1");
}
return "success";
}
}
(4)拓展
- @ExceptionHandler和@ControllerAdvice能够集中异常,使异常处理与业务逻辑分离
- 除了返回ModelAndView界面,还可以以@ResponseBody的形式响应给客户端
- @ControllerAdvice支持界面、异步响应的方式
- @RestControllerAdvice为REST变种异常增强,可参考@RestController
5、拦截器
拦截器是指访问某个Handler或Handler的方法之前或之后实施拦截,它提供了一种机制可以使开发者可以定义在一个Handler执行的前后执行的代码,也可以在一个Handler执行前阻止其执行。同时也是提供了一种可以提取Handler中可重用的部分代码的方式。 并且拦截器是可插拔的,拦截器是面向切面(AOP)的一种体现,如CGLIB正是采用的拦截器的做法实现。
拦截器栈(Interceptor Satck):拦截器栈就是将拦截器按一定的顺序连接成一条链。在访问被拦截的方法或字段时,拦截器栈中的拦截器就会按其之前定义的顺序被调用,栈结构为先进后出
SpringMVC也可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定的功能,自定义的拦截器必须实现HandlerInterceptor接口
5.1 HandlerInterceptor接口描述
boolean preHandle(request, response, Object handler) throws Exception;
void postHandle(request,response,Object handler,modelAndView) throws Exception;
void afterCompletion(request,response,Object handler,Exception ex)throws Exception;
- preHandle():在业务处理器处理请求之前被调用。预处理,可以进行编码、安全控制、权限校验等处理。需要开发人员提供一个boolean类型的返回值。
- 返回true:表示继续执行其他拦截器或handler方法
- 返回false:表示中断执行,后续等待的拦截器或handler方法不再执行
- postHandle():在业务处理器处理请求执行完成后,生成视图之前执行(页面未渲染)。
- afterCompletion():在DispatcherServlet完全处理完请求后被调用,可用于清理资源等。
5.2 拦截器配置
<mvc:interceptors>
<!--该拦截器,拦截所有的请求-->
<bean id="allInterceptor" class="com.hliedu.interceptor.AllInterceptor"/>
<!--该拦截器拦截指定mapping的请求-->
<mvc:interceptor>
<!--需要拦截的地址,通配符形式-->
<mvc:mapping path="/web/**"/>
<!--不执行拦截某个地址-->
<mvc:exclude-mapping path="/web/login"/>
<bean id="webInterceptor" class="com.hliedu.interceptor.WebInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
6、拦截器的使用
6.1 定义拦截器
java package com.hliedu.test.interceptor;
import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("在handler处理器之前调用");
//返回值如果是true,表示继续执行拦截器或handler处理方法
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
//渲染:界面数据绑定,界面的产生
request.setAttribute("inter" ,"inter");
System.out.println("在handler处理方法之后,渲染界面之前执行");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("所有功能都执行完成之后执行");
}
}
6.2 定义handler方法
java package com.hliedu.test.controller;
import com.hliedu.test.exception.CustomException;
import com.hliedu.test.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import java.util.Map;
@Controller
@RequestMapping("inter")
public class InterceptController {
/**
* MyInterceptor拦截器将对该方法进行拦截
* @return
*/
@RequestMapping("test1")
public String test1() {
System.out.println("test1方法执行");
return "success";
}
/**
* 该方法在springmvc.xml中配置的不拦截
* @return
*/
@RequestMapping("test2")
public String test2() {
System.out.println("test2方法执行");
return "success";
}
}
6.3 配置拦截器
<!--声明拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--拦截指定的映射路径-->
<mvc:mapping path="/inter/**"/>
<!--排除拦截路径-->
<mvc:exclude-mapping path="/inter/test2"/>
<bean class="com.hliedu.test.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
7、拦截器登录功能实现
需求:用户需要登录后才能进入用户中心,否则将直接回到登录界面
关键点:session
7.1 登录界面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录界面</title>
</head>
<body>
<h2>登录界面</h2>
<a href="/springmvc06/user/login">点击登录</a>
</body>
</html>
7.2 用户中心界面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>用户中心</title>
</head>
<body>
<h2>用户中心</h2>
</body>
</html>
7.3 登录与用户中心handler
package com.hliedu.test.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;
import javax.servlet.http.HttpServletRequest;
@Controller
@RequestMapping("user")
@SessionAttributes("USER_LOGIN")
public class LoginController {
@RequestMapping("login")
public String login(ModelMap modelMap) {
modelMap.addAttribute("USER_LOGIN" , true);
System.out.println("用户登录");
return "user/center";
}
@RequestMapping("center")
public String center() {
System.out.println("进入用户中心");
return "user/center";
}
}
7.4 登录拦截器
java package com.hliedu.test.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
Object user_login = session.getAttribute("USER_LOGIN");
System.out.println(user_login);
if(user_login == null){
System.out.println("未登录");
request.getRequestDispatcher("/user/login").forward(request,response);
return false;
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
//渲染:界面数据绑定,界面的产生
System.out.println("在handler处理方法之后,渲染界面之前执行");
}
} ```