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));
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM