结合springboot来学习AOP

AOP即面向切面编程,一种编程范式(思想),指导开发者如何组织程序结构。

作用:在不惊动原始设计的基础上为其进行功能的增强。具有无侵入式。

一些定义:

1:连接点:即原始方法,也就是需要进行功能增强的方法;

2:通知:共性的方法,也就是功能增强的具体内容;

3:通知类:专门写通知的类,通知(方法)写在这个类里面,这个类就称作为通知类了;

4:切入点:指明哪些方法需要进行功能增强;

5;切面:通知和切入点之间,把通知和切入点连接起来,指明该切入点对应哪一个通知。也就是                   说:切面就是一个桥梁,把通知和切入点绑定起来了。

以SpringBoot举个简单例子:

第一步:导入AOP相关坐标:

        <!-- 引入aop支持 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

第二步:定义连接点:待会需要对此进行功能增强(记得添加@Component注解)

@Component
public class ConnectionPoint {

    //连接点(待会需要增强功能)
    public void spring() {
        System.out.println("spring is running...");
    }
}

第三步:创建通知类,并在此类中添加通知(方法),即为功能增强的具体内容,添加切入点以及切面把连接点和通知连接起来。

@Pointcut("execution(void com.aurora.aop.ConnectionPoint.spring())")为定义切入点,其中的

void com.aurora.aop.ConnectionPoint.spring())为:指定为ConnectionPoint类下的spring()方法做

功能增强。

@Before("method()")为定义切面,注解下面方法的内容便是增强功能的具体内容(通知)。其中的method()就是切入点的名称,就是@Pointcut注解下面方法的名称。注意@Before为:在需要增强的方法(连接点)的前面执行该功能增强的具体内容(通知)。最后加上@Component和
@Aspect注解。@Aspect注解就会让spring在扫描到此通知类的时候,知道这个类是要做功能的增强。

@Component
@Aspect
//通知类  通知类中定义通知   通知:共性的功能
public class MyAdvice {

    @Pointcut("execution(void com.aurora.aop.ConnectionPoint.spring())") //切入点:指明那些方法要进行增强功能
    private void method(){}

    @Before("method()") //切面:通知和切入点之间,把通知和切入点连接起来,指明该切入点对应哪一个通知
    public void print() { //此方法为通知
        System.out.println("public is running...");
    }
}

最后在测试类中测试:

@SpringBootTest
class AopApplicationTests {

    @Autowired
    private ConnectionPoint connectionPoint;
    @Test
    void testAOP() {
        connectionPoint.spring();
    }
}

运行结果:

可以看到:原本的spring()方法只有System.out.println("spring is running..."),但是最后却在此之前添加了System.out.println("public is running...")。所以最后运行结果如上图所示。
 

小细节:

在springboot环境下,由于存在spring-boot-autoconfigure依赖,默认会注入AopAutoConfiguration配置类,该类的作用等同于@EnableAspectJAutoProxy注解,所以在这种情况下可以不加@EnableAspectJAutoProxy注解,AopAutoConfiguration可以通过spring.aop.auto属性控制;所以我们无需在启动类上加上@EnableAspectJAutoProxy注解。

接下来具体来学习AOP
一:切入点表达式:

就是上文中:@Pointcut("execution(void com.aurora.aop.ConnectionPoint.spring())")中的execution(void com.aurora.aop.ConnectionPoint.spring())

切入点表达式标准格式:动作关键字(访问修饰符  返回值  包名.类/接口名.方法名(参数名)异常名)

例如:

execution(public User com.aurora.service.UserService.findById(int))

动作关键字:描述切入点的行为动作,例如execution表示执行到指定的连入点(以后大部分用这个)。

其他的不在此赘述了。

此外,我们可以使用通配符描述切入点,达到快速描述的目的,以及冗余的描述。

*:单个字符的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现。

如:

execution(public * com.aurora.*.UserService.fing*(*))

表示:匹配com.aurora包下的任意包中的UserService类或接口中所有以find开头的带有一个参数的方法。

..:多个连续的字符,可以独立出现,常用于简化包名与参数的书写。

如:

execution(public User com..UserService.findById(..))

表示:匹配com包下的任意包中的UserService类或接口中所有名称为findById的方法。

+:专用于匹配子类类型。

如:

execution(* *..*Service+.*(..))

表示:任意返回值,任意包下面以Service结尾的类或接口的子类的任意方法任意参数。

二:AOP通知类型

在前面我们使用的是@Before注解,但是还有其他注解代表着在原始方法的什么位置进行增强功能。定义切面的所有注解总共有5种:

1:前置通知@Before):在原始方法被调用之前调用通知功能;

2:后置通知@After):在原始方法完成之后调用通知,此时不会关心方法的输出是什么;

3:环绕通知@Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。作为重点

4:返回通知@After-returning):在目标方法成功执行之后调用通知;了解就好

5:异常通知@After-throwing):在目标方法抛出异常后调用通知;了解就好

我们来重点学环绕通知@Around)。

示例:

1:定义连接点:

@Component
public class ConnectionPoint {

    //连接点(待会需要增强功能)
    public void spring() {
        System.out.println("spring is running...");
    }
}

2:创建通知类,添加通知,添加切入点,添加切面。把连接点和通知连接起来

@Component
@Aspect
public class MyAdvice {

    //切入点
    @Pointcut("execution(void com.aurora.aop.ConnectionPoint.spring())")
    private void method(){}


    //切面
    @Around("method()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("around before..."); //通知

        Object returnValue = proceedingJoinPoint.proceed();//表示对原始操作的调用

        System.out.println("around after...");

        return returnValue;//把原始方法的返回值,给返回回去
    }
}

运行结果:

在原始方法前加了一句System.out.println("around before..."),在原始方法的后面加了一句System.out.println("around after..."),所以最后的运行结果为上图所示。

@Around注意事项
1:环绕通知必须依赖形参ProceedingJoinPoint才能实现对原始方法的调用,进而实现原始方法调用前后同时添加通知;

2:通知中如果未使用ProceedingJoinPoint对原始方法进行调用将跳过原始方法的执行;

3:对原始方法的调用可以不接收返回值,通知方法设置成void即可,如果接收返回值,必须设定为Object类型;

4:原始方法的返回值如果是void类型,通知方法的返回值类型可以设置成void,也可以设置成Object;

5:由于无法预知原始方法运行后是否会抛出异常,因此环绕通知方法必须抛出Throwable对象;

深入了解ProceedingJoinPointJoinPoint

ProceedingJoinPoint对象是JoinPoint的子接口,该对象只用在@Around的切面方法中。是

JoinPoint的基础上暴露出 proceed() 这个方法。proceed()很重要,作用就是调用原始方法。

Object returnValue = proceedingJoinPoint.proceed();//表示对原始操作的调用

proceed()方法的返回值就是原始方法的返回值。

通过JpointPoint对象可以获取到下面信息

1:返回目标对象,即被代理的对象

 Object target = proceedingJoinPoint.getTarget();

2:获取连接点的所有参数名称

MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
String[] parameterNames = methodSignature.getParameterNames();

3:获取连接点里面的所有参数值

Object[] args = joinPoint.getArgs();

4:获取连接点的签名 

 Signature signature = joinPoint.getSignature();
 MethodSignature methodSignature = (MethodSignature) signature;

还有其他一些方法,以后遇到了在idea中很容易就能看到。

最后:贴下个人理解的AOP的关系图:


 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值