springboot2.x整合Email并利用AOP做一个项目异常通知功能
因为不知aop能干嘛,因此用aop做个小功能,再结合最近学的springboot-Email做了个系统异常自动邮件通知的功能,
感觉满满的成就感。
AOP不懂的可以看上一篇:https://www.cnblogs.com/zgq7/p/11310142.html
spring-email不懂的看上一篇:https://www.cnblogs.com/zgq7/p/11314895.html
先看看这个功能的总体规划图:
因此需要思考的是:
1:如何捕获异常?
总不能在每个会发生异常的地方写 throw 或者 try-catch 语句吧?因此利用AOP进行统一捕获并进行下一步处理。
2:如何将异常信息发送到开发者(用户)邮箱?
这就需要用到javaMail技术了,而我在maven仓库搜寻时看到了springboot-Email,因此去自发了解了这个开发流程。
本来这里打算使用原生的JavaMail的,但是springboot集成了就没用了。
原生的可参考这里:https://www.cnblogs.com/LUA123/p/5575134.html
下面开始我的设计思路的实现流程。
1:添加相关springboot-mail以及springboot-aop 相关依赖
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId><version>2.1.6.RELEASE</version></dependency><!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-mail --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId><version>2.1.6.RELEASE</version></dependency>
2:构造一个本地线程池,以防高并发。
package com.dev.config;import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean;import java.lang.reflect.Method; import java.util.concurrent.*;/*** Created on 2019-08-05 14:00.** @author zgq7*/ public class LocalThreadPool implements InitializingBean, DisposableBean {public static final String PACKAGE_BEAN_NAME = "localThreadPool";private static final Logger logger = LoggerFactory.getLogger(LocalThreadPool.class);/*** capacity 队列容量**/private final BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(5000);private final RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();private final ThreadFactory defaultThreadFactory = Executors.defaultThreadFactory();private final ThreadFactory privilegedThreadFactory = Executors.privilegedThreadFactory();/*** corePoolSize 核心线程数量* maximumPoolSize 线程池允许最大线程池数量* keepAliveTime 当线程数超过本地线程核心数同时又小于设置的最大线程数,需要重新创建一个新线程时需要等待的时间* TimeUnit.MILLISECONDS 时间单位,这里我设置的是豪秒* workQueue 一个队列:当一个task被执行前进行使用* threadFactory 用于创建新线程的线程工厂* handler 一个处理器:当线程被锁、或者队列的容量达到上限时 被调用**/public final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 3000, TimeUnit.MILLISECONDS, workQueue, defaultThreadFactory, handler);/*** 本地线程池被销毁后的回调方法**/@Overridepublic void destroy() throws Exception {logger.info("指令->[本地线程池已销毁]");}/*** 本地线程池成功初始化的回调犯法**/@Overridepublic void afterPropertiesSet() throws Exception {logger.info("指令->[本地线程池已成功初始化]");}}
3:邮件工具类请参考我上一篇博客:https://www.cnblogs.com/zgq7/p/11314895.html
4:编写一个切面类,用于全局捕捉程序产生的异常
package com.dev.config.aop;import com.dev.config.LocalThreadPool; import com.dev.model.email.EmailModel; import com.dev.utils.email.MailSendUtils; import com.dev.utils.exception.ExceptionCodes; import com.dev.utils.exception.ServiceException; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.annotation.Order;import java.util.Arrays;/*** Created on 2019-07-31 9:41.** @author zgq7*/ @Aspect @Order(2) public class RuntimeExceptionAspectJ {@Autowiredprivate MailSendUtils mailSendUtils;@Autowiredprivate LocalThreadPool localThreadPool;private final Logger log = LoggerFactory.getLogger(this.getClass());//@Pointcut("execution(public * com.dev..*(..))")@Pointcut("execution(public * com.dev.controller.TestController.*(..))")private void runtimeExceptionAspect() {}/*** 切面报错**/@AfterThrowing(value = "runtimeExceptionAspect()", throwing = "exception")public void afterThrowing(Throwable exception) {Class klass = exception.getClass();log.error("occured a [{}] , msg : [{}]", klass.getSimpleName(), ExceptionCodes.getMsgByKlass(klass));EmailModel emailModel = new EmailModel();emailModel.setEmailTheme("测试");emailModel.setRecieverName("测试");emailModel.setEmailContent(exception.toString() + ":\n" + Arrays.toString(exception.getStackTrace()));emailModel.setRecieverEmailAddress("xxx@qq.com");localThreadPool.threadPoolExecutor.getThreadFactory().newThread(() -> mailSendUtils.sendEmailAsSysExceptionHtml(emailModel)).start();throw new ServiceException(ExceptionCodes.getCodeByKlass(klass), ExceptionCodes.getMsgByKlass(klass));}}
4.1:为了更方便的区分异常类型,我在程序内部设计了这个枚举(各位看官看看就好)
package com.dev.utils.exception;import java.io.FileNotFoundException; import java.io.IOException; import java.net.ProtocolException; import java.sql.SQLException;/*** Created on 2019-07-31 16:01.** @author zgq7* @apiNote 自定义的code - XxxException.class*/ public enum ExceptionCodes {//RuntimeException 类型异常RUNTIME_EXCEPTION(100, "运行时异常", RuntimeException.class),NULL_POINT_EXCEPTION(101, "空指针异常", NullPointerException.class),ARITHMETIC_EXCEPTION(102, "数学错误,被0除", ArithmeticException.class),INDEX_OUT_OF_BOUNDS_EXCEPTION(103, "当某对象的索引超出范围时抛出异常", IndexOutOfBoundsException.class),ARRAY_INDEX_OUT_OF_EXCEPTION(103001, "数组下标越界", ArrayIndexOutOfBoundsException.class),CLASS_CAST_EXCEPTION(104, "强制转换异常", ClassCastException.class),ILLEGAL_ARGUMENT_EXCEPTION(105, "非法转换", IllegalArgumentException.class),NUMBER_FORMAT_EXCEPTION(105001, "字符串转换为数字异常类", NumberFormatException.class),PROTOCOL_EXCEPTION(106, "网络协议有错误", ProtocolException.class),//IOException 类型异常IO_EXCEPTION(200, "IO 流异常", IOException.class),FILE_NOT_FOUND_EXCEPTION(201, "文件找不到", FileNotFoundException.class),//数据库 sql 操作异常SQL_EXCEPTION(300, "操作数据库异常", SQLException.class),//其他相关异常REFLECTIVE_OPERATION_EXCEPTION(400, "", ReflectiveOperationException.class),ILLEGAL_ACESS_EXCEPTION(401, "访问某类被拒绝时抛出的异常", IllegalAccessException.class);private int code;private String msg;private Class<Exception> klass;ExceptionCodes(int code, String msg, Class klass) {this.code = code;this.msg = msg;this.klass = klass;}public int getCode() {return this.code;}public String getMsg() {return this.msg;}public Class<Exception> getKlass() {return this.klass;}/*** 通过异常码获取对应异常类**/public static Class<Exception> getKlassByCode(int code) {for (ExceptionCodes exceptionCodes : ExceptionCodes.values()) {if (exceptionCodes.getCode() == code)return exceptionCodes.getKlass();}return null;}/*** 根据异常码获取对应异常信息**/public static String getMsgByCode(int code) {for (ExceptionCodes exceptionCodes : ExceptionCodes.values()) {if (exceptionCodes.getCode() == code)return exceptionCodes.getMsg();}return null;}/*** 根据异常类获取异常码**/public static int getCodeByKlass(Class<? extends Exception> klass) {for (ExceptionCodes exceptionCodes : ExceptionCodes.values()) {if (exceptionCodes.getKlass() == klass)return exceptionCodes.getCode();}return 3000;}/*** 根据异常类获取异常信息**/public static String getMsgByKlass(Class<? extends Exception> klass) {for (ExceptionCodes exceptionCodes : ExceptionCodes.values()) {if (exceptionCodes.getKlass() == klass)return exceptionCodes.getMsg();}return null;}}
异常枚举
5:注册相关bean
package com.dev.config;import com.dev.config.aop.BaseAop; import com.dev.config.aop.RuntimeExceptionAspectJ; import com.dev.filter.BaseFilter; import com.dev.utils.email.MailSendUtils; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.core.annotation.Order; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.JavaMailSenderImpl;import java.util.*;/*** Created by zgq7 on 2019/6/6.* 注册一些bean进入ioc** @EnableAspectJAutoProxy 开启aop代理*/ @Configuration @EnableAspectJAutoProxy public class BeanRegistryCenterConfig {/*** 邮箱工具类 bean 注册**/@Beanpublic MailSendUtils mailSendUtils() {return new MailSendUtils();}/*** 本地线程 bean 注册**/@Bean(name = LocalThreadPool.PACKAGE_BEAN_NAME)public LocalThreadPool localThreadPool() {return new LocalThreadPool();}/*** 异常捕获类 RuntimeExceptionAspectJ bean 注册**/@Beanpublic RuntimeExceptionAspectJ runtimeExceptionAspectJ() {return new RuntimeExceptionAspectJ();}}
6:controller中制造一个runtimeException类型的异常,如下
package com.dev.controller;import com.dev.controller.bases.BaseController; import com.dev.service.AopiService; import com.dev.utils.exception.ServiceException; import com.google.common.collect.ImmutableMap; import okhttp3.*; import org.springframework.web.bind.annotation.*;import javax.annotation.Resource; import java.io.*; import java.util.Collections; import java.util.Map;/*** Created by zgq7 on 2019/6/6.*/ @RestController @RequestMapping(value = "/dev") public class TestController extends BaseController {@Resource(name = AopiService.PACKAGE_BEAN_NAME)private AopiService aopiService;@GetMapping(value = "")public Map<Object, Object> get() {if (1 == 1)throw new NullPointerException();return ImmutableMap.of("code", aopiService.getAopList());} }
7:运行项目进行测试
7.1:先看启动日志
项目成功启动,本地线程池也初始化成功!!!
7.2:使用postMan进行测试
7.3:邮箱接收到的邮件
总结:做到这一步,程序在遇到异常时,便可主动给开发者发报错信息了,若线上出了什么小异常,可以直接查看。
如有不理解的地方请下方留言,喜欢的点个赞。
本文项目地址:https://github.com/zgq7/devloper-mine
转载于:https://www.cnblogs.com/zgq7/p/11310776.html
springboot2.x整合Email并利用AOP做一个项目异常通知功能相关推荐
- python自己做个定时器_技术图文:如何利用 Python 做一个简单的定时器类?
原标题:技术图文:如何利用 Python 做一个简单的定时器类? 背景 今天在B站上看有关 Python 最火的一个教学视频 -- "零基础入门学习 Python",这也是我们 P ...
- 利用pgzero做一个接球的小游戏
利用pgzero做一个接球的小游戏 说明 pgzero为python的一个用于游戏制作的库,它基于pygame模块 可用如下命令去安装 pip install pygame pip install p ...
- 利用Python做一个简单的对战小游戏
利用Python做一个简单的文字对战小游戏 一.游戏介绍 1.大体介绍:文字版的对战小游戏,可以利用Python随机生成两个角色,角色带有各自的血量和攻击值两个指标.两人在对战时同时攻击对方,同时造成 ...
- 多个敏捷团队同时做一个项目_您说您的团队很敏捷……但是这个词可能并不代表您的想法。...
多个敏捷团队同时做一个项目 by Mark Shead 由马克·希德(Mark Shead) Many things get called Agile - especially by people w ...
- android计算器功能实现,在android中利用 studio实现一个简单的计算器功能
在android中利用 studio实现一个简单的计算器功能 发布时间:2020-11-07 15:35:20 来源:亿速云 阅读:168 作者:Leah 这篇文章将为大家详细讲解有关在android ...
- android实现计算器功能吗,利用Android实现一个简单的计算器功能
利用Android实现一个简单的计算器功能 发布时间:2020-11-20 16:25:01 来源:亿速云 阅读:90 作者:Leah 今天就跟大家聊聊有关利用Android实现一个简单的计算器功能, ...
- 做一个项目,平时都用到哪些工具提高效率(上)
做一个项目,平时都用到哪些工具提高效率(上) 转载 做.NET 相关项目,Visual Studio 2008,SQL Server 2000/2005是标准的配置,但是,除此之外,还可以应用哪些工 ...
- 使用vue-cli+element-ui+expsess+mysql做一个简易的登录功能
使用vue-cli+element-ui+expsess+mysql做一个简易的登录功能 1使用webpack下载vue模板 vue init webpack aaa(aaa为项目名称) cd到aaa ...
- php利用ajax文件上传,如何在PHP中利用AjaxForm实现一个文件上传功能
如何在PHP中利用AjaxForm实现一个文件上传功能 发布时间:2020-12-18 14:52:38 来源:亿速云 阅读:94 作者:Leah 如何在PHP中利用AjaxForm实现一个文件上传功 ...
- 做一个项目,平时都用到哪些工具提高效率(下)
接上回,继续分享.NET开发中关于工具的使用经验 15 报表工具.数据的导入导出是系统的一项基本功能,我们让用户辛苦的输入那么多数据,现在是让用户享受成果的时候,你的程序要能用各种方法分析用户的in ...
最新文章
- Java基础/利用fastjson序列化对象为JSON
- 关于版本号:alpha、beta、rc、stable
- Python常用模块之random模块
- 一般性网络错误 请检查网络文档_如何编写好的软件设计文档
- 来自http://oldboy.blog.51cto.com/2561410/1308647 有趣的企业shell实战编程题:
- qt样式表中背景图片的使用
- Windows7系统资源怎么看?
- java怎么把弹框设置为圆角_自定义圆角Dialog
- ffmpeg drawtext 背景_8款电视背景墙:电视背景墙这样装,不仅省钱还作用多!效果大不一样!...
- 西南科技大学OJ题 集合的交运算的实现1045
- 用波尔理论推导里德伯公式
- 笔记MySQLJavaweb
- 大一python选择题题库及答案_大学计算机python选择填空题库及答案
- spark学习之资源调度
- JSON学习之XOM的认识
- C语言怎么提出大写字母,c语言函数toupper()如何将小写字母转换为大写字母
- JavaEE——作业管理系统期末总结
- CAM350 V10.5/V14.6 导出拼板gerber文件
- 20221005CSP-J2/S2模拟赛总结
- static变量会被垃圾回收吗_来自灵魂的拷问,你会扔垃圾了吗?