Spring系列(一)-切面

245 阅读3分钟

A、面向切面

AOP解决了通用功能点和业务逻辑分离开

DI有助于应用对象之间的解耦,AOP实现横切关注点与他们所影响的对象之间的解耦

看完能回答的问题

a、如何将一个类声明为切面?

b、Spring如何实现切面?

1、AOP基础信息

切面的常用术语:通知、切点、连接点

通知:定义了切面是什么以及何时使用,分为五种类型通知:前置、后置、返回、异常、环绕通知

连接点:应用程序中的需要织入通知的点

切点:定义了切面的何处,定义好之后会匹配上织入点的一个或者多个连接点,

切面:切点和通知的结合,总和,它是什么,在何时和何处完成其功能

织入:把切面应用到目标对象并创建新的代理对象的古城

目标对象生命周期的多个点织入:编译器、类加载器、运行期(Spring AOP就是这种形式织入的)

Spring在运行时通知对象

在代理类中包裹切面,代理类封装了目标类,拦截被通知方法的调用,再转发调用,代理拦截到调用时执行切面逻辑再调用目标bean

Spring支持方法级别的连接点

定义切点,选择连接点

定义切点的时候使用aspectJ表达式来进行操作,execution指示器是用来实际执行匹配的,其他的指示器用来限制所匹配的切点。@within、@target、@annotation、@args等等

2、编写切点:

@Aspect 定义一个类是切面

2.1、切点表达式:

@Pointcut("execution(public * spring.Performance.perform(..))")

以*号开始,不关心返回值类型;spring.Performance.perform 全限定名和方法名;(..)不关注调用perform时传入的参数

@Pointcut("execution(public * spring.Performance.perform(..)) && within(spring.*)")

仅匹配spring包下的方法被调用

&& and关系;|| 或关系;!not关系;

2.2、在切点中选择指定的bean

@Pointcut("execution(public * spring.Performance.perform(..)) && bean('wookstock')")

仅在bean为wookstock的时候织入切点

2.2.1、定义通知(使用注解表明应该在什么时候调用)

@After(通知方法会在目标方法返回或者抛出异常后调用)、@AfterReturning、@AfterThrowing

@Before

@Around(通知方法会将目标方法封装起来)

@Pointcut 定义一个切点表达式,在其他的定义通知里可以直接使用@Pointcut定义的切点方法

2.2.2、启动自动代理功能:

xml形式: <aop:aspectj-autoproxy proxy-target-class="true" /> 声明注解类

注解形式: @EnableAspectJAutoProxy

切面依然是基于代理的???这句话怎么理解,尽管使用的是@Aspect注解,但我们仍然限于代理方法的调用,代理方法是什么意思?

2.2.3、创建环绕通知

环绕通知顾名思义就是环绕在目标方法执行过程中的通知,执行前、执行返回后、执行异常返回后,一个顶好几个通知定义

  @Around("execution(* spring.Performance.perform(..))")
  public void watchPerforence(ProceedingJoinPoint pj) {
      try {
          // 表演前
          System.out.println("Silencing cell phones");
          System.out.println("Taking Seats");
          pj.proceed();  // 将控制权交给被通知的方法
          // 表演后
          System.out.println("CLAP CLAP CLAP!");
      } catch (Throwable e) {
          // 表演失败
          System.out.println("Demand a refund");
      }
  }

携带参数的切点:"execution(* spring.Performance.perform(int)) && args(trackNumber)

Spring发现一个bean使用了@Aspect注解时,Spring就会创建一个代理,然后将调用委托给被代理的bean或者被引入的实现

基于注解的配置 > 基于Java的配置 > 基于XML的配置

XML的配置: aop:after/before/around

<aop:aspectj-autoproxy> 启用@Aspect注解的驱动
    <aop:config>
        <aop:aspect ref="audience">
            <!-- 定义切点-->
            <aop:pointcut id="embark" expression="execution(* *.)"/>
            <!--前置通知-->
            <aop:before pointcut-ref="embark" method="XXX"/>
            <!--后置通知-->
            <aop:after pointcut-ref="embark" method="YYY"/>
        </aop:aspect>
    </aop:config>

多个方法协作的时候,要想进行通信,那么就是有一个成员变量,都可以调用,但是会存在线程问题,因为实例只有一个

asepctOf() 获取切面的引用