因为不知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做一个项目异常通知功能相关推荐

  1. python自己做个定时器_技术图文:如何利用 Python 做一个简单的定时器类?

    原标题:技术图文:如何利用 Python 做一个简单的定时器类? 背景 今天在B站上看有关 Python 最火的一个教学视频 -- "零基础入门学习 Python",这也是我们 P ...

  2. 利用pgzero做一个接球的小游戏

    利用pgzero做一个接球的小游戏 说明 pgzero为python的一个用于游戏制作的库,它基于pygame模块 可用如下命令去安装 pip install pygame pip install p ...

  3. 利用Python做一个简单的对战小游戏

    利用Python做一个简单的文字对战小游戏 一.游戏介绍 1.大体介绍:文字版的对战小游戏,可以利用Python随机生成两个角色,角色带有各自的血量和攻击值两个指标.两人在对战时同时攻击对方,同时造成 ...

  4. 多个敏捷团队同时做一个项目_您说您的团队很敏捷……但是这个词可能并不代表您的想法。...

    多个敏捷团队同时做一个项目 by Mark Shead 由马克·希德(Mark Shead) Many things get called Agile - especially by people w ...

  5. android计算器功能实现,在android中利用 studio实现一个简单的计算器功能

    在android中利用 studio实现一个简单的计算器功能 发布时间:2020-11-07 15:35:20 来源:亿速云 阅读:168 作者:Leah 这篇文章将为大家详细讲解有关在android ...

  6. android实现计算器功能吗,利用Android实现一个简单的计算器功能

    利用Android实现一个简单的计算器功能 发布时间:2020-11-20 16:25:01 来源:亿速云 阅读:90 作者:Leah 今天就跟大家聊聊有关利用Android实现一个简单的计算器功能, ...

  7. 做一个项目,平时都用到哪些工具提高效率(上)

    做一个项目,平时都用到哪些工具提高效率(上)  转载 做.NET 相关项目,Visual Studio 2008,SQL Server 2000/2005是标准的配置,但是,除此之外,还可以应用哪些工 ...

  8. 使用vue-cli+element-ui+expsess+mysql做一个简易的登录功能

    使用vue-cli+element-ui+expsess+mysql做一个简易的登录功能 1使用webpack下载vue模板 vue init webpack aaa(aaa为项目名称) cd到aaa ...

  9. php利用ajax文件上传,如何在PHP中利用AjaxForm实现一个文件上传功能

    如何在PHP中利用AjaxForm实现一个文件上传功能 发布时间:2020-12-18 14:52:38 来源:亿速云 阅读:94 作者:Leah 如何在PHP中利用AjaxForm实现一个文件上传功 ...

  10. 做一个项目,平时都用到哪些工具提高效率(下)

    接上回,继续分享.NET开发中关于工具的使用经验 15  报表工具.数据的导入导出是系统的一项基本功能,我们让用户辛苦的输入那么多数据,现在是让用户享受成果的时候,你的程序要能用各种方法分析用户的in ...

最新文章

  1. Java基础/利用fastjson序列化对象为JSON
  2. 关于版本号:alpha、beta、rc、stable
  3. Python常用模块之random模块
  4. 一般性网络错误 请检查网络文档_如何编写好的软件设计文档
  5. 来自http://oldboy.blog.51cto.com/2561410/1308647 有趣的企业shell实战编程题:
  6. qt样式表中背景图片的使用
  7. Windows7系统资源怎么看?
  8. java怎么把弹框设置为圆角_自定义圆角Dialog
  9. ffmpeg drawtext 背景_8款电视背景墙:电视背景墙这样装,不仅省钱还作用多!效果大不一样!...
  10. 西南科技大学OJ题 集合的交运算的实现1045
  11. 用波尔理论推导里德伯公式
  12. 笔记MySQLJavaweb
  13. 大一python选择题题库及答案_大学计算机python选择填空题库及答案
  14. spark学习之资源调度
  15. JSON学习之XOM的认识
  16. C语言怎么提出大写字母,c语言函数toupper()如何将小写字母转换为大写字母
  17. JavaEE——作业管理系统期末总结
  18. CAM350 V10.5/V14.6 导出拼板gerber文件
  19. 20221005CSP-J2/S2模拟赛总结
  20. static变量会被垃圾回收吗_来自灵魂的拷问,你会扔垃圾了吗?

热门文章

  1. 网奇iwms插件之“我浏览过的文章”
  2. 游戏筑基开发之指针的练习掌握
  3. 设备密码的设置以及遗忘重设置
  4. 动态路由协议的基本配置---RIP
  5. 和为S的两个数字(python)
  6. 为何AI也学会了种族和性别歧视?
  7. 模板方法模式的房间改造-组合查询
  8. JavaScript学习笔记——运算符和表达式
  9. js中for循环的优化写法
  10. 【杭电ACM】1.2.6 decimal system