有時候,我們需要將被通知方法的實參傳遞給通知,這時就要用到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)); } }