接上篇:SpirngMVC框架--学习笔记(上):https://blog.csdn.net/a745233700/article/details/81038382
17、全局异常处理:
系统中异常包括两类,预期异常和运行时异常RuntimeException,前者通过捕获异常从而获得异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。
系统的dao、service、controller出现异常都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理,如下图:
springmvc提供全局异常处理器(一个系统只有一个异常处理器)进行统一异常处理。
(1)自定义异常类:
对不同的异常类型定义异常类,继承Exception
//自定义异常:
//针对预期的异常,需要在此类中抛出此类的异常
public class CustomException extends Exception{
private String message;
public CustomException(String message)
{
super(message);
this.message=message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
(2)全局异常处理器:实现HandlerExceptionResolver接口
思路:
系统遇到异常,在程序中手动抛出,dao抛给service、service给controller、controller抛给前端控制器,前端控制器调用全局异常处理器。
全局控制器处理思路:
①解析出异常类型;
②如果该异常类型是系统自定义的异常,直接取出异常信息,在错误页面展示。
③如果该异常不是系统自定义的异常,构造一个自定义的异常类型(信息为“未知错误”)
//自定义异常:
//针对预期的异常,需要在此类中抛出此类的异常
public class CustomException extends Exception{
private String message;
public CustomException(String message)
{
super(message);
this.message=message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
在springmvc文件中注册全局异常处理器:
如果与业务功能相关的异常,建议在service中抛出异常。
与业务功能没有关系的异常,建议在controller中抛出。
18、上传图片:
(1)创建图片虚拟目录:
①第一种:
②第二种:
也可以直接修改tomcat的配置:在conf/server.xml文件,添加虚拟目录:
注意:在图片虚拟目录中,一定将图片目录分级创建(提高I/O性能),一般我们采用按日期进行分级创建。
(2)加入上传图片的jar包:
(3)在页面的form表单中加入enctype="multipart/form-data"
(4)在springmvc.xml文件中配置解析器:
5242880
(5)编写Controller:
//MultipartFile items_pic用于接收图片
@RequestMapping(value="/editItemsSubmit",method={RequestMethod.POST,RequestMethod.GET})
public String editItemsSubmit(Model model,HttpServletRequest request,Integer id,
@ModelAttribute("itemsCustom") @Validated(value={ValidGroup1.class}) ItemsCustom itemsCustom,
BindingResult bindingResult,MultipartFile items_pic) throws Exception
{
if(bindingResult.hasErrors())
{
List allErrors=bindingResult.getAllErrors();
//自定义一个list接受自己编码后的提示字符串,在把自己定义的list传到界面,
//这样就解决了把乱码传到界面的问题了
List listErrors=new ArrayList<>();
for(ObjectError objectError:allErrors)
{
//System.out.println(objectError.getDefaultMessage());
//把返回错误的提示再次编码
String strError=new String(objectError.getDefaultMessage().getBytes("ISO-8859-1"),"UTF-8");
listErrors.add(strError);//把编码好的错误提示信息加自己定义好list集合里面去
}
model.addAttribute("allErrors", listErrors);
return "Items/editItems";
}
//原始名称
String originalFilename=items_pic.getOriginalFilename();
System.out.println(originalFilename);
//上传图片:
if(items_pic!=null && originalFilename!=null && originalFilename.length()>0)
{
//存储图片的物理路径:
String pic_path="D:\\Tomcat 5.5\\pictures\\";
//新的图片名称
String newFileName=UUID.randomUUID()+originalFilename.substring(originalFilename.lastIndexOf("."));
//新图片:
File newFile=new File(pic_path+newFileName);
//将内存中的数据写入磁盘
items_pic.transferTo(newFile);
//将新图片名称写到itemsCustom中
itemsCustom.setPic(newFileName);
}
//调用service更新商品信息,页面需要将商品信息传到此方法中
itemsService.updateItems(id, itemsCustom);
//页面转发:
return "forward:queryItems.action";
}
(6)页面显示:
19、json交互:
@RequestBody:将json串转成java对象
@ResponseBody:将java对象转成json输出。
(1)请求是Json串,输出也是json串。
(2)请求是key/value,输出是json(常用)
最终结果都是输出json数据,为了在前端页面方便对请求结果进行解析。
步骤:
第一步:导入jar包依赖:
第二步:配置json转化器:
在注解适配器中加入messageConverters
注意:如果使用
第三步:页面和控制器:
20、springmvc对RESTful的支持:
(1)对url进行规范,写RESTful格式的url
(2)http的方法规范 :
不管是删除、添加、更新。。使用的url是一致的,如果进行删除,需要设置http的方法为delete,同理添加。
后台controller方法:判断http方法,如果是delete执行删除,如果是post执行添加。
(3)对http的contentType规范:
请求是指定contentType,要json数据,设置成json格式的type..
需求:查询商品信息,返回json数据。
思路:定义方法,进行url映射使用RESTful风格的url,将查询商品的信息的id传入controller。输出json是,使用@ResponseBody将java对象输出json。
(1)Controller:
//根据id查找商品信息,使用RESTful风格,并返回json格式的数据
//("/itemView/{id}")的id要和@PathVariable("id")的id一致,表示将("/itemView/{id}")的id绑定到@PathVariable后面的参数上面
@RequestMapping("/itemView/{id}")
public @ResponseBody ItemsCustom itemView(@PathVariable("id") Integer id) throws Exception
{
ItemsCustom itemsCustom=itemsService.findItemsById(id);
return itemsCustom;
}
(2)前端控制器配置:
springmvc_restful
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:/spring/springmvc.xml
springmvc_restful
/
(3)访问的url:
(4)json结果数据:
--对静态资源的解析:
在springmvc.xml中添加静态资源的解析:
对静态资源的访问:
21、springmvc拦截器:
(1)定义拦截器:实现HandlerInterceptor接口
public class HandlerInterceptor1 implements HandlerInterceptor{
//进入Handler方法之前
//用于身份认证,身份授权
//比如身份认证,如果认证不通过表示当前用户没有登陆,需要此方法拦截不再向下执行
@Override
public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
//return false:表示拦截,不向下执行
//return ture:表示放行
System.out.println("HandlerInterceptor1...preHandle");
return true;
}
//进去Handler方法之后,返回modelAndView之前执行
//应用场景:将公用的模型数据(比如菜单导航)传到视图,也可以在这里统一制定视图
@Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception {
System.out.println("HandlerInterceptor1...postHandle");
}
//执行Handler完成执行此方法
//应用场景:统一异常处理,统一日志处理
@Override
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {
System.out.println("HandlerInterceptor1...afterCompletion");
}
}
(2)配置拦截器:
①针对HandlerMapping进行拦截配置:(一般不用)
如果在某个HandlerMapping中配置,经过该HandlerMapping映射成功的Handler最终使用该拦截器。
②类似全局的拦截器:
springmvc配置类似全局的拦截器,springmvc框架将配置的类似全局的拦截器注入到每个HandlerMapping中。
(3)测试:
①两个拦截器都放行:
总结:preHandle方法按顺序执行;postHandle和afterCompletion按拦截器配置的逆向顺序执行。
②拦截器1放行,拦截器2不放行:
总结:拦截器1放行,拦截器2的preHandle才会执行;
拦截器2的preHandle不放行,拦截器2的postHandle和afterCompletion不会执行;
只要有一个拦截器不放行,postHandle不会执行。
③拦截器1不放行,拦截器2不放行:
总结:拦截器1的preHandle不放行,postHandler和afterCompletion不会执行。
拦截器1的preHandle不放行,拦截器2不执行。
(4)拦截器小结:
根据测试结果,对拦截器应用。
比如:统一日志处理拦截器,需要该拦截器的preHandle一定要放行,且将它放在拦截器链中第一个位置。
比如:登陆认证拦截器,放在拦截器链中第一个位置;权限校验拦截器,放在登陆认证拦截器之后。(因为登陆通过之后才检验权限)
(5)拦截器应用:
需求:
①用户请求url
②拦截器进行拦截校验:
--如果请求的url是公开地址(无需登陆即可访问的url),让放行。
--如果用户session不存在,跳转到登陆页面
--如果用户session存在,放行,继续操作。
登陆页面:
拦截器:
public class LoginInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
//获取请求的url
String url=request.getRequestURI();
//判断url是否是公开地址(实际使用时,将公开地址配置在配置文件中)
//这里公开地址是登陆提交的地址
if(url.indexOf("login.action")>=0){
//如果进行登陆提交,放行
return true;
}
//判断session
HttpSession session=request.getSession();
//从session中取出用户身份信息:
String username=(String) session.getAttribute("username");
if(username!=null)
{ //身份存在,放行
return true;
}
//执行到这里,表示用户身份需要认证,跳转到登陆界面
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object arg2, ModelAndView arg3) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object arg2, Exception arg3)
throws Exception {
}
}
注册拦截器:
编写Controller:
@Controller
public class LoginController {
@RequestMapping("/login")
public String login(HttpSession sesison,String username,String password) throws Exception{
//调用service进行身份验证
//...
//保存用户身份信息
sesison.setAttribute("username", username);
//重定向到商品列表
return "redirect:items/queryItems.action";
}
@RequestMapping("/logout")
public String logout(HttpSession sesison) throws Exception{
//清除session
sesison.invalidate();
//重定向商品列表
return "redirect:items/queryItems.action";
}
}
登陆成功页面: