spring aop编程

1. 引入aspectjweaver依赖.

    aspectj
    aspectjweaver
    1.5.4

2. 开启@AspectJ支持
2.1 java配置方式

@EnableAspectJAutoProxy

2.2 XML方式


注: 在xml配置中, 要提供aop的命名空间,即引入spring-aop schema.

3. 声明切面

创建一个类, 在类中定义切面逻辑.

3.1 java方式

在常规类上添加@Aspect注解, 由于组件扫描不能自动发现这个注解, 所以还要加上@Component注解.

3.2 xml方式
  • 将常规类声明为一个bean.
  • 将这个bean声明为一个切面

    
        ...
    

注:所有的aspect元素都必须声明在元素之内.

4. 声明切入点

在spring aop中, 切入点是两部分组成的:

  • 一是在切面中定义的一个方法, 且方法的返回值类型必须是void.方法可以有参数, 方法体为空(仅为切入点时).
  • 二是切入点表达式.具体的表达式定义见附录一.
4.1 java方式定义切入点表达式

使用@Pointcut注解
注: java配置方式中可以使用&&, ||, !来组合多个切点表达式.

4.2 xml方式定义切入点表达式
  • 元素中定义expression属性. 可以作为的子元素定义, 也可以作为的子元素定义.
// 作为的子元素定义
// 在引用时通过通知元素的pointcut-ref属性引用这个切入点的id

    


// 也可以在元素中定义它并通过与上面相同的方式引用它.

    
        
        
    

注1:表达式中可用and, or, not来代替&&, ||, !
注2:xml配置中不能使用复合切入点(即不能将多个单独的切入点组合在一起使用).

5. 声明通知

通知的作用是在切入点处添加额外的业务逻辑.因此它是与切入点相关联的.

5.1 通知类型

通知有5种类型, 可以借用try-catch-finally模型来理解, 具体见附录二.

5.2 java方式定义

在通知注解中引入切入点, 也可在通知注解中直接使用切入点表达式.

  • @Before
  • @After
  • @AfterReturning
    可以通过returning属性定义返回值的参数名称, 在通知方法参数中用这个参数名接收返回值.
  • @AfterThrowing
    可以通过throwing属性定义异常参数, 在通知方法参数中定义可匹配指定的异常类型.
  • @Around
    通知方法的第一个参数必须是ProceedingJoinPoint类型, 调用它的proceed()方法会执行切入点方法.可以不调用, 也可以调用多次
5.3 xml方式定义

通知是作为元素的子元素定义的, 通过pointcut属性定义切入点表达式或通过pointcut-ref引用定义好的切入点.通过method属性指定通知方法.通过arg-names属性指定通知参数.

  • before

  • afterReturning

  • afterThrowing

  • after

  • around

6. 通知参数
6.1 JoinPoint

任何通知都可以声明`org.aspectj.lang.JoinPoint类型的参数作为它的第一个参数.
这个类提供了一些方法:

  • getArgs(): 返回连接点方法的参数值.
  • getThis(): 返回代理对象
  • getTarget(): 返回目标对象
  • getSignature(): 返回切入点方法签名
  • toString(): 返回切入点表达式
6.2 绑定参数

在通知定义中使用某些相关的参数.

  • 通过args绑定
    a. 在args标识符中定义参数名称, 使用..表示任何参数.
    b. 在切入点签名中定义参数类型
    c. 在通知方法参数中定义相关的参数类型
    @Pointcut("args(p)") // 定义参数名
    public void insert(Person p) {  // 定义参数类型
        
    }
    
    
    @Before("insert(p)") // 引用切入点
    public void before(Person p) { // 切入点定义的参数
        System.out.println(p.getName()); // 使用参数
        System.out.println("==========before=========");
    }
  • this, target, @within, @target, @annotation, @args也可用上面类似的方式来绑定参数.

  • 绑定泛型参数
    只需要在切入点定义中指定具体的参数类型即可.
    注: 这对集合类型的泛型是不能像上面那样定义的, 因为此时spring aop不会去检查集合中元素的类型,即无论你的集合定义的是何种具体类型,都会得到同样的结果, 如果要使用其正常工作, 只能用collection来接收参数, 然后手动检查集合中数据的类型.

  • 关于参数名称
    aop默认是使用相关表达式中定义的参数, 可以通过argNames属性显示指定参数名称, 也必须也表达式中定义的一致.

6.3 通知的执行顺序

在同一个连接点执行多个通知, 可通过@Order注解或实现Ordered接口, 定义的值
小的拥有相对高的优先级, 对于before通知, 最高优先级的通知最先执行, 对于after类的通知, 则是优先级最高的最后执行.

7. 引入

所谓引入就是给一个类添加新的方法或字段.即等于在一个类中添加一些属性或方法. 这通常是在为一些不能修改或不便于修改的类添加新的逻辑.

7.1 步骤如下:
  • 定义一个接口
  • 定义这个接口的实现类
  • 定义切面
    java配置方式:
@Aspect
public class UsageTracking {

    @DeclareParents(value="com.xzy.myapp.service.*+", defaultImpl=DefaultUsageTracked.class)
    public static UsageTracked mixin;

    @Before("com.xyz.myapp.SystemArchitecture.businessService() && this(usageTracked)")
    public void recordUsage(UsageTracked usageTracked) {
        usageTracked.incrementUseCount();
    }

}

xml配置方式:



    

    


附录:

一. 切入点表达式
1. 切点标识符
标识符 说明
execution 方法级别(连接点)匹配
within 类级别匹配,注意不能匹配与切面同包中的类
this 匹配到具体的某个类或接口(代理对象)
target 匹配到具体的某个类或接口(目标对象)
args 匹配到方法的参数类型
@target 目标对象所在类上具有给定的注解且注解的Retention为RUNTIME, 如果为CLASS则会抛出异常
@within 类上有指定类型的注解, 以上两种都可以
@args 指参数所在的类上具有指定类型的注解
@annotation 连接点方法上具有指定类型的注解
bean 通过id或名称指定的bean

以上标识符中都可以使用*作为通配符

2. 表达式格式
  • execution
execution(modifiers? ret-type declaring-type?method-name(param) throws-exp?)
// 带?部分为可选部分

modifiers: 方法修饰符, public, private, protected. 可选.
ret-type: 方法返回值类型, 必需.
declaring-type: 方法所在的类的全限定类名称, 可选.
method-name: 方法名, 必需.
param: 参数

a. (): 表示无参.
b. (..): 表示任何参数(0个或多个, 任意类型).
c. (): 表示一个参数(任意类型).
d. (
, String): 第一个参数为任意参数, 第二参数必须是String类型.

throws-exp: 表示方法抛出的异常类型(必须是全限定类名称), 可选.
以上各部分都可用*通配符进行全部可部分匹配.对于类型最好用全限定类名(可控).

// 匹配任意的public方法
execution(public * *(..))

// 匹配以set开头的方法
execution(* set*(..))

// 匹配在com.xyz.service.AccountService接口中定义的任何方法
execution(* com.xyz.service.AccountService.*(..))

// 匹配在com.xyz.service包中定义的任何方法
execution(* com.xyz.service.*.*(..))

// 匹配在com.xyz.service包及其子包中的任何方法
execution(* com.xyz.service..*.*(..))
  • within
// 匹配com.xyz.service包中的任何方法
within(com.xyz.service.*)

// 匹配com.xyz.service包及其子包中的方法
within(com.xyz.service..*)
  • this
// 代理对象实现了com.xyz.service.AccountService接口
this(com.xyz.service.AccountService)
  • target
// 目标对象实现了com.xyz.service.AccountService接口
target(com.xyz.service.AccountService)
  • args
// 只有一个参数且参数的类实现了Serializable接口
args(java.io.Serializable)
  • @target
// 目标对象上有@Transactional注解
@target(org.springframework.transaction.annotation.Transactional)
  • @within
// 目标对象上有@Transactional注解
@within(org.springframework.transaction.annotation.Transactional)
  • @annotation
// 方法上有@Transactional注解
@annotation(org.springframework.transaction.annotation.Transactional)
  • @args
// 参数类型上有@Classified注解
@args(com.xyz.security.Classified)
  • bean
// tradeService bean
bean(tradeService)

// 匹配所有以Service结尾的bean
bean(*Service)
二. 通知类型
  1. 通知类型
类型 说明
before 前置通知, 在进入切入点之前执行
afterReturning 后置返回通知, 在切入点正常执行完成后执行
afterThrowing 后置异常通知, 在切入点执行时抛出异常时执行
after 后置通知,无论切入点是否正常执行完, 都会执行.
around 环绕通知, 可在切入点前后都执行.

你可能感兴趣的:(spring aop编程)