Spring AOP基础

IOC和AOP是Spring的两大基石,AOP(面向切面编程),是一种编程范式,提供从另一个角度来考虑程序结构从而完善面向对象编程(OOP)。

在进行 OOP 开发时,都是基于对组件(比如类)进行开发,然后对组件进行组合,OOP 最大问题就是无法解耦组件进行开发。AOP 为开发者提供一种进行横切关注点(比如日志关注点)分离并织入的机制,把横切关注点分离,然后通过某种技术织入到系统中,从而无耦合的完成了我们的功能。

AOP世界中的居民

  • Joinpoint(连接点):指那些被拦截到的点。在Spring中只支持方法级别的连接点。
  • Pointcut(切入点):指需要配置的Joinpoint。
  • Advice(通知):指拦截到Joinpoint后要做的操作,通知分为前置通知/后置通知/异常通知/后置通知/环绕通知。
  • Aspect(切面):切入点和通知的结合。
  • Target(目标对象):需要被代理的对象。
  • Proxy(代理对象):目标对象被AOP 织入通知产生的对象。
  • Weaving(织入):指把通知应用到目标对象来创建代理对象的过程(Spring采用动态代理织入,AspectJ采用编译期织入和类装载期织入)。

AOP中的通知类型

  • 前置通知:在切入点选择的连接点处的方法之前执行的通知,该通知不影响正常程序执行流程,除非发生了异常。
  • 后置通知:在切入点选择的连接点处的方法正常执行完毕时执行的通知。
  • 返回通知:在切入点选择的连接点处的方法返回时执行的通知,不管抛没抛出异常都执行,类似于 Java 中的 finally 块。
  • 异常通知:在切入点选择的连接点处的方法抛出异常返回时执行的通知。
  • 环绕通知:环绕着在切入点选择的连接点处的方法所执行的通知,环绕通知可以在方法调用之前和之后自定义任何行为,并且可以决定是否执行连接点处的方法、替换返回值、抛出异常等等。
    Spring AOP基础_第1张图片

AOP程序示例

@Component
public interface HelloAop {
    void hello();
}

@Component
public class HelloAopImpl implements HelloAop{
    @Override
    public void hello() {
        System.out.println("---hello HelloAopImpl---");
    }
}

@Aspect
@Component
public class HelloAopAspect {
    @Before("execution(public void com.luo.aop.HelloAopImpl.hello())")
    public void beforeAdvice(JoinPoint point) {
        String methodName = point.getSignature().getName();
        System.out.println("beforeAdvice: " + methodName);
    }

    @After("execution(public void com.luo.aop.HelloAopImpl.hello())")
    public void afterAdvice(JoinPoint point) {
        String methodName = point.getSignature().getName();
        System.out.println("afterAdvice: " + methodName);
    }

    @AfterThrowing("execution(public void com.luo.aop.HelloAopImpl.hello())")
    public void afterThrowingAdvice(JoinPoint point) {
        String methodName = point.getSignature().getName();
        System.out.println("afterThrowingAdvice: " + methodName);
    }

    @AfterReturning("execution(public void com.luo.aop.HelloAopImpl.hello())")
    public void afterReturningAdvice(JoinPoint point) {
        String methodName = point.getSignature().getName();
        System.out.println("afterReturningAdvice: " + methodName);
    }

    @Around("execution(public void com.luo.aop.HelloAopImpl.hello())")
    public Object aroundAdvice(ProceedingJoinPoint point) {
        Object result = null;

        // 环绕通知(前通知)
        System.out.println("aroundAdvice start...");
        try {
            // 前置通知
            result = point.proceed(); // 目标方法执行
        } catch (Throwable throwable) {
            // 异常通知
            throwable.printStackTrace();
        }
        // 环绕通知(后通知)
        System.out.println("aroundAdvice end...");
        // 后置通知
        // 返回通知

        return result;
    }
}

public static void main(String[] args) {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("springConfig.xml");

    HelloAop helloAop = ctx.getBean(HelloAop.class);

    helloAop.hello();
}

spring配置文件:






Spring AOP基础_第2张图片
AOP通知输出结果

指定切面优先级

在同一个连接点上应用不止一个切面时, 除非明确指定, 否则它们的优先级是不确定的。切面的优先级可以通过实现 Ordered 接口或利用 @Order 注解指定。实现 Ordered 接口, getOrder() 方法的返回值越小, 优先级越高.若使用 @Order 注解, 序号出现在注解中。

@Aspect
@Order(0)
@Component
public class HelloAspect2 { }

@Aspect
@Order(1)
@Component
public class HelloAspect { }

参考资料:

  1. 《Spring揭秘》AOP章节
  2. Spring学习之AOP总结帖

你可能感兴趣的:(Spring AOP基础)