为讲解例子,我们首先定义一个Performance接口:
1 package aoptest; 2 3 public interface Performance { 4 public void perform(); 5 }
再定义一个该接口的实现:
1 package aoptest; 2 3 public class PianoPerform implements Performance { 4 5 @Override 6 public void perform() { 7 // TODO Auto-generated method stub 8 System.out.println("i am playing piano"); 9 } 10 11 }
在创建切面之前,我们先来看一下切点表达式的用法,如图所示:
关于切点表达式的更多用法,可查看相关文档。
接着,我们使用注解定义一个切面,Audience类会在perform方法执行前后织入指定的方法:
1 package aoptest; 2 3 import org.aspectj.lang.annotation.Aspect; 4 import org.aspectj.lang.ProceedingJoinPoint; 5 import org.aspectj.lang.annotation.*; 6 7 @Aspect 8 public class Audience { 9 10 11 @Pointcut("execution(** aoptest.Performance.perform(..))") 12 public void performance() {} 13 //performance()方法的实际内容并不重要,在这里它是空的。 14 //其实该方法本身只是一个标识,供@Pointcut注解依附 15 //不这样做的话,就需要在每个方法前都使用这个长点的表达式 16 17 @Before("performance()") 18 public void silenceCellPhones() { 19 System.out.println("Slience cell phones"); 20 } 21 22 @Before("performance()") 23 public void takeSeats() { 24 System.out.println("takeSeats"); 25 } 26 27 @AfterReturning("performance()") 28 public void applause() { 29 System.out.println("applause"); 30 } 31 32 @AfterThrowing("performance()") 33 public void demandRefund() { 34 System.out.println("demandRefund"); 35 } 36 }
- @Before:通知方法会在目标方法调用之前调用
- @AfterReturning:通知方法在目标方法成功返回后调用
- @AfterThrowing:通知方法在目标方法抛出异常后调用
- @Around:通知方法会将目标方法封装起来
接着,进行测试,首先使用JavaConfig进行相关bean的配置:
1 package aoptest; 2 3 import org.springframework.context.annotation.Bean; 4 import org.springframework.context.annotation.ComponentScan; 5 import org.springframework.context.annotation.Configuration; 6 import org.springframework.context.annotation.EnableAspectJAutoProxy; 7 8 @Configuration 9 @EnableAspectJAutoProxy //启用aspectJ自动代理 10 @ComponentScan 11 public class AopConfig { 12 @Bean 13 public Audience audience() { 14 return new Audience(); 15 } 16 17 @Bean 18 Performance performance() { 19 return new PianoPerform(); 20 } 21 }
然后,创建测试类:
1 package aoptest; 2 3 import org.junit.Test; 4 import org.junit.runner.RunWith; 5 import org.springframework.beans.factory.annotation.Autowired; 6 import org.springframework.test.context.ContextConfiguration; 7 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 8 9 //用于在测试开始时自动创建Spring上下文 10 @RunWith(SpringJUnit4ClassRunner.class) 11 //告诉上下文需要在AopConfig中加载配置 12 @ContextConfiguration(classes = { AopConfig.class }) 13 public class PerformTest { 14 @Autowired 15 public Audience audience; 16 @Autowired 17 public Performance performance; 18 @Test 19 public void play() { 20 performance.perform(); 21 } 22 }
测试结果,符合预期:
现在,我们利用@Around创建环绕通知,重新实现切面,可以达到相同的效果:
package aoptest; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; @Aspect public class Audience { @Pointcut("execution(** aoptest.Performance.perform(..))") public void performance() {} //performance()方法的实际内容并不重要,在这里它是空的。 //其实该方法本身只是一个标识,供@Pointcut注解依附 //不这样做的话,就需要在每个方法前都使用这个长点的表达式 @Around("performance()") //ProceedingJoinPoint这个对象是必须有的,因为需要通过它来调用被通知的方法,使用proceed()方法 public void watchPerformance(ProceedingJoinPoint jp) { System.out.println("Slience cell phones"); System.out.println("takeSeats"); try { jp.proceed(); } catch (Throwable e) { System.out.println("demandRefund"); } System.out.println("applause"); } }
当然,你也可以不调用proceed()方法,从而阻塞对通知方法的访问。