【Java框架】SpringMVC(三)——异常处理,拦截器,文件上传,SSM整合

目录

异常处理

  • HandlerExceptionResolver
    • resolveException()
  • 局部异常处理
    • 仅能处理指定Controller中的异常
    • @ExceptionHandler

解释

  • Spring MVC 通过HandlerExceptionResolver 处理程序的异常,包括Handler 映射、数据绑定以及目标方法执行时发生的异常。
  • 主要处理Handler 中用@ExceptionHandler 注解定义的方法。
  • ExceptionHandlerMethodResolver 内部若找不到@ExceptionHandler 注解的话, 会找@ControllerAdvice 类的@ExceptionHandler 注解方法, 这样就相当于一个全局异常处理器。

局部异常处理

java 复制代码
package cn.smbms.controller;

import cn.smbms.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;

/**
 * @author: zjl
 * @datetime: 2024/4/18
 * @desc:
 */
@RestController
public class HelloController {
    @GetMapping(value = "/index")
    public String index(){
        System.out.println(5/0);//模拟异常
        return "index";
    }

    /**
     * 这里处理ArithmeticException.class,NullPointerException.class类型的异常
     * Exception ex: 生成的异常对象,会传递给ex, 通过ex可以得到相关的异常信息,这里程序员可以加入自己的业务逻辑
     * 这里采用ServletAPI入参,HttpServletRequest、HttpServletResponse、HttpSession...都可以直接用
     */
    @ExceptionHandler({ArithmeticException.class,NullPointerException.class})
    public String localException(Exception ex, HttpServletRequest request){
        System.out.println("局部异常信息是-" + ex.getMessage());
        //如何将异常的信息带到下一个页面.
        request.setAttribute("errorInfo", ex.getMessage());
        return "error";
    }
}

全局异常

  • 对所有异常进行统一处理
  • 配置SimpleMappingExceptionResolver
xml 复制代码
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionMappings">
            <props>
                <!--<prop key="java.lang.NullPointerException">null.jsp</prop>
                <prop key="java.lang.ClassNotFoundException">class.jsp</prop>
                <prop key="java.sql.SQLException">sql.jsp</prop>
                <prop key="java.io.IOException">io.jsp</prop>-->
                <prop key="java.lang.RuntimeException">error</prop>
            </props>
        </property>
    </bean>

拦截器

  1. 浏览器发送一个请求会先到Tomcat的web服务器
  2. Tomcat服务器接收到请求以后,会去判断请求的是静态资源还是动态资源
  3. 如果是静态资源,会直接到Tomcat的项目部署目录下去直接访问
  4. 如果是动态资源,就需要交给项目的后台代码进行处理
  5. 在找到具体的方法之前,我们可以去配置过滤器(可以配置多个),按照顺序进行执行
  6. 然后进入到到中央处理器(SpringMVC中的内容),SpringMVC会根据配置的规则进行拦截
  7. 如果满足规则,则进行处理,找到其对应的controller类中的方法进行执行,完成后返回结果
  8. 如果不满足规则,则不进行处理

这个时候,如果我们需要在每个Controller方法执行的前后添加业务,具体该如何来实现?------拦截器

拦截器介绍

拦截器(Interceptor)是一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法的执行

作用:

  • 在指定的方法调用前后执行预先设定的代码
  • 阻止原始方法的执行
  • 总结:拦截器就是用来做增强

拦截器和过滤器之间的区别

  • 归属不同:Filter属于Servlet技术,Interceptor属于SpringMVC技术
  • 拦截内容不同:Filter对所有访问进行增强,Interceptor仅针对SpringMVC的访问进行增强

拦截器执行流程

代码实现

自定义拦截器类

java 复制代码
package cn.smbms.interceptor;

import cn.smbms.tools.Constants;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author: zjl
 * @datetime: 2024/4/12
 * @desc:
 */
public class SysInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Object obj = request.getSession().getAttribute(Constants.USER_SESSION);
        if(obj == null){
            //未登录,重定向到401.jsp
            response.sendRedirect(request.getContextPath() + "/401.jsp");
            return false;
        }
        return true;
    }
}

配置拦截规则

xml 复制代码
    <!-- 配置拦截器interceptors -->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/><!-- 配置拦截规则 -->
            <mvc:exclude-mapping path="/login.do"/><!-- 配置不拦截规则 -->
            <mvc:exclude-mapping path="/logout"/><!-- 配置不拦截规则 -->
            <bean class="cn.smbms.interceptor.SysInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

补充

  • 当配置多个拦截器时,形成拦截器链
  • 拦截器链的运行顺序参照拦截器添加顺序为准
  • 当拦截器中出现对原始处理器的拦截,后面的拦截器均终止运行
  • 当拦截器运行中断,仅运行配置在前面的拦截器的afterCompletion操作

preHandle:与配置顺序相同,必定运行

postHandle:与配置顺序相反,可能不运行

afterCompletion:与配置顺序相反,可能不运行。

文件上传

  • MultipartResolver接口
    • 用于处理上传请求,将上传请求包装成可以直接获取文件的数据,方便操作
  • 两个实现类
    • StandardServletMultipartResolver
      • 使用了Servlet3.0标准的上传方式
    • CommonsMultipartResolver
      • 使用了Apache的commons-fileupload来完成具体的上传操作

依赖

xml 复制代码
	<!-- 文件上传 -->
	<dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.8.1</version>
    </dependency>
    <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.6</version>
    </dependency>
    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.4</version>
    </dependency>

配置MultipartResolver

xml 复制代码
    <!-- 配置MultipartResolver,用于上传文件,使用spring的CommonsMultipartResolver -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize" value="5000000"/><!-- 上传文件大小上限,单位为字节-- >
        <property name="defaultEncoding" value="UTF-8"/><!-- 请求的编码格式,默认为ISO-8859-1,此处设置为UTF-8 -->
    </bean>

编写文件上传表单页

  • method="POST"
  • enctype="multipart/form-data"
    • 指定表单内容类型,支持文件上传
  • <input type="file" name="a_idPicPath"/>
    • 用来上传文件的file组件

API

MultipartFile

  • Spring MVC会将上传文件绑定到MultipartFile对象中,并通过该对象完成文件的上传操作

File.separator

  • Windows、Linux自适应路径分隔符
  • 低入侵性的代码实现

必须对上传文件进行重命名

原因

  • 原文件名存在中文 乱码问题
  • 原文件名与服务器上已有文件重名 覆盖问题

重新定义上传文件名(保证不重名)

  • 当前系统时间+随机数+"_Personal.jpg"

文件上传成功后,须更新数据库字段(idPicPath),记录上传文件的存储路径

代码示例

java 复制代码
	//文件上传
	@RequestMapping(value="/useraddsave.html",method=RequestMethod.POST)
	public String addUserSave(User user,HttpSession session,HttpServletRequest request,
							 @RequestParam(value ="a_idPicPath", required = false) MultipartFile attach){
		String idPicPath = null;
		//判断文件是否为空
		if(!attach.isEmpty()){
			String path = request.getSession().getServletContext().getRealPath("statics"+File.separator+"uploadfiles"); 
			logger.info("uploadFile path ============== > "+path);
			String oldFileName = attach.getOriginalFilename();//原文件名
			logger.info("uploadFile oldFileName ============== > "+oldFileName);
			String prefix=FilenameUtils.getExtension(oldFileName);//原文件后缀     
	        logger.debug("uploadFile prefix============> " + prefix);
			int filesize = 500000;
			logger.debug("uploadFile size============> " + attach.getSize());
	        if(attach.getSize() >  filesize){//上传大小不得超过 500k
            	request.setAttribute("uploadFileError", " * 上传大小不得超过 500k");
	        	return "useradd";
            }else if(prefix.equalsIgnoreCase("jpg") || prefix.equalsIgnoreCase("png") 
            		|| prefix.equalsIgnoreCase("jpeg") || prefix.equalsIgnoreCase("pneg")){//上传图片格式不正确
            	String fileName = System.currentTimeMillis()+RandomUtils.nextInt(1000000)+"_Personal.jpg";  
                logger.debug("new fileName======== " + attach.getName());
                File targetFile = new File(path, fileName);  
                if(!targetFile.exists()){  
                    targetFile.mkdirs();  
                }  
                //保存  
                try {  
                	attach.transferTo(targetFile);  
                } catch (Exception e) {  
                    e.printStackTrace();  
                    request.setAttribute("uploadFileError", " * 上传失败!");
                    return "useradd";
                }  
                idPicPath = path+File.separator+fileName;
            }else{
            	request.setAttribute("uploadFileError", " * 上传图片格式不正确");
            	return "useradd";
            }
		}
		user.setCreatedBy(((User)session.getAttribute(Constants.USER_SESSION)).getId());
		user.setCreationDate(new Date());
		user.setIdPicPath(idPicPath);
		if(userService.add(user)){
			return "redirect:/user/userlist.html";
		}
		return "useradd";
	}

省略service、mapper、sql代码

SpringMVC文件上传流程

多文件上传

html 复制代码
<label for="a_idPicPath">证件照:</label>
<input type="file" name="attachs" id="a_idPicPath"/>
<label for="a_workPicPath">工作证照片:</label>
<input type="file" name="attachs" id="a_workPicPath"/>
  • 控制器-处理方法addUserSave()
    • 修改入参MultipartFile为数组
      • MultipartFile[] attachs
  • MultipartFile[] 数组内存放文件对象顺序
    • 按照form表单的file标签的顺序进行存储
  • 分别更新字段:idPicPath、workPicPath
  • 若入参对象MultipartFile为数组时,该参数前面必须要加上@RequestParam注解,否则会报错
java 复制代码
@RequestParam(value="a_idPicPath",required=false) 		    							MultipartFile idPicFile,
@RequestParam(value="a_workPicPath",required=false)
                                            MultipartFile workPicFile)

静态资源加载

1.将静态资源统一放在一个路径下

2.配置静态资源加载

xml 复制代码
    <mvc:resources location="/statics/" mapping="/statics/**"></mvc:resources>

SSM整合

  • 1.使用IDEA创建SSM的Maven工程
  • 2.在main目录中创建出来java和resources目录
  • 3.修改pom.xml
    • 3.1导入SSM使用的依赖
    • 3.2导入resources配置
    • 3.3对maven工程重新构建(reload project)
  • 4.创建package目录结构,基本目录命名规则:com.项目名简称
    实体类:entity或pojo
    请求层:controller
    业务层:service
    数据访问层:mapper
    工具类包:utils或tools
    配置类包:config
    过滤器包:filter
    拦截器包:interceptor
    ...
  1. 导入SSM使用的配置文件,放在resources目录下
  • 5.1spring配置文件:applicationContext.xml【修改扫描的包路径】
  • 5.2springMvc配置文件:springm-servlet.xml【修改扫描的包路径,去掉没用的配置】
  • 5.3创建mybatis目录,导入mybatis配置文件:mybatis-config.xml【修改实体类别名的包名】
  • 5.4在mybatis目录下创建mapper目录
  • 5.5将写SQL的mapper.xml导入进来,修改为对应实体类的mapper.xml【修改namespace的路径】
  • 5.6数据库配置文件:database.properties【修改数据库连接信息】
  • 5.7日志配置文件:log4j.properties
  • 5.8web.xml配置文件:修改启动页"welcome-file"、引入spring、springmvc、过滤器的配置
  • 5.9注意:修改配置文件中的自定义配置(包名、路径名等)
  • 6.编写java代码
    pojo------controller------service------mapper------mapper.xml
  • 7.编写前端页面
    • 将前端页面统一放在一个目录下(jsp),以便配置视图解析器,在请求层可以直接使用逻辑视图名。
  • 8.编写/导入静态资源
    • 图片、音频、视频、样式、js、插件等等
    • 注意:将静态资源统一放在webapp/statics下
    • 若没有statics目录需要手动创建。
  • 9.在springm-servlet.xml中配置静态资源访问,否则无法使用静态资源
  • 10.在编写完请求层、业务逻辑层、以及SQL后,可以进行功能测试
相关推荐
神仙别闹3 分钟前
基于C#和Sql Server 2008实现的(WinForm)订单生成系统
开发语言·c#
XINGTECODE4 分钟前
海盗王集成网关和商城服务端功能golang版
开发语言·后端·golang
天天扭码9 分钟前
五天SpringCloud计划——DAY2之单体架构和微服务架构的选择和转换原则
java·spring cloud·微服务·架构
程序猿进阶10 分钟前
堆外内存泄露排查经历
java·jvm·后端·面试·性能优化·oom·内存泄露
FIN技术铺14 分钟前
Spring Boot框架Starter组件整理
java·spring boot·后端
zwjapple20 分钟前
typescript里面正则的使用
开发语言·javascript·正则表达式
小五Five22 分钟前
TypeScript项目中Axios的封装
开发语言·前端·javascript
小曲程序22 分钟前
vue3 封装request请求
java·前端·typescript·vue
前端每日三省24 分钟前
面试题-TS(八):什么是装饰器(decorators)?如何在 TypeScript 中使用它们?
开发语言·前端·javascript
余生H34 分钟前
transformer.js(三):底层架构及性能优化指南
javascript·深度学习·架构·transformer