Spring - 4. AOP

AOP 面向切面编程

面向切面式编程, 会横切程序中的流程, 在不破坏原有代码结构的情况下, 将相同的业务逻辑(比如 Transaction, Security, Logging)应用到"切面的位置".

AOP 定义

  1. ADVICE, 定义 what and when 插入, Spring 提供以下时机:

    • Before, ADVICE 方法之前调用
    • After, ADVICE 方法之后调用, 不管成功或者异常
    • After-returning, 只在方法成功时调用
    • After-throwing, 只在方法抛出异常时调用
    • Around, 前后都会调用
  2. JOIN POINTS, 定义插入点

  3. POINTCUTS, 定义哪里插入, 匹配一个或者多个 JOIN POINT

  4. ASPECTS, 是 ADVICE 和 POINTCUT 的结合

  5. INTRODUCTIONS, 允许往已经存在的类中添加方法和属性

  6. WEAVING, 通过创建一个代理创新来将切面应用到目标对象. WEAVING 一般有三个时机:

    • 编译时: 切面在目标单位编译时应用,需要特殊的编译器支持, 如 AspectJ 的 weaving compiler.
    • 类加载时: 当目标对象字节码被加载到 JVM 时应用, AspectJ 5的 load-time weaving(LTW).
    • 运行时: 应用程序运行时动态创建代理, Spring AOP采用的方式

Spring AOP 支持

Spring 对 AOP 的支持特征体现在:

  1. 基于代理对象的经典 Spring AOP
  2. Pure-POJO 切面
  3. @AspectJ 注解驱动的切面
  4. 支持使用 AspectJ 切面(Spring 自身实现的 AOP 不支持构造函数和 Setter 切面)

前三种都是基于 Spring 所实现的 AOP. Spring AOP 只支持方法级别.

通过 pointcuts 选择 join points

定义 pointcuts

假如有一个接口:

	package concert;
	public interface Performance {
	  public void perform();
	}

那么如何才能在 perorm() 方法定义一个切入点呢?

	executioin(* concert.Performance.perform(..))

其中:

  • execution 表明这是在目标 方法(Spring AOP 是围绕方法的) 运行时触发

  • * concert.Performance.perform(..)标明方法

    • * 代表返回值为任意返回值
    • concert.Performance, 代表方法所在的类
    • perform是被触发方法的名称
    • (..)代表参数, ..代表任意参数

可以用 within()属性限制 pointcut 的范围:

	executioin(* concert.Performance.perform(..) && within(concert.*)

within 代表 concert 包及其子包下的任何方法执行.

从 pointcuts 中选择 bean

	 execution(* concert.Performance.perform()) and bean('woodstock')

只有 ID 为woodstock 的 bean 才需要插入切入点.

也可以用 !bean('woodstock') 来指定除了 ID 为 'woodstock'的 Bean 以外, 都需要插入切入点

创建切面

直接写明切入点

	@Before("execution(** concert.Performance.perform(..))")
	public void takeSeats() {
	  System.out.println("Taking seats");
	}

引用切入点

先定义 pointcut

	@Pointcut("execution(** concert.Performance.perform(..))")
	public void performance() {}

再在切面中引用 pointcut

	@Before("performance()")
	public void takeSeats() {
		System.out.println("Taking seats");
	}

启用切面

启用切面需要在 @Configuration 标注的类名上使用 @EnableAspectJAutoProxy.

或者是通过 XML:

	<?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/aop
	  http://www.springframework.org/schema/aop/spring-aop.xsd
	  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">
	<context:component-scan base-package="concert" />
	<aop:aspectj-autoproxy />
	    <bean class="concert.Audience" />
	</beans>

@Around 的用法

@Around 相当于做一个封装.

    @Aspect
    public class Audience {
        @Pointcut("execution(** concert.Performance.perform(..))")
        public void performance() {}

        @Around("performance()")
        public void watchPerformance(ProceedingJoinPoint jp) {
            try {
                System.out.println("Silencing cell phones");
                System.out.println("Taking seats");
                jp.proceed();
                System.out.println("CLAP CLAP CLAP!!!");
            } catch (Throwable e) {
                System.out.println("Demanding a refund");
            } 	
        }
    }

参数 @args()

    @Pointcut(
         "execution(* soundsystem.CompactDisc.playTrack(int))  && args(trackNumber)")
    public void trackPlayed(int trackNumber) {}


    @Before("trackPlayed(trackNumber)")
    public void countTrack(int trackNumber) {
        int currentCount = getPlayCount(trackNumber);
        trackCounts.put(trackNumber, currentCount + 1);
    }
  • playTrack(int) 代表接受一个 int 类型的参数.
  • args(trackNumber) 切点中 playTrack(int) 的参数别名(未必与代码中参数名一样)叫 trackNumber. 而名字trackNumber 也会作为 Advice 的参数.
posted @ 2017-02-04 21:25  still_water  阅读(450)  评论(0编辑  收藏  举报