接上篇《微服务实战spring cloud alibaba(五)在项目中使用nacos的配置中心》。之前我们项目已经搭建好了,注册中心,配置中心我们都有使用到,这样子其实微服务已经前进了一小步了。这时候我们来写个接口。在GoodsContoller.java里面添加一个接口:
@RequestMapping("/good/price/{goodId}") public BaseResponse getPrice(@PathVariable("goodId")Integer goodId) { String price = null; return BaseResponse.success(new BigDecimal(price).setScale(2, BigDecimal.ROUND_DOWN)); }
然后我么请求一下:http://localhost:9002/good/price/2,是不是报错500了,服务器端有具体的错误信息输出:
大家想象一下,在真实的业务中,如果我们的接口报错,没有具体的错误处理,那这样子好吗?发生错误的话,又怎么来处理了? 这就带来今天的主题-如何处理全局异常。
首先了解下全局异常是什么?
像上面的异常,我们在程序里面可以处理下,例如try-catch捕获一下,出现异常,我们就直接给错误提示,例如:
@RequestMapping("/good/price/{goodId}") public BaseResponse getPrice(@PathVariable("goodId")Integer goodId) { try { String price = null; return BaseResponse.success(new BigDecimal(price).setScale(2, BigDecimal.ROUND_DOWN)); } catch (Exception e) { log.error(e.getMessage(),e); return BaseResponse.fail(HttpResponseStatus.SERVERERROR); } }
这时候我们再请求:http://localhost:9002/good/price/2,给我们返回了正确的json,不再是报httpstatus 500的错误了。
这样子前端就能知道是什么样的错误,可以直接给用户提示而不是再自定义。但是我们再想,程序中经常会出现各种错误,我们是不是每一个方法都要手动去try-catch呢? 有没有办法做一个全局的异常,出现异常就自动调用呢?其实这个描述就是全局异常的由来。
二、如何实现全局异常?
要实现全局异常,就要基于spring mvc的 ControllerAdvice和ExceptionHandler。
@ControllerAdvice注解是Spring3.2中新增的注解,学名是Controller增强器,作用是给Controller控制器添加统一的操作或处理。 对于@ControllerAdvice,我们比较熟知的用法是结合@ExceptionHandler用于全局异常的处理,但其作用不止于此。ControllerAdvice拆开来就是Controller Advice,关于Advice,在Spring的AOP中,是用来封装一个切面所有属性的,包括切入点和需要织入的切面逻辑。这里ControllerAdvice也可以这么理解,其抽象级别应该是用于对Controller进行切面环绕的,而具体的业务织入方式则是通过结合其他的注解来实现的。@ControllerAdvice是在类上声明的注解,其用法主要有三点: 1.结合方法型注解@ExceptionHandler,用于捕获Controller中抛出的指定类型的异常,从而达到不同类型的异常区别处理的目的。 2.结合方法型注解@InitBinder,用于request中自定义参数解析方式进行注册,从而达到自定义指定格式参数的目的。 3.结合方法型注解@ModelAttribute,表示其注解的方法将会在目标Controller方法执行之前执行。 从上面的讲解可以看出,@ControllerAdvice的用法基本是将其声明在某个bean上,然后在该bean的方法上使用其他的注解来指定不同的织入逻辑。**不过这里@ControllerAdvice并不是使用AOP的方式来织入业务逻辑的,而是Spring内置对其各个逻辑的织入方式进行了内置支持**
三、全局异常如何实现?
3.1、创建一个common项目
3.2、创建一个GloabalException.java类
package com.umall.common.error; import javax.servlet.http.HttpServletRequest; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import com.umall.common.enu.HttpResponseStatus; import com.umall.common.model.BaseResponse; import lombok.extern.slf4j.Slf4j; @RestController @ControllerAdvice @Slf4j public class GloabalException { @ExceptionHandler public BaseResponse exceptionHandler(HttpServletRequest req, Exception e) { log.error(e.getMessage(), e); return BaseResponse.fail(HttpResponseStatus.SERVERERROR); } @ExceptionHandler(NullPointerException.class) @ResponseStatus(code = HttpStatus.OK) public BaseResponse NullPointer(NullPointerException ex) { log.error(ex.getMessage(), ex); return BaseResponse.fail(HttpResponseStatus.SERVERERROR); } }
3.3、在goods-service的依赖里面添加common的依赖
<dependency> <groupId>com.umall.common</groupId> <artifactId>u-common</artifactId> <version>1.0</version> </dependency>
3.4、修改getPrice接口
@RequestMapping("/good/price/{goodId}") public BaseResponse getPrice(@PathVariable("goodId") Integer goodId) throws Exception { String price = null; return BaseResponse.success(new BigDecimal(price).setScale(2, BigDecimal.ROUND_DOWN)); }
3.5、在GoodApplication.java里面添加扫描common的包
@SpringBootApplication(scanBasePackages = { "com.umall.common.*","com.umall.goods.*" })
备注:这里由于我们是实战项目,所以使用的是多模块的微服务,这里的全局异常我们是放在common包里的,正常的goods-service就扫描不到了,所以需要给他添加扫描路径,让spring注解可以被扫描到。
3.6、启动GoodApplication.java,访问:http://localhost:9002/good/price/2
是不是结果就出来了。好了总结下:
1、这篇文章主要讲解了多模块为什么要使用全局异常。 2、处理全局异常使用的注解是controlleradvice和exceptionHandler。 3、多模块添加依赖后,导致包扫描不到如何解决。 4、如何自定义异常。
完整代码下载:点击下载
还没有评论,来说两句吧...