SpringAop的核心概念和使用,表达式匹配刨析,xml配置通知和环绕通知

AOP中的核心概念

 Pointcut(切入点)

一个项目中有很多类,每个类中有很多个方法。这么多个方法中,如何定位到需要增强功能的方法呢?靠的是切点

Aspect(切面)

  • 切面由切点和增强组成。

  • 切面=切入点+增强

  • 切面=切入点+方位信息+横切逻辑

  • 切面=连接点+横切逻辑

JoinPoint(连接点)

横切程序执行的特定位置,比如类中某个方法调用前、调用后,方法抛出异常后等,这些代码中的特定点就称为“连接点”。

Target(目标对象)

增强逻辑的目标类。比如未添加任何事务控制的AccountServiceImplNoTcf类

Weaving(织入)

织入是将增强逻辑/横切逻辑添加到目标类具体连接点上的过程,AOP像一台织布机,将目标类、增强或者引介通过AOP(其实就是动态代理技术)这台织布机天衣无缝地编织到一起。

Proxy(代理)

一个类被AOP织入增强后,就产出了一个代理类,它是融合了原类和增强逻辑的代理类。

Advice(通知/增强)

  • 增强的第一层意思就是你的横切逻辑代码(增强逻辑代码)

  • 在Spring中,增强除了用于描述横切逻辑外,包含一层意思就是横切逻辑执行的方位信息。刚刚说了切点只能定位到方法,在进一步使用方位信息就可以定位到我们感兴趣的连接点了(方法调用前、方法调用后还是方法抛出异常时等)。

Maven坐标

<!--aop的jar包支持-->
<dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-aop</artifactId>
       <version>5.1.12.RELEASE</version>
</dependency>
<!--aop 依赖支持-->
<dependency>
       <groupId>org.aspectj</groupId>
       <artifactId>aspectjweaver</artifactId>
       <version>1.8.9</version>
</dependency>
格式:
execution(
[modifiers-pattern] //修饰符匹配 可省略
ret-type-pattern //返回值匹配
[declaring-type-pattern](paraname) //类路径+方法名匹配+参数
[throws-pattern] //异常类型匹配
)
  • [modifiers-pattern]:修饰符匹配,例如public等修饰符列表中的修饰符。
  • ret-type-pattern:返回值匹配可为 * 或 void 或 具体返回值的全限定类名。必填
  • [declaring-type-pattern](paraname):格式 包名.类名.方法名(参数)
    • 没有参数则为空不填 methName()。
    • 有具体参数需要写具体参数的全限定类名
    • 一个参数可以使用 methName(*)
    • 若干参数使用 methName(..)
    • 若参数是lang包下数据类型,则省略全限定包名直接写数据类型
  • [throws-pattern]:方法异常信息

现在来看看几个例子

1)com.spring.service.UserServiceImpl包下的saveAll()方法,无返回值,修饰符public
execution(public void com.spring.service.UserServiceImpl.saveAll())

2)省略修饰符,com.spring.service.UserServiceImpl包下的deleteUser方法,参数为Integer 无返回值
execution(void com.spring.service.UserServiceImpl.deleteUser(Integer))

3)省略修饰符,com.spring.service.UserServiceImpl包下的updateUser方法,参数为User 返回值User
execution(com.spring.domain.User com.spring.service.UserServiceImpl.updateUser(com.spring.domain.User)

4)所有参数类型,所有包(有几层包写几个*.)类下的方法
execution(* *.*.*.UserServiceImpl.updateUser(com.spring.domain.User))

5)所有参数类型, ..表示当前包及其子包下所有 类的 所有方法,且此方法只有一个所有满足参数类型的方法
execution(* com..*.*(*))

6)所有参数累心,所有包下所有方法,且满足有参和无参,满足多个参数
execution(* *..*.*(..))
execution(* *(..)) 效果相同

注:第六种写法会把符合情况的所有包匹配到,所以通常情况下我们都是对业务层的方法进行增强,所以切入点表达式都是切到业务层实现类。
execution(* com.itheima.service.impl.*.*(..))

AOP常用四中通知类型

1.前置通知(before)

作用:用于配置前置通知。指定增强的方法在切入点方法之前执行。

执行时间点:切入点方法执行之前执行。

2.后置通知(afterReturn)

作用:用于配置后置通知。指定增强的方法在切入点方法之后执行。

执行时间点:切入点方法正常执行之后。抛出异常就不执行了。

3.异常通知(afterThrowing)

作用:用于配置异常通知,方法抛出异常执行。

执行时间点:切入点方法执行产生异常后执行。它和后置通知只能执行一个。

4.最终通知(after)

作用:用于配置最终通知,在方法执行完毕的最后执行

执行时间点:无论切入点方法执行时是否有异常,它都会在其后面执行。

5.AOP 环绕通知(around)

作用:用于配置环绕通知。它是 spring 框架为我们提供的一种可以在代码中手动控制增强代码什么时候执行的方式。

注意:通常情况下,环绕通知都是独立使用的

XML配置需要的标签

<!-- 作用 : 用于配置AOP -->
<aop:config>  
<!--标签作用: 声明一个切面
id属性 : 当前切面的唯一标识
ref : 当前切面功能增强的对象
-->
<aop:aspect id="" ref="logUtils"> 

<!--配置前置通知【在方法之前加功能】
<aop:before>前置通知标签
method属性 : 指的是要增强的功能方法
pointcut属性 : 切入点表达式
pointcut-ref属性 : 导入外部的切入点表达式
-->
<aop:before method="" pointcut=""/>

<!--配置后置通知【在方法之后加功能】-->
<aop:after-returning method="afterReturningPrintLog"pointcut="execution(* com.spring.service.UserServiceImpl.*(..))"/> 

<!--配置异常通知【在方法出现时候加功能】-->
<aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pointcutService"/>

<!--配置最终通知【在方法执行完毕,最终加功能】-->
<aop:after method="" pointcut=""/>

<!--配置环绕通知: 四合一通知 抽取相同表达式 id属性注入到上面的pointcut-ref属性-->
<aop:pointcut id="pointcutService" expression="execution(* com.spring.service.UserServiceImpl.*(..))"/>

代码实现和xml配置

有一个日志类

public class LogAopUtils {
    //在目标方法执行前
    public void beforePrintLog() {
        System.out.println("方法执行之前,输出日志");
    }
    //在目标方法执行之后,输出日志的方法
    public void afterReturningPrintLog() {
        System.out.println("方法执行之后,输出日志");
    }
    //在目标方法出现异常,输出日志的方法
    public void afterThrowingPrintLog() {
        System.out.println("方法执行过程出现了异常,输出日志");
    }
    //在目标方法最终时刻,输出日志的方法
    public void afterPrintLog() {
        System.out.println("方法执行最终,输出日志");
    }
/** * 环绕通知对应的功能 增强: 对应四合一 * 要求必须要传递一个参数: ProceedingJoinPoint */ public void aroundPrint(ProceedingJoinPoint joinPoint) { try { //前置通知 System.out.println("方法执行之前"); joinPoint.proceed(joinPoint.getArgs());//作用,执行被拦截的方法。类似于method.invoke() //后置通知 System.out.println("方法执行之后"); } catch (Throwable throwable) { throwable.printStackTrace(); //异常通知 System.out.println("方法出异常了"); } finally { //最终通知 System.out.println("方法执行最终节点"); } } }

https://www.cnblogs.com/xiaozhang666/p/12132408.html    ProceedingJoinPoint 详解

 xml配置

<context:component-scan base-package="com"></context:component-scan>
<bean id="logAopUtils" class="com.spring.utils.LogAopUtils"></bean>
<aop:config>
   <aop:aspect id="logAspect" ref="logAopUtils">
      <aop:before method="beforePrintLog" pointcut-ref="pointcutService"/>
      <aop:after-returning method="afterReturningPrintLog" pointcut-ref="pointcutService"/>
      <aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pointcutService"/>
      <aop:after method="afterPrintLog" pointcut-ref="pointService"/>
      <aop:pointcut id="pointcutService" expression="execution(* com.spring.service.UserServiceIml.*(..))"/>
    <!--配置环绕通知:  四合一通知-->
    <aop:around method="aroundPrint" pointcut-ref="pointcutService"/>
    </aop:aspect>
</aop:config>

通知图解

 环绕图解

原文地址:https://www.cnblogs.com/xiaozhang666/p/13641885.html