温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Springboot中切面编程AOP是怎样的

发布时间:2021-09-29 17:06:28 来源:亿速云 阅读:154 作者:柒染 栏目:大数据

这篇文章将为大家详细讲解有关Springboot中切面编程AOP是怎样的,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。

Springboot 切面编程AOP

AspectJ是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法。它和我们平时接触到的OOP都是编程的不同思想,OOP,即『面向对象编程』,它提倡的是将功能模块化,对象化,而AOP的思想,则不太一样,它提倡的是针对同一类问题的统一处理,当然,我们在实际编程过程中,不可能单纯的按照AOP或者OOP的思想来编程,很多时候,可能会混合多种编程思想,大家也不必要纠结该使用哪种思想,取百家之长,才是正道。

AspectJ实际上是对AOP编程思想的一个实践,当然,除了AspectJ以外,还有很多其它的AOP实现,例如ASMDex,但目前最好、最方便的,依然是AspectJ。

SpringAOP包含AspectJ,这是因为Spring AOP借助了AspectJ的语法,但底层技术用的还是Spring自己的。

Spring Aop采用的动态织入,而Aspectj是静态织入。

静态织入:指在编译时期就织入,即:编译出来的class文件,字节码就已经被织入了。

动态织入又分静动两种,静则指织入过程只在第一次调用时执行;动则指根据代码动态运行的中间状态来决定如何操作,每次调用Target的时候都执行。有不清楚的同学,可以自己补下基础的代理知识。

一、AOP相关术语

pointcut切入点

通常一个pointcut,会选取程序中的某些我们感兴趣的执行点,或者说是程序执行点的集合。

是一个(组)基于正则表达式的表达式。

joinPoint连接点

通过pointcut选取出来的集合中的具体的一个执行点,我们就叫JoinPoint.

target目标类

被aspectj横切的对象。我们所说的joinPoint就是Target的某一行,如方法开始执行的地方、方法类调用某个其他方法的代码。

advice通知

在选取出来的JoinPoint上要执行的操作、逻辑。

典型的 Advice 类型有 before、after 和 around,分别表示在目标方法执行之前、执行后和完全替代目标方法执行的代码。

@Before前置通知   			目标方法执行前
@After后置通知    			目标方法执行后,无论是否发生异常
@AfterReturning后置通知    	目标方法正常执行结束后,可以知道目标方法的返回值
@AfterThrowing后置通知		目标方法执行发生异常后,可以访问异常对象,进行打印。也可以指定异常发生时才执行
@Around环绕通知				包含上面4种

二、实例

2.1、引入jar包

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>


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

在完成了引入AOP依赖包后,不需要去做其他配置。AOP的默认配置属性中,spring.aop.auto属性默认是开启的,也就是说只要引入了AOP依赖后,默认已经增加了@EnableAspectJAutoProxy,不需要在程序主类中增加@EnableAspectJAutoProxy来启用。

2.2、业务模块

@RestController
@RequestMapping("/aopController")
public class AopController {

    @GetMapping(value = "/Curry")
    public void Curry(){
        System.out.println("库里上场打球了!!");
    }

    @GetMapping(value = "/Harden")
    public void Harden(){
        System.out.println("哈登上场打球了!!");
    }

    @GetMapping(value = "/Antetokounmpo")
    public void Antetokounmpo(){
        System.out.println("字母哥上场打球了!!");
    }


    @GetMapping(value = "/Jokic")
    public void Jokic(){
        System.out.println("约基奇上场打球了!!");
    }

    @GetMapping(value = "/Durant/{point}")
    public void Durant(@PathVariable("point")  int point){
        System.out.println("杜兰特上场打球了!!");
    }


}

2.3、定义切面类

@Aspect 注解 使之成为切面类

@Component 注解 把切面类加入到IOC容器中

@Aspect
@Component
public class CustomAspect {


    //定义切入点
    @Pointcut("execution(public * com.lee.springaop.controller.AopController.*(..)))")
    public void CustomAspect(){

    }


    @Pointcut("execution(public * com.lee.springaop.controller.AopController.Durant(..))")
    public void CustomAroundAspect(){

    }

    @Pointcut("execution(public * com.lee.springaop.controller.AopController.Durant(int)) && args(int))")
    public void CustomAroundAspect2(){

    }

    //前置通知
    @Before("CustomAspect()")
    public void doBeforeGame(){
        System.out.println("before : 经纪人正在处理球星赛前事务!");
    }

    //后置通知
    @After("CustomAspect()")
    public void doAfterGame(){
        System.out.println("after : 经纪人为球星表现疯狂鼓掌!");
    }

    //后置通知--正常返回
    @AfterReturning("CustomAspect()")
    public void doAfterReturningGame(){
        System.out.println("after returning : 返回通知:经纪人为球星表现疯狂鼓掌!");
    }

    //后置通知--发生异常
    @AfterThrowing("CustomAspect()")
    public void doAfterThrowingGame(){
        System.out.println("after throwing : 异常通知:球迷要求退票!");
    }


    //环绕通知
    @Around("CustomAroundAspect()")
    public void doAroundGame(ProceedingJoinPoint joinPoint) {
        try {

            System.out.println("Around before 1: 经纪人正在处理球星赛前事务");

            //参数
            Object[] args = joinPoint.getArgs();
            for (Object o : args){
                System.out.println("params 1: "+o.toString());
            }

            //target
            Object target = joinPoint.getTarget();
            System.out.println("target 1: "+target.toString());

            //进行下一个advice或目标方法调用
            joinPoint.proceed();

            System.out.println("Around after 1 : 返回通知:经纪人为球星表现疯狂鼓掌");
        } catch (Throwable throwable) {
            System.out.println("Aroung throwing 1: 异常通知:球迷要求退票");
        }
    }


    //环绕通知2
    @Around("CustomAroundAspect2() && args(point)")
    public void doAroundGame2(ProceedingJoinPoint joinPoint,int point) {
        try {

            System.out.println("Around before 2: 经纪人正在处理球星赛前事务");


            //进行下一个advice或目标方法调用
            joinPoint.proceed();

            System.out.println("球星本场得到 2 :" + point + "分" );

            System.out.println("Around after 2 : 返回通知:经纪人为球星表现疯狂鼓掌");
        } catch (Throwable throwable) {
            System.out.println("Aroung throwing 2 : 异常通知:球迷要求退票");
        }
    }


}

2.4、测试

浏览器访问:http://127.0.0.1:8080/aopController/Harden

Springboot中切面编程AOP是怎样的

浏览器访问:http://127.0.0.1:8080/aopController/Durant/80

Springboot中切面编程AOP是怎样的

三、相关语法

3.1、通配符

*		匹配任何数量的字符

..		匹配任何数量字符的重复
			如:类型模式中:匹配任何数量的子包;  方法参数模式中:匹配任何数量参数

+		匹配指定类型的子类型
			注:仅能作为后缀放在类型模式后边

eg:

java.lang.String    匹配String类型。

java.*.String       匹配java包下的任何“一级子包”下的String类型。匹配java.lang.String,但不匹配java.lang.ss.String

java..*             匹配java包及任何子包下的任何类型。如匹配java.lang.String、java.lang.annotation.Annotation 

java.lang.*ing      匹配任何java.lang包下的以ing结尾的类型。  

java.lang.Number+   匹配java.lang包下的任何Number的自类型。如匹配java.lang.Integer,也匹配java.math.BigInteger 


“()”表示方法没有任何参数
“(..)”表示匹配接受任意个参数的方法
“(..,java.lang.String)”表示匹配接受java.lang.String类型的参数结束,且其前边可以接受有任意个参数的方法;
“(java.lang.String,..)” 表示匹配接受java.lang.String类型的参数开始,且其后边可以接受任意个参数的方法;
“(*,java.lang.String)” 表示匹配接受java.lang.String类型的参数结束,且其前边接受有一个任意类型参数的方法;

3.2、表达式

&&  且

||	或

!	非

3.3、切入点指示符

3.3.1、指示符
execution	匹配方法执行的链接点

within		用于匹配指定类型内的方法执行

this		用于匹配当前AOP代理对象类型的执行方法

target		用于匹配当前目标对象类型的执行方法

args		用于匹配当前执行的方法传入的参数为指定类型的执行方法


@within			用于匹配所以持有指定注解类型内的方法

@target			用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解

@args			用于匹配当前执行的方法传入的参数持有指定注解的执行

@annotation		用于匹配当前执行方法持有指定注解的方法



bean					用于匹配特定名称的Bean对象的执行方法

reference pointcut		表示引用其他命名切入点
3.3.2、实例:
3.3.2.1、execution
模式描述
public * *(..)任何公共方法的执行
* cn.javass..IPointcutService.*()cn.javass包及所有子包下IPointcutService接口中的任何无参方法
* cn.javass..*. *(..)cn.javass包及所有子包下任何类的任何方法
* cn.javass..IPointcutService.*( *)cn.javass包及所有子包下IPointcutService接口的任何只有一个参数方法
* (!cn.javass..IPointcutService+).*(..)非“cn.javass包及所有子包下IPointcutService接口及子类型”的任何方法
* cn.javass..IPointcutService+.*()cn.javass包及所有子包下IPointcutService接口及子类型的的任何无参方法
* cn.javass..IPointcut*.test*(java.util.Date)cn.javass包及所有子包下IPointcut前缀类型的的以test开头的只有一个参数类型为java.util.Date的方法
* cn.javass..IPointcut*.test*(..) throws IllegalArgumentException, ArrayIndexOutOfBoundsExceptioncn.javass包及所有子包下IPointcut前缀类型的的任何方法,且抛出IllegalArgumentException和ArrayIndexOutOfBoundsException异常
* (cn.javass..IPointcutService+&& java.io.Serializable+).*(..)任何实现了cn.javass包及所有子包下IPointcutService接口和java.io.Serializable接口的类型的任何方法
@java.lang.Deprecated * *(..)任何持有@java.lang.Deprecated注解的方法
@java.lang.Deprecated @cn.javass..Secure * *(..)任何持有@java.lang.Deprecated和@cn.javass..Secure注解的方法
@(java.lang.Deprecated || cn.javass..Secure) * *(..)任何持有@java.lang.Deprecated或@ cn.javass..Secure注解的方法
(@cn.javass..Secure *) *(..)任何返回值类型持有@cn.javass..Secure的方法
* (@cn.javass..Secure ).(..)任何定义方法的类型持有@cn.javass..Secure的方法
* (@cn.javass..Secure () , @cn.javass..Secure (*))任何签名带有两个参数的方法,且这个两个参数都被@ Secure标记了,如public void test(@Secure String str1, @Secure String str1);
* *((@ cn.javass..Secure ))或 *(@ cn.javass..Secure *)任何带有一个参数的方法,且该参数类型持有@ cn.javass..Secure;如public void test(Model model);且Model类上持有@Secure注解
* *(@cn.javass..Secure (@cn.javass..Secure *) ,@ cn.javass..Secure (@cn.javass..Secure *))任何带有两个参数的方法,且这两个参数都被@ cn.javass..Secure标记了;且这两个参数的类型上都持有@ cn.javass..Secure;
* *(java.util.Map<cn.javass..Model, cn.javass..Model>, ..)任何带有一个java.util.Map参数的方法,且该参数类型是以< cn.javass..Model, cn.javass..Model >为泛型参数;注意只匹配第一个参数为java.util.Map,不包括子类型;如public void test(HashMap<Model, Model> map, String str);将不匹配,必须使用“* *(java.util.HashMap<cn.javass..Model,cn.javass..Model>, ..)”进行匹配;而public void test(Map map, int i);也将不匹配,因为泛型参数不匹配
* *(java.util.Collection<@cn.javass..Secure *>)任何带有一个参数(类型为java.util.Collection)的方法,且该参数类型是有一个泛型参数,该泛型参数类型上持有@cn.javass..Secure注解;如public void test(Collection<Model> collection);Model类型上持有@cn.javass..Secure
3.3.2.2、within

within(类型表达式) 匹配指定类内的方法执行

模式描述
within(cn.javass..*)cn.javass包及子包下的任何方法执行
within(cn.javass..IPointcutService+)cn.javass包或所有子包下IPointcutService类型及子类型的任何方法
within(@cn.javass..Secure *)持有cn.javass..Secure注解的任何类型的任何方法必须是在目标对象上声明这个注解,在接口上声明的对它不起作用
3.3.2.3、this

this(类型全限定名)匹配当前AOP代理对象类型的执行方法

模式描述
this(cn.javass.spring.chapter6.service.IPointcutService)当前AOP对象实现了 IPointcutService接口的任何方法
this(cn.javass.spring.chapter6.service.IIntroductionService)当前AOP对象实现了 IIntroductionService接口的任何方法也可能是引入接口
3.3.2.4、target

target(类型全限定名)匹配当前目标对象类型的执行方法

模式描述
target(cn.javass.spring.chapter6.service.IPointcutService)当前目标对象(非AOP对象)实现了 IPointcutService接口的任何方法
target(cn.javass.spring.chapter6.service.IIntroductionService)当前目标对象(非AOP对象) 实现了IIntroductionService 接口的任何方法不可能是引入接口
3.3.2.5、args

args(类型参数列表) 匹配当前执行的方法传入的参数为指定类型的执行方法

模式描述
args (java.io.Serializable,..)任何一个以接受“传入参数类型为 java.io.Serializable” 开头,且其后可跟任意个任意类型的参数的方法执行,args指定的参数类型是在运行时动态匹配的
3.3.2.6、@within

使用“@within(注解类型)”匹配所以持有指定注解类型内的方法;注解类型也必须是全限定类型名;

模式描述
@within cn.javass.spring.chapter6.Secure)任何目标对象对应的类型持有Secure注解的类方法;必须是在目标对象上声明这个注解,在接口上声明的对它不起作用
3.3.2.7、@target

使用“@target(注解类型)”匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;注解类型也必须是全限定类型名

模式描述
@target (cn.javass.spring.chapter6.Secure)任何目标对象持有Secure注解的类方法;必须是在目标对象上声明这个注解,在接口上声明的对它不起作用
3.3.2.8、@args

使用“@args(注解列表)”匹配当前执行的方法传入的参数持有指定注解的执行;注解类型也必须是全限定类型名;

模式描述
@args (cn.javass.spring.chapter6.Secure)任何一个只接受一个参数的方法,且方法运行时传入的参数持有注解 cn.javass.spring.chapter6.Secure;动态切入点,类似于arg指示符;
3.3.2.9、@annotation

使用“@annotation(注解类型)”匹配当前执行方法持有指定注解的方法;注解类型也必须是全限定类型名;

模式描述
@annotation(cn.javass.spring.chapter6.Secure )当前执行方法上持有注解 cn.javass.spring.chapter6.Secure将被匹配
3.3.2.10、bean

使用“bean(Bean id或名字通配符)”匹配特定名称的Bean对象的执行方法;Spring ASP扩展的,在AspectJ中无相应概念

模式描述
bean(*Service)匹配所有以Service命名(id或name)结尾的Bean
3.3.2.11、reference pointcut

表示引用其他命名切入点,只有@ApectJ风格支持,Schema风格不支持

package cn.javass.spring.chapter6.aop;  
import org.aspectj.lang.annotation.Aspect;  
import org.aspectj.lang.annotation.Pointcut;  
@Aspect  
public class ReferencePointcutAspect {  
    @Pointcut(value="execution(* *())")  
    public void pointcut() {}  
}

引用:

@Before(value = "cn.javass.spring.chapter6.aop.ReferencePointcutAspect.pointcut()")  
public void referencePointcutTest2(JoinPoint jp) {
    
}

关于Springboot中切面编程AOP是怎样的就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI