SpringBoot如何使用AOP

AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。其与设计模式完成的任务差不多,是提供另一种角度来思考程序的结构,来弥补面向对象编程的不足,这个可能是面试中经常提到的问题,同时它也是Spring框架中一个重大的特性,对于我们开发中最常见的可能就是日志记录,事务处理,异常处理等等。。。

proxy.png


本文将给大家介绍如何在SpringBoot中使用以及AOP的相关知识点

一、AOP知识点

1、原理

AOP是通过动态代理实现的,动态代理又分为两个部分:JDK动态代理和CGLIB动态代理,AOP功能的使用还是比较简单的,把相关bean注入到Spring容器中,编写好相应的Aspect类即可,以下两点需要记住:

1、AOP基于动态代理模式
2、AOP是方法级别

对代理模式不熟悉的可以参考我的这篇文章:JAVA代理模式

2、AOP术语

  • 切面(Aspect)

一个关注点的模块化,这个关注点可能会横切多个对象。事务管理是J2EE应用中一个关于横切关注点的很好的例子。在Spring AOP中,切面可以使用基于模式或者基于@Aspect注解的方式来实现

  • 连接点(JoinPoint)

在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。在SpringAOP中,一个连接点总是表示一个方法的执行

  • 通知(Advice)

在切面的某个特定的连接点上执行的动作。其中包括了AroundBeforeAfter等不同类型的通知。许多AOP框架(包括Spring)都是以拦截器做通知模型,并维护一个以连接点为中心的拦截器链

  • 切入点(PointCut)

匹配连接点的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(例如,当执行某个特定名称的方法时),切入点表达式如何和连接点匹配是AOP的核心:Spring缺省使用AspectJ切入点语法

  • 引入(Intorduction)

用来给一个类型声明额外的方法或属性(也被称为连接类型声明)。Spring允许引入新的接口(以及一个对应的实现)到任何被代理的对象。例如,你可以使用引入来使一个bean实现接口,以便简化缓存机制

  • 目标对象(Target Object)

被一个或者多个切面所通知的对象。也被称做被通知对象。既然Spring AOP是通过运行时代理实现的,这个对象永远是一个被代理对象

  • AOP代理(Aop proxy)

AOP框架创建的对象,用来实现切面契约(例如通知方法执行等等)。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理

  • 植入(weaving)

把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时(例如使用AspectJ编译器),类加载时和运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入

二、常用切入点指示符

1、execution表达式

用于匹配方法执行的连接点,属于方法级别

语法
execution(修饰符 返回值类型 方法名(参数)异常)

语法参数 描述
修饰符 可选,如public,protected,写在返回值前,任意修饰符填*号就可以
返回值类型 必选,可以使用*来代表任意返回值
方法名 必选,可以用*来代表任意方法
参数 ()代表是没有参数,(..)代表是匹配任意数量,任意类型的参数,当然也可以指定类型的参数进行匹配,如要接受一个String类型的参数,则(java.lang.String), 任意数量的String类型参数:(java.lang.String..)等等。。。
异常 可选,语法:throws 异常,异常是完整带包名,可以是多个,用逗号分隔

符号

符号 描述
* 匹配任意字符
.. 匹配多个包或者多个参数
+ 表示类及其子类

条件符

符号 描述
&&、and
||
!

案例

  • 拦截com.gj.web包下的所有子包里的任意类的任意方法
    execution(* com.gj.web..*.*(..))
  • 拦截com.gj.web.api.Test2Controller下的任意方法
    execution(* com.gj.web.api.Test2Controller.*(..))
  • 拦截任何修饰符为public的方法
    execution(public * * (..))
  • 拦截com.gj.web下的所有子包里的以ok开头的方法
    execution(* com.gj.web..*.ok*(..))
    更多用法大家可以根据语法自行设计,本文不在进行举例

2、@annotation

根据所应用的注解对方法进行过滤
语法
@annotation(注解全路径)
实例
对用了com.gj.annotations.Test注解的所有方法进行拦截
@annotation(com.gj.annotations.Test)

3、Within

根据类型(比如接口、类名或者包名过滤方法)进行拦截
语法
within(typeName)
示例

  • 对com.gj.web下的所有子包的所有方法进行拦截
    within(com.gj.web..*)
    更多用法可以根据语法自行设计

4、@Within

用于匹配所有持有指定注解类型内的方法,与within是有区别的,within是用于匹配指定类型内的方法执行,而@within是指定注解类型内的方法

5、bean

Spring AOP扩展的,AspectJ没有对于它的指示符,用于匹配特定名称的bean对象的执行方法
语法
bean(beanName)
示例
bean(testController)

三、AOP通知

在切面类中需要定义切面方法用于响应响应的目标方法,切面方法即为通知方法,通知方法需要用注解标识,AspectJ支持5种类型的通知注解

注解 描述
@Before 前置通知, 在方法执行之前执行
@After 后置通知, 在方法执行之后执行
@AfterReturn 返回通知, 在方法返回结果之后执行
@AfterThrowing 异常通知, 在方法抛出异常之后
@Around 环绕通知,围绕方法的执行

1、@Before

示例

    @Before("testCut()")
    public void cutProcess(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        System.out.println("注解方式AOP开始拦截, 当前拦截的方法名: " + method.getName());
    }

2、@After

示例

    @After("testCut()")
    public void after(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        System.out.println("注解方式AOP执行的方法 :"+method.getName()+" 执行完了");
    }

3、@AfterReturn

其中value表示切点方法,returning表示返回的结果放到result这个变量中
示例

    /**
     * returning属性指定连接点方法返回的结果放置在result变量中
     * @param joinPoint 连接点
     * @param result 返回结果
     */
    @AfterReturning(value = "testCut()",returning = "result")
    public void afterReturn(JoinPoint joinPoint, Object result) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        System.out.println("注解方式AOP拦截的方法执行成功, 进入返回通知拦截, 方法名为: "+method.getName()+", 返回结果为: "+result.toString());
    }

4、@AfterThrowing

其中value表示切点方法,throwing表示异常放到e这个变量
示例

    @AfterThrowing(value = "testCut()", throwing = "e")
    public void afterThrow(JoinPoint joinPoint, Exception e) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        System.out.println("注解方式AOP进入方法异常拦截, 方法名为: " + method.getName() + ", 异常信息为: " + e.getMessage());
    }

5、Around

示例

    @Around("testCut()")
    public Object testCutAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("注解方式AOP拦截开始进入环绕通知.......");
            Object proceed = joinPoint.proceed();
            System.out.println("准备退出环绕......");
            return proceed;
    }

四、SpringBoot中使用

1、导入依赖

common和swagger是附加的,读者不用必须的哈

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>cn.gjing</groupId>
    <artifactId>tools-common</artifactId>
    <version>1.0.4</version>
</dependency>
<dependency>
    <groupId>cn.gjing</groupId>
    <artifactId>tools-starter-swagger</artifactId>
    <version>1.0.9</version>
</dependency>

2、创建注解

注解用于进行AOP拦截

/**
 * @author Gjing
 **/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Test {
}

2、创建一个接口

/**
 * @author Gjing
 **/
@RestController
@RequestMapping("test1")
public class TestController {

    @GetMapping("/ok")
    @ApiOperation(value = "测试1", httpMethod = "GET")
    @ApiImplicitParam(name = "id", value = "id值", dataType = "int", paramType = "query")
    @Test
    @NotNull
    public String test2(Integer id) {
        return "ok";
    }

}

3、创建切面类

/**
 * @author Gjing
 **/
@Aspect
@Component
public class TestAspect {

    @Pointcut("@annotation(com.gj.annotations.Test)")
    public void testCut() {

    }

    @Before("testCut()")
    public void cutProcess(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        System.out.println("注解方式AOP开始拦截, 当前拦截的方法名: " + method.getName());
    }

    @After("testCut()")
    public void after(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        System.out.println("注解方式AOP执行的方法 :"+method.getName()+" 执行完了");
    }


    @Around("testCut()")
    public Object testCutAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("注解方式AOP拦截开始进入环绕通知.......");
            Object proceed = joinPoint.proceed();
            System.out.println("准备退出环绕......");
            return proceed;
    }

    /**
     * returning属性指定连接点方法返回的结果放置在result变量中
     * @param joinPoint 连接点
     * @param result 返回结果
     */
    @AfterReturning(value = "testCut()",returning = "result")
    public void afterReturn(JoinPoint joinPoint, Object result) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        System.out.println("注解方式AOP拦截的方法执行成功, 进入返回通知拦截, 方法名为: "+method.getName()+", 返回结果为: "+result.toString());
    }

    @AfterThrowing(value = "testCut()", throwing = "e")
    public void afterThrow(JoinPoint joinPoint, Exception e) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        System.out.println("注解方式AOP进入方法异常拦截, 方法名为: " + method.getName() + ", 异常信息为: " + e.getMessage());
    }

}

4、启动运行

  • 正常情况
    y.png
  • 异常情况
    error.png

本文到此就结束了,篇幅过长,如果有哪里有些错的地方欢迎各位评论区留言,项目Demo源码地址:SpringBoot-Demo

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