【JAVA】SpringMVC(下)—— SSM整合&异常处理器

发布于:2022-12-18 ⋅ 阅读:(997) ⋅ 点赞:(0)

【REST风格】—— Representational State Transfer

【Spring整合SpringMVC】

【拦截器与过滤器的区别】

【SpringMVC】

【REST风格】—— Representational State Transfer

【概述】

表现形式状态转换

【描述】

传统风格资源描述形式

  • http://localhost/user/ get ById?id=1
  • http://localhost/user/ save User

REST风格描述形式

  • http://localhost/user/1
  • http://localhost/user

【优点】

  • 隐藏资源的访问行为,无法通过地址得知对资源是何种操作
  • 书写简化

【行为区分】

按照REST风格访问资源时使用 行为动作 区分对资源进行了何种操作

  • http://localhost/users  查询全部用户信息                GET(查询)
  • http://localhost/users/1  查询指定用户信息             GET(查询)
  • http://localhost/users  添加用户信息                        POST(新增/保存)
  • http://localhost/users  修改用户信息                        PUT(修改/更新)
  • http://localhost/users/1  删除用户信息                     DELETE(删除)

根据REST风格对资源进行访问称为 RESTful

【注意】

  • 上述行为是约定方式,约定不是规范,可以打破,所以称REST风格,而不是REST规范
  • 描述模块的名称通常使用复数,也就是加s的格式描述,表示此类资源,而非单个资源,例如:users、books、accounts……

【使用】

1、设置http请求动作

@RequestMapping(value = "/users", method = RequestMethod.POST)
@ResponseBody
public String save(@RequestBody User user){
    System.out.println("user save..." + user);
    return "{'module':'user save'}";
}

@RequestMapping(value = "/users" ,method = RequestMethod.PUT)
@ResponseBody
public String update(@RequestBody User user){
    System.out.println("user update..."+user);
    return "{'module':'user update'}";
}

2、设置请求参数(路径变量)

@RequestMapping(value = "/users/{id}" ,method = RequestMethod.DELETE)
@ResponseBody
public String delete(@PathVariable Integer id){
    System.out.println("user delete..." + id);
    return "{'module':'user delete'}";
}

【@RequestMapping】

  • 名称:@RequestMapping
  • 类型: 方法注解
  • 作用:设置当前控制器方法请求访问路径
  • 属性:
    • value(默认):请求访问路径
    • method:http请求动作,标准动作(GET/POST/PUT/DELETE)

例:

@RequestMapping(value = "/users", method = RequestMethod.POST)
@ResponseBody
public String save(@RequestBody User user){
    System.out.println("user save..." + user);
    return "{'module':'user save'}";
}

【@PathVariable】

  • 名称:@PathVariable
  • 类型: 形参注解
  • 作用:绑定路径参数与处理器方法形参间的关系,要求路径参数名与形参名一一对应

例:

@RequestMapping(value = "/users/{id}" ,method = RequestMethod.DELETE)
@ResponseBody
public String delete(@PathVariable Integer id){
    System.out.println("user delete..." + id);
    return "{'module':'user delete'}";
}

【注意】

@ RequestBody、 @ RequestParam、 @ PathVariable的区别

  • @RequestParam用于接收url地址传参或表单传参
  • @RequestBody用于接收json数据
  • @PathVariable用于接收路径参数,使用{参数名称}描述路径参数

应用

  • 开发中,发送请求参数超过1个时,以json格式为主,@RequestBody应用较广
  • 如果发送非json格式数据,选用@RequestParam接收请求参数
  • 采用RESTful进行开发,当参数数量较少时,例如1个,可以采用@PathVariable接收请求路径变量,通常用于传递id值

【标准开发】

【@RestController】

  • 名称:@RestController
  • 类型: 类注解
  • 作用:设置当前控制器类为RESTful风格,等同于@Controller与@ResponseBody两个注解组合功能

例:

@RestController
public class BookController {
}

【@GetMapping、@PostMapping、@PutMapping、@DeleteMapping】

  • 名称:@GetMapping  @PostMapping  @PutMapping  @DeleteMapping
  • 类型: 方法注解
  • 作用:设置当前控制器方法请求访问路径与请求动作,每种对应一个请求动作,例如@GetMapping对应GET请求
  • 属性:value(默认):请求访问路径
@GetMapping("/{id}")
public String getById(@PathVariable Integer id){
    System.out.println("book getById..."+id);
    return "{'module':'book getById'}";
}

【SSM整合】

【整合流程】

1.创建工程

2.SSM整合

  • Spring
    • SpringConfig
  • MyBatis
    • MybatisConfig
    • JdbcConfig
    • jdbc.properties
  • SpringMVC
    • ServletConfig
    • SpringMvcConfig

3.功能模块

  • 表与实体类
  • dao(接口+自动代理)
  • service(接口+实现类)
    • 业务层接口测试(整合JUnit)
  • controller
    • 表现层接口测试(PostMan)

【Spring整合MyBatis】

  • 配置
    • SpringConfig
    • JDBCConfig、jdbc.properties
    • MyBatisConfig
  • 模型
    • Book
  • 数据层标准开发
    • BookDao
  • 业务层标准开发
    • BookService
    • BookServiceImpl
  • 测试接口
    • BookServiceTest
  • 事务处理

【Spring整合SpringMVC】

  • SpringMVC配置类
  • 基于Restful的Controller开发

【表现层数据封装】

1、设置统一数据返回结果类

public class Result {
    private Object data;
    private Integer code;
    private String msg;
}

【注意】

  • Result类中的字段并不是固定的,可以根据需要自行增减
  • 提供若干个构造方法,方便操作

2、设置统一数据返回结果编码

public class Code {
    public static final Integer SAVE_OK = 20011;
    public static final Integer DELETE_OK = 20021;
    public static final Integer UPDATE_OK = 20031;
    public static final Integer GET_OK = 20041;

    public static final Integer SAVE_ERR = 20010;
    public static final Integer DELETE_ERR = 20020;
    public static final Integer UPDATE_ERR = 20030;
    public static final Integer GET_ERR = 20040;
}

【注意】 

Code类的常量设计也不是固定的,可以根据需要自行增减,例如将查询再进行细分为GET_OK,GET_ALL_OK,GET_PAGE_OK

3、根据情况设定合理的Result

@RequestMapping("/books")
public class BookController {
    @Autowired
    private BookService bookService;
    @GetMapping("/{id}")
    public Result getById(@PathVariable Integer id) {
        Book book = bookService.getById(id);
        Integer code = book != null ? Code.GET_OK : Code.GET_ERR;
        String msg = book != null ? "" : "数据查询失败,请重试!";
        return new Result(code,book,msg);
    }
}

【异常处理器】

出现异常现象的常见位置与常见原因:

  • 框架内部抛出的异常:因使用不合规导致
  • 数据层抛出的异常:因外部服务器故障导致(例如:服务器访问超时)
  • 业务层抛出的异常:因业务逻辑书写错误导致(例如:遍历业务书写操作,导致索引异常等)
  • 表现层抛出的异常:因数据收集、校验等规则导致(例如:不匹配的数据类型间导致异常)
  • 工具类抛出的异常:因工具类书写不严谨不够健壮导致(例如:必要释放的连接长期未释放等)

【注意】

  1. 所有的异常均抛出到表现层进行处理
  2. 表现层处理异常,使用AOP思想

【使用】

集中的、统一的处理项目中出现的异常

@RestControllerAdvice
public class ProjectExceptionAdvice {
    @ExceptionHandler(Exception.class)
    public Result doException(Exception ex){
        return new Result(666,null,"异常");
    }
}

【@RestControllerAdvice】

  • 名称:@RestControllerAdvice
  • 类型:类注解
  • 作用:为Rest风格开发的控制器类做增强

【注意】

此注解自带@ResponseBody注解与@Component注解,具备对应的功能

【@ExceptionHandler】

  • 名称:@ExceptionHandler
  • 类型: 方法注解
  • 作用:设置指定异常的处理方案,功能等同于控制器方法,出现异常后终止原始控制器执行,并转入当前方法执行

【注意】

此类方法可以根据处理的异常不同,制作多个方法分别处理对应的异常

【项目异常分类】

  • 业务异常(BusinessException)
    • 规范的用户行为产生的异常
    • 不规范的用户行为操作产生的异常

系统异常(SystemException)

  • 项目运行过程中可预计且无法避免的异常

其他异常(Exception)

  • 编程人员未预期到的异常

【项目异常处理方案】

业务异常(BusinessException)

  • 发送对应消息传递给用户,提醒规范操作

系统异常(SystemException)

  • 发送固定消息传递给用户,安抚用户
  • 发送特定消息给运维人员,提醒维护
  • 记录日志

其他异常(Exception)

  • 发送固定消息传递给用户,安抚用户
  • 发送特定消息给编程人员,提醒维护(纳入预期范围内)
  • 记录日志

【异常处理】

1、自定义项目系统级异常

public class SystemException extends RuntimeException{
    private Integer code;

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public SystemException(Integer code) {
        this.code = code;
    }

    public SystemException(Integer code,String message) {
        super(message);
        this.code = code;
    }

    public SystemException(Integer code, String message, Throwable cause) {
        super(message, cause);
        this.code = code;
    }

    public SystemException(Integer code, Throwable cause) {
        super(cause);
        this.code = code;
    }

    public SystemException(Integer code, String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
        this.code = code;
    }
}

2、自定义项目业务级异常

public class BusinessException extends RuntimeException{
    private Integer code;

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public BusinessException(Integer code) {
        this.code = code;
    }

    public BusinessException(Integer code, String message) {
        super(message);
        this.code = code;
    }

    public BusinessException(Integer code, String message, Throwable cause) {
        super(message, cause);
        this.code = code;
    }

    public BusinessException(Integer code, Throwable cause) {
        super(cause);
        this.code = code;
    }

    public BusinessException(Integer code, String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
        this.code = code;
    }
}

3、自定义异常编码

public class Code {
    public static final Integer SYSTEM_ERR = 50001;
    public static final Integer SYSTEM_TIMEOUT_ERR = 50001;
    public static final Integer SYSTEM_UNKNOW_ERR=59999;

    public static final Integer BUSINESS_ERR = 60002;
}

4、触发自定义异常

public Book getById(Integer id) {
        if(id==1){
            throw new BusinessException(Code.BUSINESS_ERR,"请勿进行非法操作");
        }
        try {
            int i=1/0;
        }catch (Exception ex){
            throw new SystemException(Code.SYSTEM_TIMEOUT_ERR,"服务器访问超时,请稍后再试");
        }
        return bookDao.getById(id);
    }

5、拦截并处理异常

@RestControllerAdvice
public class ProjectExceptionAdvice {
    @ExceptionHandler(SystemException.class)
    public Result doSystemException(SystemException ex) {
        return new Result(ex.getCode(), null, ex.getMessage());
    }

    @ExceptionHandler(BusinessException.class)
    public Result doBusinessException(BusinessException ex) {
        return new Result(ex.getCode(), null, ex.getMessage());
    }

    @ExceptionHandler(Exception.class)
    public Result doException(Exception ex) {
        return new Result(Code.SYSTEM_UNKNOW_ERR, null, "系统异常,请稍后再试");
    }
}

【拦截器】

【概述】

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

【作用】

  • 在指定的方法调用前后执行预先设定的代码
  • 阻止原始方法的执行

【拦截器与过滤器的区别】

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

【使用】

1、声明拦截器的bean,并实现HandlerInterceptor接口(注意:扫描加载bean)

@Component
public class ProjectInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion");
    }
}

2、定义配置类,继承WebMvcConfigurationSupport,实现addInterceptor方法(注意:扫描加载配置)

​@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {

    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
    }
}

​

3、添加拦截器并设定拦截的访问路径,路径可以通过可变参数设置多个

@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
    @Autowired
    private ProjectInterceptor projectInterceptor;

    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");
    }
}

【可使用标准接口WebMvcConfigurer简化开发】(注意:侵入式较强)

@Configuration
@ComponentScan("com.itheima.controller")
@EnableWebMvc
public class SpringMvcConfig implements WebMvcConfigurer{
    @Autowired
    private ProjectInterceptor projectInterceptor;
   
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(projectInterceptor).addPathPatterns("/books", "/books/*");
    }
}

【执行流程】

【拦截器参数】

【前置处理】

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle");
        return true;
    }

【参数】

  • request:请求对象
  • response:响应对象
  • handler:被调用的处理器对象,本质上是一个方法对象,对反射技术中的Method对象进行了再包装

【返回值】

返回值为false,被拦截的处理器将不执行

【后置处理】

public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle");
    }

【参数】

modelAndView:如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行调整

【完成后处理】

public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion");
    }

【参数】

ex:如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理

【多拦截器执行顺序】

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

【注意】

与过滤器链执行类似