spring aop介绍

介绍

  AOP(Aspect-Oriented Programming), 即 面向切面编程, 它与 OOP( Object-Oriented Programming, 面向对象编程) 相辅相成, 提供了与 OOP 不同的抽象软件结构的视角。在 OOP 中, 我们以类(class)作为我们的基本单元, 而 AOP 中的基本单元是 Aspect(切面)。

aop术语

Aspect(切面)

  aspect 由 pointcount 和 advice 组成, 它既包含了横切逻辑的定义, 也包括了连接点的定义,即它是什么,在何时和何处完成什么功能。Spring AOP就是负责实施切面的框架, 它将切面所定义的横切逻辑织入到切面所指定的连接点中。

advice(增强/通知)

  由 aspect 添加到特定的 join point(即满足 point cut 规则的 join point) 的一段代码,即切面需要完成的工作,同时通知还确定了通知何时被调用,以下为通知的具体类型

  • before advice, 在 join point 前被执行的 advice. 虽然 before advice 是在 join point 前被执行, 但是它并不能够阻止 join point 的执行, 除非发生了异常(即我们在 before advice 代码中, 不能人为地决定是否继续执行 join point 中的代码)
  • after return advice, 在一个 join point 正常返回后执行的 advice
  • after throwing advice, 当一个 join point 抛出异常后执行的 advice
  • after(final) advice, 无论一个 join point 是正常退出还是发生了异常, 都会被执行的 advice.
  • around advice, 在 join point 前和 joint point 退出后都执行的 advice. 这个是最常用的 advice.
join point(连接点)

  表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。

point cut(切点)

  符合某种规则的一系列连接点,Advice 是和特定的 point cut 关联的, 并且在 point cut 相匹配的 join point 中执行。在 Spring 中, 所有的方法都可以认为是 joinpoint, 但是我们并不希望在所有的方法上都添加 Advice, 而 pointcut 的作用就是提供一组规则(使用 AspectJ pointcut expression language 来描述) 来匹配joinpoint, 给满足规则的 joinpoint 添加 Advice。

切入点表达式

  Spring AOP支持在切入点表达式中使用如下的切入点指示符:

  • execution - 匹配方法执行的连接点,这是你将会用到的Spring的最主要的切入点指示符。
//使用execution()指示器选择com.hewenkai.code.spring.aop.Meal的eat()方法
//execution:在方法执行时触发
//public:public方法
//*:返回任意类型
//com.code.spring.aop.Meal:接口/类名
//eat(..):eat方法,参数为任意参数
execution(public * com.code.spring.aop.Meal.eat(..))
  • within - 限定匹配特定类型的连接点(在使用Spring AOP的时候,在匹配的类型中定义的方法的执行)。
  • this - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中bean reference(Spring AOP 代理)是指定类型的实例。
  • target - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中目标对象(被代理的应用对象)是指定类型的实例。
  • args - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中参数是指定类型的实例。
  • @target - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中正执行对象的类持有指定类型的注解。
  • @args - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中实际传入参数的运行时类型持有指定类型的注解。
  • @within - 限定匹配特定的连接点,其中连接点所在类型已指定注解(在使用Spring AOP的时候,所执行的方法所在类型已指定注解)。
  • @annotation - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中连接点的主题持有指定的注解。
      其中execution使用最频繁,即某方法执行时进行切入。定义切入点中有一个重要的知识,即切入点表达式,我们一会在解释怎么写切入点表达式。切入点意思就是在什么时候切入什么方法,定义一个切入点就相当于定义了一个“变量”,具体什么时间使用这个变量就需要一个通知。即将切面与目标对象连接起来。
Weaving(织入)

   将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程。

  • 编译器织入, 这要求有特殊的Java编译器。
  • 类装载期织入, 这需要有特殊的类装载器。
  • 动态代理织入, 在运行期为目标类添加增强(Advice)生成子类的方式。
    Spring 采用动态代理织入, 而AspectJ采用编译器织入和类装载期织入。
Target(目标对象)

  织入 advice 的目标对象. 目标对象也被称为 advised object.
因为 Spring AOP 使用运行时代理的方式来实现 aspect, 因此 adviced object 总是一个代理对象(proxied object)
注意, adviced object 指的不是原来的类, 而是织入 advice 后所产生的代理类。

introduction(引入)

  为一个类型添加额外的方法或字段. Spring AOP 允许我们为 目标对象 引入新的接口(和对应的实现)。

示例

  首先在spring配置文件中开启aop:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:annotation-config/>
    <context:component-scan base-package="com.code.spring"/>
    <aop:aspectj-autoproxy/>
</beans>

  定义一个Meal接口,并为其实现了Lunch和Breakfast两个实现类:

public interface Meal {
    void eat();
}
@Component
public class Lunch implements Meal {

    @Override
    public void eat() {
        System.out.println("eat lunch");
    }
}
@Component
public class Breakfast implements Meal {
    @Override
    public void eat() {
        System.out.println("eat breakfast");
    }
}

  其次定义一个前置通知:

@Aspect
@Component
public class WashHand {
    @Pointcut(value = "execution(public * com.code.spring.aop.Meal.eat(..))")
    private void pointCut() {

    }

    @Before("pointCut()")
    public void washHand() {
        System.out.println("you need to wash hand first!!");
    }
}

  再定义一个环绕通知:

@Aspect
@Component
public class ChopSticks {
    @Pointcut(value = "execution(public * com.code.spring.aop.Meal.eat(..))")
    private void pointCut() {

    }

    @Around("pointCut()")
    public void action(ProceedingJoinPoint joinPoint) {
        try {
            System.out.println("pick up the chopsticks");
            //被aop代理的对象实际执行
            joinPoint.proceed();
            System.out.println("put down the chopsticks");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }
}

  最后编写执行代码:

@Component
public class SpringAopDemo {
    @Autowired
    @Qualifier("lunch")
    private Meal lunch;

    @Autowired
    @Qualifier("breakfast")
    private Meal breakfast;

    public void aopTest() {
        lunch.eat();
        breakfast.eat();
    }
}

  执行的结果如下:

pick up the chopsticks
you need to wash hand first!!
eat lunch
put down the chopsticks
pick up the chopsticks
you need to wash hand first!!
eat breakfast
put down the chopsticks

  可以看出执行的顺序如下:首先执行环绕通知里再被代理对象执行之前的代码,再执行前置通知,之后才会执行被代理对象的代码,最后执行被代理对象执行之后的代码,由此可见Around的优先级是大于Before的。实际上,在spring执行aop的顺序如下:
Around>Before=After>AfterReturning=AfterThrowing

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值