马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
优质博文:IT-BLOG-CN
为了防止黑客从前台非常信息,对体系举行攻击。同时,为了进步用户体验,我们都会都抛出的非常举行拦截处理处罚。
一、非常处理处罚类
Java把非常当做是粉碎正常流程的一个变乱,当变乱发生后,就会触发处理处罚机制。Java有一套独立的非常处理处罚机制,在碰到非常时,方法并不返回任何值(返回值属于正常流程),而是抛出一个封装了错误信息的对象。
Throwable
全部的非常对象都派生于Throwable类的一个实例。在一个Throwable内里可以获取如下信息:
■ 获取堆栈跟踪信息。源代码中哪个类,哪个方法,第几行出现了标题……从当前代码到最底层的代码调用链都可以查出来。追踪获取底层的非常信息。
■ 获取没抛出来的其他Throwable。一次只能抛出一个非常,假如发生了多个非常,其他非常就不会被抛出,这时可以通过参加suppressed非常列表来办理(JDK7 以后才有)
Throwable类只有两个直接继承者:Error和 Exception。然后Exception又分为RuntimeException和CheckedException。
Error
在Java中, 由体系情况标题引起的非常,一样寻常都继承于 Error 类。对于Error类:
■ 一样寻常开发者不要自界说Error子类,由于它代表体系级别的错误。与一样寻常的步调无关。
■ 在Java非常处理处罚机制中,Error不欺压捕捉或声明,也就是不欺压处理处罚。由于步调自己对此类错误无能为力。一样寻常情况下我们只要把堆栈跟踪信息记录下来就行。
Exception
在Java中,除了体系情况标题引起的非常,一样寻常都继承于Exception类。Exception分为RuntimeException和CheckedException。CheckedException必须要捕捉或声明。而RuntimeException不欺压。
CheckedException: 在Java中,直接或间接由于“资源”标题引起的非常,一样寻常属于查抄非常CheckedException。查抄非常继承于Exception,而不继承于RuntimeException。对于查抄非常:
■ 必须捕捉或声明
■ 交给关心这个非常的方法处理处罚
■ 非常处理处罚器应该引导用户接下来怎么办,至少做到安全退出
RuntimeException: 在Java中,由于接口方法利用不妥造成的非常,一样寻常属于RuntimeException,也就是运行时非常。对于RuntimeException:
■ 假如你调用服务方法的方式不精确,你应该立刻修改代码,制止发生RuntimeException
■ 假如是用户方法调用你的方法的方式不精确,你应该立刻抛出RuntimeException,欺压让利用者修正代码或改变利用方式,防止标题伸张
■ 一样寻常情况下,不要捕捉或声明RuntimeException。由于标题在于你的步调自己有标题,假如你用非常流程处理处罚了,反而让正常流程标题不绝存在
Uncheck Exception
Error和RuntimeException统称为非查抄非常。两者的共同点就是都不被欺压捕捉或声明。实际上两者形貌标题的范围完全没有交集。
二、Java 非常处理处罚机制
【1】抛出非常: 当一个方法出现错误引发非常时,方法创建非常对象并交付运行时体系,非常对象中包罗了非常范例和非常出现时的步调状态等非常信息。运行时体系负责探求处理非常的代码并实验。
【2】捕捉非常:在方法抛出非常之后,运行时体系将转为探求符合的非常处理处罚器(exception handler)。埋伏的非常处理处罚器黑白常发生时依次存留在调用栈中的方法的聚集。当非常处理处罚器所能处理处罚的非常范例与方法抛出的非常范例符合时,即为符合的非常处理处罚器。运行时体系从发生非常的方法开始,依次回查调用栈中的方法,直至找到含有符合非常处理处罚器的方法并实验。当运行时体系遍历调用栈而未找到符合的非常处理处罚器,则运行时体系停止。同时,意味着 Java 步调的停止。
对于运行时非常、错误或可查非常,Java 技能所要求的非常处理处罚方式有所差别。
■ 由于运行时非常的不可查性,为了更公道、更容易地实现应用步调,Java 规定,运行时非常将由 Java 运行时体系自动抛出,允许应用步调忽略运行时非常。
■ 对于方法运行中大概出现的 Error,当运行方法不欲捕捉时,Java 允许该方法不做任何抛作声明。由于,大多数 Error 非常属于永久不能被允许发生的状态,也属于公道的应用步调不应捕捉的非常。
■ 对于全部的可查非常,Java 规定:一个方法必须捕捉,大概声明抛出方法之外。也就是说,当一个方法选择不捕捉可查非常时,它必须声明将抛出非常。
■ 可以或许捕捉非常的方法,须要提供符合范例的非常处理处罚器。所捕捉的非常,大概是由于自身语句所引发并抛出的非常,也大概是由某个调用的方法大概 Java 运行时 体系等抛出的非常。也就是说,一个方法所能捕捉的非常,肯定是 Java 代码在某地方抛出的非常。简单地说,非常总是先被抛出,后被捕捉的。
■ 任何 Java 代码都可以抛出非常,如:自己编写的代码、来自 Java 开发情况包中代码,大概 Java 运行时体系。无论是谁,都可以通过 Java 的 throw 语句抛出非常。
■ 从方法中抛出的任何非常都必须利用 throws 子句。捕捉非常通过 try-catch 语句大概 try-catch-finally 语句实现。
总体来说,Java 规定:对于可查非常必须捕捉、大概声明抛出。允许忽略不可查的 RuntimeException 和 Error。
捕捉非常
【1】try-catch 语句:在 Java 中,非常通过 try-catch 语句竣事。关键词 try 后的一对大括号将一块大概发生非常的代码包起来,称为监控 地区。Java 方法在运行过程中出现非常,则创建非常对象。将非常抛出监控 地区之 外,由 Java 运行时体系试图探求匹配的 catch 子句以捕捉非常。如有匹配的 catch 子句,则运行其非常处理处罚代码,try-catch 语句竣事。
匹配的原则是:假如抛出的非常对象属于 catch 子句的非常类,大概属于该非常类的子类,则以为天生的非常对象与 catch 块捕捉的非常范例相匹配。
须要注意的是,一旦某个 catch 捕捉到匹配的非常范例,将进入非常处理处罚代码。一经处理处罚竣事,就意味着整个 try-catch 语句竣事。其他的 catch 子句不再有匹配和捕捉非常范例的机遇。对于有多个 catch 子句的非常步调而言,应该只管将捕捉底层非常类的 catch 子句放在前面,同时只管将捕捉相对高层的非常类的 catch 子句放在背面。否则,捕捉底层非常类的 catch 子句将大概会被屏蔽。
【2】try-catch-finally 语句:try-catch 语句还可以包罗第三部门,就是 finally 子句。它表现无论是否出现非常,都应当实验的内容。
无论是否捕捉或处理处罚非常,finally 块里的语句都会被实验。当在 try 块或 catch 块中碰到 return 语句时,finally 语句块将在方法返回之前被实验。在以下 4 种特别情况下,finally 块不会被实验:
■ 在 finally 语句块中发生了非常。
■ 在前面的代码中用了 exit()退出步调。
■ 步调所在的线程殒命。
■ 关闭 CPU。
三、全局非常处理处罚
编写一个非常拦截类,如下:@ControllerAdvice,顾名思义,这是一个加强的Controller。利用这个Controller ,可以实现三个方面的功能:①、全局非常处理处罚;②、全局数据绑定;③、全局数据预处理处罚;机动利用这三个功能,可以资助我们简化许多工作,须要注意的是,这是SpringMVC提供的功能,在SpringBoot中可以直接利用,下面分别来看。
- import com.edu.tools.R;
- import org.springframework.web.bind.annotation.ControllerAdvice;
- import org.springframework.web.bind.annotation.ExceptionHandler;
- import org.springframework.web.bind.annotation.ResponseBody;
- /**
- * @description:
- * @author: zzx
- * @createDate: 2020/6/2
- * @version: 1.0
- */
- @ControllerAdvice
- public class GlobalExceptionHandler {
- //很重要,括号类制定需要拦截的异常,也可以进行定制化
- @ExceptionHandler(Exception.class)
- @ResponseBody
- public R error(Exception e){
- e.printStackTrace();
- //R表示我们给前端返回的接口格式
- return R.error().message("执行全局异常处理。。。");
- }
- }
复制代码 测试:
四、自界说非常处理处罚
【1】创建自界说非常类继承RuntimeException类。
- /**
- * @description: 自定义异常类,包含了有参合无参构造器
- * @author: zzx
- * @createDate: 2020/6/2
- * @version: 1.0
- */
- @Data
- @AllArgsConstructor
- @NoArgsConstructor
- public class BusinessException extends RuntimeException {
- private Integer code;//状态码
- private String msg;//异常消息
- }
复制代码 【2】将自界说的非常类添加到拦截的Handler中
- /**
- * @description:
- * @author: zzx
- * @createDate: 2020/6/2
- * @version: 1.0
- */
- @ControllerAdvice
- public class GlobalExceptionHandler {
- //拦截自定义异常
- @ExceptionHandler(BusinessException.class)
- @ResponseBody
- public R error(BusinessException e){
- e.printStackTrace();
- return R.error().code(e.getCode()).message(e.getMsg());
- }
- }
复制代码 【3】在业务代码根据需求举行手动抛出即可,业务代码展示:throw new BusinessException(20001,“手动非常抛出”);
- /**
- * <p>
- * 讲师 前端控制器
- * </p>
- *
- * @author zhengzhaoxiang
- * @since 2020-06-01
- */
- @RestController
- @RequestMapping("/eduservice/edu-teacher")
- public class EduTeacherController {
- @Autowired
- private EduTeacherService eduTeacherService;
- /**
- * @Description 获取所有数据
- * @Author zhengzhaoxiang
- * @Date 2020/6/2 15:27
- * @Param []
- * @Return void
- */
- @GetMapping("findAll")
- public R findAll(){
- List<EduTeacher> list = eduTeacherService.list(null);
- try{
- int i = 1/0;
- } catch (Exception e){
- //手动抛出异常
- throw new BusinessException(20001,"手动异常抛出");
- }
- return R.ok().data("items",list);
- }
- }
复制代码 自界说非常处理处罚类测试:
五、案例
某日11点23分 xxx处理处罚中量(5s)智能检测发现非常降落60%
事后分析得知其根因是: 修改模板设置时,多配了个占位符%S导致字符串格式化的方法堕落,步调未举行非常捕捉,输入页加载查询接口失败,前端页面举行降级,关闭了入口,影响用户操纵。
研发团队给出的办理方案:针对读取设置文案非常须要举行捕捉降级,不能影响业务主流程。
案例分析
针对这类错误,以往的办理方案通常是打补丁式的修正,即:针对已发生非常的设置项举行容错处理处罚(对分析函数添加try…catch,如上方)。
然而,每一次非常险些都是发生在差别的设置上,也就是说我们的补丁对于生产变乱的产生险些没有任何克制作用。补丁式修正只能治标而不能治本,我们须要一种能治本的方案。下面就让我来给各人先容这么一种方案(假如您有更好的方案,望不吝见教)
最佳实践
步调初始化时将全部设置读取并分析后生存为当地变量,应用每次直接读取当地Config变量。监听QConfig,当设置发生变革时调用步调1的方法重新分析设置文件。假如分析时发生非常,记录错误日志 /报警,并利用旧的设置。示例代码:
- public class ConfigStatic {
- private static final Logger LOGGER = LoggerFactory.getLogger(ConfigStatic.class);
- private static ConfigStatic instance = new ConfigStatic();
- private static final String TITLE = "ConfigStatic";
- private final boolean initializeSuccess;
- static {
- // 将ConfigStatic.refresh注册至ConfigurationFunc,当配置发生变化的时候ConfigStatic.refresh将被调用
- ConfigurationFunc.registerListener(TITLE, ConfigStatic::refresh);
- }
- private ConfigStatic() {
- initializeSuccess = initialize();
- }
- public static ConfigStatic getInstance() {
- return instance;
- }
- private static void refresh() {
- // 创建新配置(构造函数会调用initialize进行初始化)
- ConfigStatic newConfig = new ConfigStatic();
- if (newConfig.initializeSuccess) {
- // 如果新实例初始化成功,将其替换为instance
- instance = newConfig;
- }
- }
-
- private Set<Integer> hsFcAddjustPriceAgencyId;
- // 此处省略其他配置...
- private boolean initialize() {
- final String title = "ConfigStatic.initialize";
- try {
-
- this.directCompareNormalAirlines = ConfigurationFunc.getHashSet("yPlusXProductConfig", ",");
- // 此处省略其他配置...
- return true;
- } catch (Exception ex) {
- LogManager.build(title, ex).error();
- return false;
- }
- }
- }
复制代码 |