Spring AOP:args(),向通知传递数据的方法
有时候,我们需要将被通知方法的实参传递给通知,这时就要用到args()了。
这个范例中,假设有一个CD类、Player类,而CD只记录了CD的音轨信息,Player只实现了CD的播放功能。如果我们需要记录音轨的播放次数,当然可以通过CD或Player来实现,但是,记录次数这个功能是否应该由它们来实现?这点有待商讨。在这里,通过切点的参数传递,在不破坏CD或Player的结构的前提下,来实现这个功能。
定义 CD 类:
@Component public class CD { private String title; private List<String> tracks; public CD(){} // 如果自定义了构造方法,必须显式地定义默认构造方法,否则 Spring 无法实现自动注入 public CD(String title, List<String> tracks) { this.title = title; this.tracks = tracks; } public List<String> getTracks() { return tracks; } @Override public String toString() { return "CD{" + "title='" + title + '\'' + ", tracks=" + tracks + '}'; } }
定义 Player 类:
@Component public class Player { @Autowired private CD cd; public void play() { System.out.println(cd); } public void play(int index) { System.out.println(cd.getTracks().get(index-1) + " is playing...."); } }
定义一个切面类,实现记录播放次数的功能:
@Component @Aspect public class TrackCounter { private Map<Integer, Integer> map = new HashMap<>(); // execution(...play(int))的 int 是被通知的方法的获得的参数的类型 // 通过 && args(trackNumber) 表示被通知方法的实参也将传递给通知方法 @Pointcut("execution(* com.san.spring.aop.Player.play(int)) && args(trackNumber)") public void pointcut(int trackNumber) { // 形参名必须和 args()一致 } // @Around("pointcut(trackNumber)")中的 "trackNumber" // 不必与 args() 相同 ,可以另外命名的,但必须保证本通知内一致即可。 @Around("pointcut(trackNumber)") public void countTrack(ProceedingJoinPoint pjp, int trackNumber) { try { pjp.proceed(); //调用被通知方法 // 每次调用被通知方法成功之后,音轨的播放次数+1 int currentCount = getTrackCurrentCount(trackNumber); map.put(trackNumber, ++currentCount); } catch (Throwable e) { // 调用出现异常后的代码 } } public int getTrackCurrentCount(int trackNumber) { return map.containsKey(trackNumber) ? map.get(trackNumber) : 0; } }
定义一个SpringConfig配置类:
@Configuration @ComponentScan @EnableAspectJAutoProxy public class SpringConfig { /* 由于CD类中有两个构造方法,Spring在匹配 bean 时出现冲突,所以必须显式指定一个bean。 否则将出现异常,大概就是说,我只一个 bean 就够了,但给我两个,叫我怎么选啊: No qualifying bean of type 'com.san.spring.aop.CD' available: expected single matching bean but found 2: CD,setCD */ @Bean @Primary public CD setCD(){ String title = "唐朝"; List<String> tracks = new ArrayList<>(); tracks.add("梦回唐朝"); tracks.add("太阳"); tracks.add("九拍"); tracks.add("天堂"); tracks.add("选择"); tracks.add("飞翔鸟"); tracks.add("世纪末之梦"); tracks.add("月梦"); tracks.add("不要逃避"); tracks.add("传说"); return new CD(title, tracks); } }
测试:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SpringConfig.class) public class AopTest { @Autowired private Player player; @Autowired private TrackCounter trackCounter; @Test public void testTrackCounter() { // 执行 play()一次,该音轨的播放次数加1 player.play(1); player.play(1); player.play(2); player.play(3); player.play(5); // 与期待的次数一致,则测试通过 assertEquals(2, trackCounter.getTrackCurrentCount(1)); assertEquals(1, trackCounter.getTrackCurrentCount(2)); assertEquals(1, trackCounter.getTrackCurrentCount(3)); assertEquals(0, trackCounter.getTrackCurrentCount(4)); assertEquals(1, trackCounter.getTrackCurrentCount(5)); } }