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

 

posted @ 2017-02-26 21:33  陌上骚  阅读(8241)  评论(0编辑  收藏  举报