AOP的几个主要概念

  • JointPoint:切入点,切面和过程的交点
  • PointCut:切入点的集合,如何描述这个集合是本文的主要内容
  • Aspect:切面,包含切入的类方法
  • Advice:切点的位置描述,包括BeforeAdvice、AfterAdvice等等
  • Target:被代理的对象,织入对象

AOP应用示例

使用XML配置

1
2
3
4
5
6
7
<bean id="aspectbean" class="xx.xx.aspectBean" />
<aop:config>
<aop:aspect ref="aspectbean" order="0" >
<aop:pointcut id="samplePointCut" expression="切入点语法" />
<aop:after-returning method="aspectmethod" pointcut-ref="samplePointCut" />
</aop:aspect>
</aop:config>

使用注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Component  
@Aspect
public class sampleAspectClass {

@Pointcut("切入点语法")
private void pointCutMethod() {
}

//声明后置通知
@AfterReturning(pointcut = "pointCutMethod()", returning = "returnValue")
public void doAfterReturning(JoinPoint point,Object returnValue) {
// do something ...
}
}

切入点语法描述

切入点语法可以有下列方式来定义或者通过&& || 和!的方式进行组合:

  • execution(…)
  • args(…)
  • target(…)
  • within(…)
  • annotation(…)

其中execution是最常见的一种,直接描述执行的方法,几个比较通用的语法有

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 任意的公有方法:
execution(public * *(..))

// 任何以"set"开头的方法:
execution(* set*(..))

// AccountService接口定义的所有方法:
execution(* com.xyz.service.AccountService.*(..))

// service包下的所有方法:
execution(* com.xyz.service.*.*(..))

// service包或其子包下的所有方法:
execution(* com.xyz.service..*.*(..))

rgs主要通过方法的参数来描述指定的方法,例如通过下面的表达式指定单个参数且参数在运行时为Serializable的方法进行切入:

1
args(java.io.Serializable)

target、within、 annotation这三个都和注解有关。target限制目标对象包含某个注解,within限制所有包含某个注解的类方法,annotation限制包含了某个注解的方法。target和within中传入的注解位于类上,而annotion中传入的注解位于方法上。

个人感觉有些场景用annotion会比较舒服、精确分配,尤其是在需要aop的方法名字没有什么共同点而且方法不是很多的情况下。

获取连接点上下文信息

很多时候进行切入需要相关的上下文信息,比如日志记录时需要在切面方法中知道当前织入的是哪个方法。AspectJ使用org.aspectj.lang.JoinPoint接口表示目标类连接点对象,如果是环绕增强时,使用org.aspectj.lang.ProceedingJoinPoint表示连接点对象,该类是JoinPoint的子接口。任何一个增强方法都可以通过将第一个入参声明为JoinPoint访问到连接点上下文的信息。这两个接口的主要方法如下:

  1. JoinPoint

    • Object[] getArgs():获取连接点方法运行时的入参列表;
    • Signature getSignature() :获取连接点的方法签名对象;
    • Object getTarget() :获取连接点所在的目标对象;
    • Object getThis() :获取代理对象本身;
  2. ProceedingJoinPoint

    ProceedingJoinPoint继承JoinPoint子接口,它新增了两个用于执行连接点方法的方法

    • Object proceed() throws java.lang.Throwable:通过反射执行目标对象的连接点处的方法;
    • Object proceed(Object[] args) throws java.lang.Throwable:通过反射执行目标对象连接点处的方法,不过使用新的入参替换原来的入参。

注意事项

spring现在使用aspectj来作为切面的解决方案,那么就需要aspectj相关的jar包,这个jar包一般来说越新越好,且与java版本有关,如果版本比较低,可能会出现”the @annotation pointcut expression is only supported at Java 5 compliance level or above”类似的错误,采用高版本的jar包即可。

在执行时如果因为切入点不是接口方法产生报错,则需要引入CGLib相关的jar包。