Springboot中Aspect实现切面(以记录日志为例)

今天我们来说说spring中的切面Aspect,这是Spring的一大优势。面向切面编程往往让我们的开发更加低耦合,也大大减少了代码量,同时呢让我们更专注于业务模块的开发,把那些与业务无关的东西提取出去,便于后期的维护和迭代。

视频看的动力节点的视频,整理了这篇博客作为笔记,以后复习时候看看,也分享给有需要的小伙伴。

视频资源:https://www.bilibili.com/video/BV1XQ4y1m7ex

相关概念

1. 切面(Aspect):

首先要理解‘切’字,需要把对象想象成一个立方体,传统的面向对象变成思维,类定义完成之后(封装)。每次实例化一个对象,对类定义中的成员变量赋值,就相当于对这个立方体进行了一个定义,定义完成之后,就等着被使用,等着被回收。

面向切面编程则是指,对于一个我们已经封装好的类,我们可以在编译期间或在运行期间,对其进行切割,把立方体切开,在原有的方法里面添加(织入)一些新的代码,对原有的方法代码进行一次增强处理。而那些增强部分的代码,就被称之为切面,如下面代码实例中的通用日志处理代码,常见的还有事务处理、权限认证等等。

2. 切入点(PointCut):

要对哪些类中的哪些方法进行增强,进行切割,指的是被增强的方法。即要切哪些东西。

3. 连接点(JoinPoint):

知道了要切哪些方法后,剩下的就是什么时候切,在原方法的哪一个执行阶段加入增加代码,这个就是连接点。如方法调用前,方法调用后,发生异常时等等。

4. 通知(Advice):

通知被织入方法,改如何被增强。定义切面的具体实现。那么这里面就涉及到一个问题,空间(切哪里)和时间(什么时候切,在何时加入增加代码),空间我们已经知道了就是切入点中定义的方法,而什么时候切,则是连接点的概念。

5. 目标对象(Target Object):

被一个或多个切面所通知的对象,即为目标对象。

6. AOP代理对象(AOP Proxy Object):

AOP代理是AOP框架所生成的对象,该对象是目标对象的代理对象。代理对象能够在目标对象的基础上,在相应的连接点上调用通知。

7. 织入(Weaving):

将切面切入到目标方法之中,使目标方法得到增强的过程被称之为织入。

AOP 必须的依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
image.gif

日志实体类

package com.space.aspect.bo;

import lombok.Data;

/**
 * 系统日志bo
 */
@Data
public class SysLogBO {

    private String className;

    private String methodName;

    private String params;

    private Long exeuTime;

    private String remark;

    private String createDate;
}
image.gif

日志service

package com.space.aspect.service;

import com.space.aspect.bo.SysLogBO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class SysLogService {

    public boolean save(SysLogBO sysLogBO){
        // 这里就不做具体实现了
        log.info(sysLogBO.getParams());
        return true;
    }

}
image.gif

定义日志注解

package com.space.aspect.anno;

import java.lang.annotation.*;

/**
 * 定义系统日志注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {
    String value() default "";
}
image.gif

声明切面,完成日志记录

以上4点我们的准备工作已经完成。接下来就是重点了

package com.space.aspect.aspect;

import com.google.gson.Gson;
import com.space.aspect.anno.SysLog;
import com.space.aspect.bo.SysLogBO;
import com.space.aspect.service.SysLogService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
image.gif
/**
 * 系统日志切面
 */
@Aspect  // 使用@Aspect注解声明一个切面
@Component
public class SysLogAspect {

    @Autowired
    private SysLogService sysLogService;

    /**
     * 这里我们使用注解的形式
     * 当然,我们也可以通过切点表达式直接指定需要拦截的package,需要拦截的class 以及 method
     * 切点表达式:   execution(...)
          *
     * execution(public * *(..)) 任意的公共方法
     * execution(* set*(..)) 以set开头的所有的方法
     * execution(* com.LoggerApply.*(..))com.LoggerApply这个类里的所有的方法
     * execution(* com.annotation.*.*(..))com.annotation包下的所有的类的所有的方法
     * execution(* com.annotation..*.*(..))com.annotation包及子包下所有的类的所有的方法
     * execution(* com.annotation..*.*(String,?,Long)) com.annotation包及子包下所有的类的有三个参数,第一个参数为String类型,第二个参数为任意类型,第三个参数为Long类型的方法
     * execution(@annotation(com.lingyejun.annotation.Lingyejun))
     */
    @Pointcut("@annotation(com.space.aspect.anno.SysLog)")
    public void logPointCut() {}

    /**
     * 环绕通知 @Around  , 当然也可以使用 @Before (前置通知)  @After (后置通知)
     * @param point
     * @return
     * @throws Throwable
     */
    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        Object result = point.proceed();
        long time = System.currentTimeMillis() - beginTime;
        try {
            saveLog(point, time);
        } catch (Exception e) {
        }
        return result;
    }

    /**
     * 保存日志
     */
    private void saveLog(ProceedingJoinPoint joinPoint, long time) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        SysLogBO sysLogBO = new SysLogBO();
        sysLogBO.setExeuTime(time);
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        sysLogBO.setCreateDate(dateFormat.format(new Date()));
        SysLog sysLog = method.getAnnotation(SysLog.class);
        if(sysLog != null){
            //注解上的描述
            sysLogBO.setRemark(sysLog.value());
        }
        //请求的 类名、方法名
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = signature.getName();
        sysLogBO.setClassName(className);
        sysLogBO.setMethodName(methodName);
        //请求的参数
        Object[] args = joinPoint.getArgs();
        try{
            List<String> list = new ArrayList<String>();
            for (Object o : args) {
                list.add(new Gson().toJson(o));
            }
            sysLogBO.setParams(list.toString());
        }catch (Exception e){ }
        sysLogService.save(sysLogBO);
    }
}
image.gif

测试

package com.space.aspect.controller;

import com.space.aspect.anno.SysLog;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    @SysLog("测试")
    @GetMapping("/test")
    public String test(@RequestParam("name") String name){
        return name;
    }
}
image.gif

这样,我们就成功实现了使用Aspect实现切面记录日志。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容