美文网首页SpringBoot + Vue后台维护系统JavaJava子弹
SpringBoot + Vue 后台管理系统(六):系统操作日

SpringBoot + Vue 后台管理系统(六):系统操作日

作者: Bertram_Wang | 来源:发表于2019-06-09 01:33 被阅读10次

    AOP

    面向切面编程。主要功能是对方法的加强。其实现是居于代理模式使用。
    Spring事务就是居于AOP的实现。
    首先了解一下相关概念

    • 切面(Aspect): 通常是一个类,定义切入点和通知
    • 连接点(Join point): 程序执行过程中方法的调用
    • 通知(Advice):切面在特定的连接点的增强。
      • 前置通知: 连接点执行之前的增强,但不能阻止连接点执行(除非抛出异常)
      • 后置通知: 连接点正常执行之后的增强
      • 异常通知: 连接点抛出异常的增强
      • 执行通知: 连接点执行后(不论是否抛出异常)的增强
      • 围绕通知: 在连接点执行前后的增强
    • 切入点(Pointcut): 执行切面的匹配点。(特定名称的方法,特定的注解等)
    • AOP代理(AOP proxy): AOP框架创建的对象,代理就是目标对象的加强。
    • 引用(introduction): 准许目标对象引入新的接口以及相应的实现。
    • 目标对象(Target object): 被加强的对象。
    • 编织(Weaving):将切面与其他应用程序类型或对象链接,以创建通知的对象。这可以在编译时(例如,使用AspectJ编译器)、加载时或运行时完成。与其他纯Java AOP框架一样,Spring AOP在运行时执行编织。
      具体参考:SpringAOP

    AOP使用-日志记录

    • 创建表记录数据
    CREATE TABLE `sys_logger`  (
      `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '记录ID',
      `create_date` datetime(0) NOT NULL COMMENT '创建时间',
      `modify_date` datetime(0) NOT NULL COMMENT '修改时间',
      `describes` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '描述',
      `method` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '执行的方法-类全命名.方法',
      `params` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '传入的参数',
      `host` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '主机',
      `address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '地址',
      PRIMARY KEY (`id`) USING BTREE
    ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Compact;
    
    • 创建实体类
    @Data
    @EqualsAndHashCode(callSuper = true)
    @Accessors(chain = true)
    @TableName("sys_logger")
    public class Logger extends BaseEntity { 
        private static final long serialVersionUID = 1L; 
        private String describes; 
        private String method; 
        private String params; 
        private String host; 
        private String address; 
    }
    
    • 切面类(具体业务逻辑替换为日志打印)
    @Aspect
    @Component
    public class RecordLoggerAcpect {
        private static final Logger log = LoggerFactory.getLogger(RecordLoggerAcpect.class);
    
        /**
         * @Description: 定义切入点 
         */
        //被注解CustomAopAnnotation表示的方法
        @Pointcut("@annotation(bertram.wang.vueweb.annotation.RecordLoggerAnnotation)") 
        public void pointCut(){}
    
        /**
         * @Description: 定义前置通知 
         */
        @Before("pointCut()")
        public void before(JoinPoint joinPoint) throws Throwable {
            // 接收到请求,记录请求内容
            log.info("【注解:Before】------------------切面  before");
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            // 记录下请求内容
            log.info("【注解:Before】浏览器输入的网址=URL : " + request.getRequestURL().toString());
            log.info("【注解:Before】HTTP_METHOD : " + request.getMethod());
            log.info("【注解:Before】IP : " + request.getRemoteAddr());
            log.info("【注解:Before】执行的业务方法名=CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
            log.info("【注解:Before】业务方法获得的参数=ARGS : " + Arrays.toString(joinPoint.getArgs()));
    
        }
    
        /**
         * @Description: 后置返回通知 
         */
        @AfterReturning(returning = "ret", pointcut = "pointCut()")
        public void afterReturning(Object ret) throws Throwable {
            // 处理完请求,返回内容
            log.info("【注解:AfterReturning】这个会在切面最后的最后打印,方法的返回值 : " + ret);
        }
    
        /**
         * @Description: 后置异常通知 
         */
        @AfterThrowing("pointCut()")
        public void afterThrowing(JoinPoint jp){
            log.info("【注解:AfterThrowing】方法异常时执行.....");
        }
    
        /**
         * @Description: 后置最终通知,final增强,不管是抛出异常或者正常退出都会执行 
         */
        @After("pointCut()")
        public void after(JoinPoint jp){
            log.info("【注解:After】方法最后执行.....");
        }
    
        /**
         * @Description: 环绕通知,环绕增强 
         * @return
         */
        @Around("pointCut()")
        public Object around(ProceedingJoinPoint pjp) {
            log.info("【注解:Around . 环绕前】方法环绕start.....");
            try {
                //如果不执行这句,会不执行切面的Before方法及controller的业务方法
                Object o =  pjp.proceed();
                log.info("【注解:Around. 环绕后】方法环绕proceed,结果是 :" + o);
                return o;
            } catch (Throwable e) {
                e.printStackTrace();
                return null;
            }
        }
    }
    
    • 自定义注解切入点
    public @interface RecordLoggerAnnotation { 
        String value() default "";
    }
    
    • 测试控制器
    @RestController
    @RequestMapping("/hello")
    public class HelloController {
        private static final Logger log = LoggerFactory.getLogger(HelloController.class); 
        @GetMapping("/testone")
        @RecordLoggerAnnotation("testone")
        public MyReponse<?> testone(@RequestParam Map<String, Object> params) {
            log.info("params:{}", params);
            return success();
        } 
    }
    
    • 测试单元
    @Test
    public void testone() throws Exception {
        String requestGET = requestGET("/hello/testone?name=123");
        log.info("===================rest:{}", requestGET);
    }
    
    正常执行的日志
    2019-06-09 01:22:49.924  INFO 14436 --- [           main] bertram.wang.test.ApplicationTest        : Started ApplicationTest in 34.477 seconds (JVM running for 35.444)
    2019-06-09 01:22:50.108 DEBUG 14436 --- [           main] s.n.www.protocol.http.HttpURLConnection  : sun.net.www.MessageHeader@16944b587 pairs: {GET /vueweb/hello/testone?name=123 HTTP/1.1: null}{Accept: text/plain, text/plain, application/json, application/json, application/*+json, application/*+json, */*, */*}{Content-type: application/json;charset=utf-8;Accept:application/json;}{Authorization: dcf08714-7cb6-451a-8dc1-56e2192a6933}{User-Agent: Java/1.8.0_191}{Host: localhost:55305}{Connection: keep-alive}
    2019-06-09 01:22:50.804  INFO 14436 --- [o-auto-1-exec-1] b.wang.vueweb.acpect.RecordLoggerAcpect  : 【注解:Around . 环绕前】方法环绕start.....
    2019-06-09 01:22:50.804  INFO 14436 --- [o-auto-1-exec-1] b.wang.vueweb.acpect.RecordLoggerAcpect  : 【注解:Before】------------------切面  before
    2019-06-09 01:22:50.804  INFO 14436 --- [o-auto-1-exec-1] b.wang.vueweb.acpect.RecordLoggerAcpect  : 【注解:Before】浏览器输入的网址=URL : http://localhost:55305/vueweb/hello/testone
    2019-06-09 01:22:50.805  INFO 14436 --- [o-auto-1-exec-1] b.wang.vueweb.acpect.RecordLoggerAcpect  : 【注解:Before】HTTP_METHOD : GET
    2019-06-09 01:22:50.805  INFO 14436 --- [o-auto-1-exec-1] b.wang.vueweb.acpect.RecordLoggerAcpect  : 【注解:Before】IP : 127.0.0.1
    2019-06-09 01:22:50.806  INFO 14436 --- [o-auto-1-exec-1] b.wang.vueweb.acpect.RecordLoggerAcpect  : 【注解:Before】执行的业务方法名=CLASS_METHOD : bertram.wang.vueweb.rest.HelloController.testone
    2019-06-09 01:22:50.806  INFO 14436 --- [o-auto-1-exec-1] b.wang.vueweb.acpect.RecordLoggerAcpect  : 【注解:Before】业务方法获得的参数=ARGS : [{name=123}]
    2019-06-09 01:22:50.809  INFO 14436 --- [o-auto-1-exec-1] b.wang.vueweb.rest.HelloController       : params:{name=123}
    2019-06-09 01:22:50.810  INFO 14436 --- [o-auto-1-exec-1] b.wang.vueweb.acpect.RecordLoggerAcpect  : 【注解:Around. 环绕后】方法环绕proceed,结果是 :MyReponse(code=0, message=成功, time=1560014570, data=null)
    2019-06-09 01:22:50.810  INFO 14436 --- [o-auto-1-exec-1] b.wang.vueweb.acpect.RecordLoggerAcpect  : 【注解:After】方法最后执行.....
    2019-06-09 01:22:50.811  INFO 14436 --- [o-auto-1-exec-1] b.wang.vueweb.acpect.RecordLoggerAcpect  : 【注解:AfterReturning】这个会在切面最后的最后打印,方法的返回值 : MyReponse(code=0, message=成功, time=1560014570, data=null)
    2019-06-09 01:22:50.846 DEBUG 14436 --- [           main] s.n.www.protocol.http.HttpURLConnection  : sun.net.www.MessageHeader@1dbc607d7 pairs: {null: HTTP/1.1 200}{Access-Control-Allow-Headers: Authorization,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type}{Access-Control-Allow-Methods: OPTIONS,GET,POST,DELETE,PUT}{Access-Control-Allow-Credentials: true}{Content-Type: application/json;charset=UTF-8}{Transfer-Encoding: chunked}{Date: Sat, 08 Jun 2019 17:22:50 GMT}
    2019-06-09 01:22:50.851  INFO 14436 --- [           main] bertram.wang.test.ApplicationTest        : ===================rest:{"code":0,"message":"成功","time":1560014570}
    

    从日志打印不难看出执行的顺序: 环绕前--前置--控制器(被加强的方法)--环绕后--后置(没有异常所以后置异常通知没有执行)
    最后就是把日志记录的业务逻辑修改 就行了。然后控制器的方法上添加注解即可。

    相关文章

      网友评论

        本文标题:SpringBoot + Vue 后台管理系统(六):系统操作日

        本文链接:https://www.haomeiwen.com/subject/evawxctx.html