Spring 配置使用 - AOP 通知参数

来源:互联网 发布:乐视max2 root软件 编辑:程序博客网 时间:2024/06/06 08:30

基本概念

若想要在通知方法获取被通知方法的参数共有两种方式:自动获取、手动指定。下面来探究下这两种方式的不同之处。


自动获取参数

在介绍 AOP 的通知类型时有提到过环绕通知,该通知类型可以通过参数 ProceedingJoinPoint 自动获取被通知方法的参数值并调用该方法。除了 ProceedingJoinPoint 外,还有 JoinPoint,JoinPoint.StaticPart 也都能自动获取被通知方法的参数。


1.JoinPoint

JoinPoint 即连接点,通过该参数可以获取连接点(被通知方法)的相关信息。

首先来看该接口的继承关系,如下图。可以发现在环绕通知中所使用的 ProceedingJoinPoint 就是继承自该接口。

这里写图片描述

再来看看该接口的定义:

public interface JoinPoint {    // 连接点的具体信息    String toString();    // 连接点的简单信息    String toShortString();    // 连接点的所有信息    String toLongString();    // AOP 代理对象    Object getThis();    // 目标对象       Object getTarget();    // 被通知方法的参数列表    Object[] getArgs();    // 连接点签名      Signature getSignature();    // 连接点方法所在类文件中的位置      SourceLocation getSourceLocation();    // 连接点类型      String getKind();   // 内部接口,暂不探究(下面会提到)    public interface StaticPart {        //... 省略部分代码    }    public interface EnclosingStaticPart extends StaticPart {}    // 返回连接点静态部分     StaticPart getStaticPart();    // getKind 方法的返回值    static String METHOD_EXECUTION = "method-execution";    static String METHOD_CALL = "method-call";    static String CONSTRUCTOR_EXECUTION = "constructor-execution";    static String CONSTRUCTOR_CALL = "constructor-call";    static String FIELD_GET = "field-get";    static String FIELD_SET = "field-set";    static String STATICINITIALIZATION = "staticinitialization";    static String PREINITIALIZATION = "preinitialization";    static String INITIALIZATION = "initialization";    static String EXCEPTION_HANDLER = "exception-handler";    static String SYNCHRONIZATION_LOCK = "lock";    static String SYNCHRONIZATION_UNLOCK = "unlock";    static String ADVICE_EXECUTION = "adviceexecution"; }

下面通过简单的例子来探究该接口的作用:

  • 定义一个 Bean,用它来表示连接点(joinpoint)的一部分
public class Animals {    public void setName(String name) {        System.out.println(name);    }}
  • 定义一个 Bean,用来表示切面
public class AnimalsAspect {    public void beforeAdvice() {        System.out.println(point.toString());        System.out.println(point.toShortString());        System.out.println(point.toLongString());        System.out.println(point.getThis().toString());        System.out.println(point.getTarget().toString());        System.out.println(point.getArgs()[0]);        System.out.println(point.getSignature());        System.out.println(point.getSourceLocation());        System.out.println(point.getKind());        System.out.println(point.getStaticPart());        System.out.println("before advice..." );    }}
  • 在 xml 文件中配置
<!-- 1.容器中注入 Bean --><bean id="joinpoint" class="com.demo.Animals" /><bean id="aspect" class="com.aop.AnimalsAspect"/><!-- 2.AOP 配置 --><aop:config>    <aop:aspect ref="aspect">        <aop:before method="beforeAdvice" pointcut="execution(* com.demo.Animals.setName(..))" />    </aop:aspect></aop:config>
  • 调用验证
String location = ...ApplicationContext factory = new FileSystemXmlApplicationContext(location);Animals animals = (Animals) factory.getBean("joinpoint");animals1.setName("cat");// 输出结果:// execution(void com.demo.Animals.setName(String))// execution(Animals.setName(..))// execution(public void com.demo.Animals.setName(java.lang.String))// com.demo.Animals@4245c97b// com.demo.Animals@4245c97b// cat// void com.demo.Animals.setName(String)// org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint$SourceLocationImpl@58e22f2b// method-execution// execution(void com.demo.Animals.setName(String))// before advice...// cat

2.ProceedingJoinPoint

该连接点接口只能用于环绕通知,并且该接口继承自 JoinPoint 接口。

public interface ProceedingJoinPoint extends JoinPoint {    void set$AroundClosure(AroundClosure arc);    // 调用无参的被通知方法    public Object proceed() throws Throwable;    // 调用带参的通知方法    public Object proceed(Object[] args) throws Throwable;}

3.JoinPoint.StaticPart

刚连接点是 Joinpoint 的内部接口,作用功能与 JoinPoint 基本一致。

public interface StaticPart {      Signature getSignature();       String getKind();       // 连接点标识    int getId();      String toString();      String toShortString();      String toLongString();    }  

手动指定参数

手动指定参数,即在配置切面时,需在切面的通知与切面的切点中明确指定参数。

下面通过一个实例来阐明:

  • 定义一个 Bean,用它来表示连接点(joinpoint)的一部分
public void Animals {    // 该被通知方法存在两个方法参数    public void setName(String param1 , String param2) {        System.out.println(param1 +"-"+ param2);    }}
  • 定义一个 Bean,用来表示切面
public class AnimalsAspect {    // 关键 -> 在通知中需明确指定参数名称    public void beforeAdvice(Object param1, Object param2) {        System.out.println(param1.toString() + "-" + param2.toString());    }}
  • 在 xml 文件中配置
<!-- 1.容器中注入 Bean --><bean id="joinpoint" class="com.demo.Animals" /><bean id="aspect" class="com.aop.AnimalsAspect"/><!-- 2.AOP 配置 --><aop:config>    <aop:aspect ref="aspect">        <!-- 关键 -> 在切点中也需明确指定参数名称 -->        <aop:before method="beforeAdvice" pointcut="execution(* com.demo.Animals.setName(..)) and args(param1,param2)" />    </aop:aspect></aop:config>
  • 调用验证
String location = ...ApplicationContext factory = new FileSystemXmlApplicationContext(location);Animals animals = (Animals) factory.getBean("joinpoint");animals1.setName("cat","dog");// 输出结果:// cat-dog// before advice...// cat-dog

混合使用

当同时采用自动获取参数与手动指定参数时,自动获取参数必须是第一个参数,即 JoinPoint 、ProceedingJoinPoint 等参数并需是通知方法定义的第一个参数。

  • 切面通知定义如下:
public void Animals {    // JoinPoint 必须是方法定义的第一个参数    public void setName(JoinPoint point , String param1,String param2) {        System.out.println(param1 +"-"+ param2);    }}
  • 在 xml 的配置同上。
0 0