Spring实战(第四版)读书笔记11——在java中声明切面

1、切点

Spring使用AspectJ的切点表达式语言来定义切点,但是仅支持AspectJ切点指示器的一个子集:

编写切点例子:

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

Spring还引入了bean()指示器,可以限定切点只匹配特定的bean,例子:

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

2、切面

切面=切点+通知,通知定义何时以及工作的内容。

Spring使用AspectJ注解来声明通知方法:

@Pointcut注解能够在一个@AspectJ切面定义内定义可重用的切点。

定义切面的例子:

@Aspect
public class Audience {
	
	@Pointcut("execution(** concert.Performance.perform(..))")
	public void performance() {}
	
	@Before("performance()")
	public void silenceCellPhones() {
		System.out.println("Silencing cell phones");
	}
	
	@Before("performance()")
	public void takeSeats() {
		System.out.println("Taking seats");
	}
	
	@AfterReturning("performance()")
	public void applause() {
		System.out.println("CLAP CLAP CLAP!!!");
	}
	
	@AfterThrowing("performance()")
	public void demandRefund() {
		System.out.println("Demanding a refund");
	}
}

启动自动代理功能(javaConfig):

@Configuration
@EnableAspectJAutoProxy
@ComponentScan
public class ConcertConfig {
	
	@Bean
	public Audience audience() {
		return new Audience();
	}
}

启动自动代理功能(XML,首先声明aop命名空间):

<aop:aspectj-autoproxy />

Spring的AspectJ自动代理仅仅使用@AspectJ作为创建切面的指导,切面依然是基于代理的。在本质上,它依然是Spring基于代理的切面,尽管使用的是@AspectJ注解,仍然限于代理方法的调用。如果想利用AspectJ的所有能力,必须在运行时使用AspectJ并且不依赖Spring来创建基于代理的切面。

3、环绕通知

例子:

@Aspect
public class Audience {
	
	@Pointcut("excution(** 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");
		}
	}
}

对于proceed()方法,既可以多次调用实现重试逻辑,也可以不进行调用从而阻塞对被通知方法的访问。

4、通知中的参数

例子:

public class TrackCounter {
	private Map<Integer, Integer> trackCounts = new HashMap<Integer, Integer>();
	
	@Pointcut("excution(* 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);
	}
	
	public int getPlayCount(int trackNumber) {
		return trackCounts.containsKey(trackNumber)? trackCounts.get(trackNumber) : 0;
	}
}

切点定义中的参数与切点方法中的参数名称相一致,通过注解传递到通知方法中。

5、通过注解引入新功能

利用被称为引入的AOP概念,切面可以为Spring bean添加新方法。

例子:

public interface Encoreable{
    void performEncore();
}
@Aspect
public class EncoreableIntroducer{
    @DeclareParents(value="concert.Performance+",defaultImpl=DefaultEncoreable.class)
    public static Encoreable encoreable;
}

@DeclareParents注解由三部分组成:

  • value属性指定了哪种类型的bean要引入该接口,例子中,标记符后面的加号表示是Performance的所有子类型。
  • defaultImpl属性指定了为引入功能提供实现的类。
  • @DeclareParents注解所标注的静态属性指明了要引入的接口

猜你喜欢

转载自blog.csdn.net/Nemoosi/article/details/107138864