spring aop编程

2019-07-01  本文已影响0人  xzz4632
1. 引入aspectjweaver依赖.
<dependency>
    <groupId>aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.5.4</version>
</dependency>
2. 开启@AspectJ支持
2.1 java配置方式

@EnableAspectJAutoProxy

2.2 XML方式

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

3. 声明切面

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

3.1 java方式

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

3.2 xml方式
<aop:config>
    <aop:aspect  id="" ref="aspectBean">
        ...
    </aop:aspect>
</aop:config>

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

4. 声明切入点

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

4.1 java方式定义切入点表达式

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

4.2 xml方式定义切入点表达式
// 作为<aop:config>的子元素定义
// 在引用时通过通知元素的pointcut-ref属性引用这个切入点的id
<aop:config>
    <aop:pointcut id="businessService"
        expression="execution(* com.xyz.myapp.service.*.*(..))"/>
</aop:config>

// 也可以在<aop:aspect>元素中定义它并通过与上面相同的方式引用它.
<aop:config>
    <aop:aspect id="aspect05" ref="aspect05">
        <aop:pointcut expression="execution(String com.xzz.chapter05.section02.test05.Service05.*(String))" id="accountService"/>
        <aop:before method="before" pointcut-ref="accountService"/>
    </aop:aspect>
</aop:config>

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

5. 声明通知

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

5.1 通知类型

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

5.2 java方式定义

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

5.3 xml方式定义

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

<aop:before pointcut="" method="">
<aop:after-returning pointcut="" method="" returning=""/>
<aop:after-throwing pointcut="" method="" throwing=""/>
<aop:after pointcut="" method=""/>
<around pointcut="" method=""/>
6. 通知参数
6.1 JoinPoint

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

6.2 绑定参数

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

    @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=========");
    }
6.3 通知的执行顺序

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

7. 引入

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

7.1 步骤如下:
@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配置方式:

<aop:aspect id="usageTrackerAspect" ref="usageTracking">

    <aop:declare-parents
        types-matching="com.xzy.myapp.service.*+" //定义哪些类实现这个接口
        implement-interface="com.xyz.myapp.service.tracking.UsageTracked" // 接口
        default-impl="com.xyz.myapp.service.tracking.DefaultUsageTracked" //接口实现
    />

    <aop:before
        pointcut="com.xyz.myapp.SystemArchitecture.businessService()
            and this(usageTracked)"
            method="recordUsage"/>

</aop:aspect>

附录:

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

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

2. 表达式格式
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..*.*(..))
// 匹配com.xyz.service包中的任何方法
within(com.xyz.service.*)

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

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

猜你喜欢

热点阅读