4.2 通过切点来选择连接点

切点用于准确定位应该在什么地方应用切面的通知。{通知,切点}是切面的最基本元素。

通知:做什么,什么时候做

切点:在哪里做(缩小使用切面的范围)

AspectJ 指示器描述
arg()

限制连接点匹配参数为指定类型的执行方法

execution用于匹配时连接点的执行方法
this()限制连接点匹配AOP代理的bean引用为指定类型的类
target限制连接点匹配目标对象为指定类型的类
within限制连接点匹配指定的类型

 

4.2.1编写切点

package concert;

public interface Performance {
    public void perform();
}



execution(* concert.Performance.perform(..))

我们先有一个performance接口,当这个perform方法被调用的时候会触发通知。我们用下面的那个表达式来设置。

execution : 在方法执行时触发

*:返回类型任意

concert.Performance:方法所属的类

.perform:方法

(..)任意参数

execution(* concert.Performance.perform(..) && within(concert.*))

within代表concert包小面任意类的方法被调用时

execution(* concert.Performance.perform(..) && bean("woodstock"))

使用bean来限定什么bean的名字

4.3.1定义切面

我们定义了Performance这是目标类,就是我们需要把通知加上去的类。现在我们需要定义切面

CONFIG
@ComponentScan(basePackages = "concert")
//@ComponentScan(basePackageClasses = {CompactDisc.class, MediaPlayer.class})
@EnableAspectJAutoProxy //开启proxy
@Configuration
public class ConfigClass {

    @Bean
    public Audience audience(){
        return new Audience();
    } //注册切面为bean
}

@Component
public class Me implements Performance{
    @Override
    public void perform() {
        System.out.println("haha");
    }
}

@Aspect
public class Audience {

    @Before("execution(* concert.Performance.perform(..))")
    public void slienceCellPhone() {
        System.out.println("silencing cell phones");
    }
}

AspectJ自动代理都会为使用@Aspect注解的bean创建一个代理,就是audience,这个代理会围绕着所有该切面的切点所匹配的bean。

4.3.2 创建环绕通知

@Aspect
public class Audience {

    @Around("execution(* concert.Performance.perform(..))")
    public void slienceCellPhone(ProceedingJoinPoint joinPoint) {
        try {
            System.out.println("silencing cell phones");
            System.out.println("take seats");
            joinPoint.proceed();
            System.out.println("hahahahah");
        } catch (Throwable throwable) {
            System.out.println("demanding a refund");
            throwable.printStackTrace();
        }
    }
}

这里我们定义了一个环绕通知,joinPoint.proceed()方法可以调用被拦截的方法,这个方法之前的就是前置通知,后面就是后置通知。

4.3.3 处理通知中的参数

这个例子中我们想要统计这个track中的每首歌都播放了多少次。由于这个记录工作不应该属于这个磁盘的工作,所以,我们创建了一个切面来记录。这个切面的通知里面可以接收参数!!

@Configuration
@ComponentScan(basePackages = "aspectJ")
@EnableAspectJAutoProxy
public class AspectJConfig {

    @Bean
    public CompactDisc blankDisc() {
        List<String> track = new ArrayList<>();
        track.add("野区");
        track.add("晴天");

        return new BlankDisc(
                "十一月的肖邦",
                "周杰伦",
                track
        );
    }

    @Bean
    public TrackCounter trackCounter(){
        return new TrackCounter();
    }
}

@Getter
@Setter
@AllArgsConstructor
@Component
public class BlankDisc implements CompactDisc {

    private String title;
    private String artist;
    private List<String> tracks;

    public void playTrack(int i) {
        System.out.println(tracks.get(i));
    }

    public void play() {
        System.out.println("Playing " + title + " by " + artist);
        System.out.println("--------track------");
        for (int i = 0; i < tracks.size(); i++) {
            playTrack(i);
        }
    }
}

@Aspect
@Getter
public class TrackCounter {
    private Map<Integer, Integer> trackCounts = new HashMap<>();

//    @Pointcut("execution(* aspectJ.BlankDisc.playTrack(int)) && args(trackNumber)")
//    public void trackPlayed(int trackNumber) {
//    }

    @Before("execution(* aspectJ.BlankDisc.playTrack(int)) && args(trackNumber)")
    public void countTrack(int trackNumber) {
        int currentCount = getPlayCount(trackNumber);
        trackCounts.put(trackNumber, currentCount + 1);
    }
//    @Before("execution(* aspectJ.CompactDisc.playTrack(..))")
//    public void countTrack() {
//        System.out.println("hha");
//    }

    public int getPlayCount(int trackNumber) {
        return trackCounts.getOrDefault(trackNumber, 0);
    }
}

这里同样的,拦截playTrack方法,args()表明我们需要把传给playTrack方法的参数同样传给通知中去!!

要点:args(trackNumber)和countTrack(int trackNumber)的参数名字一样。

4.3.4 通过注解引入新功能

一些语言,如RUBY。可以不用直接修改对象或者类的定义就能够为对象或类增加新方法。

但是Java不是动态语言,一旦编译完成了,就很难添加新功能了。使用切面可以为SpringBean添加新方法。

在Spring中,切面只是实现了它们包装bean相同接口的代理。如果代理也可以暴露接口,那么看起来这个bean也实现了这个接口!

package concert;

public interface Encoreable {
    void performEncore();
}

假设,我们有一个新的接口,需要应用到Performance的实现中。假设我们可以获得所有实现了Performance的实现的类,手动添加这个接口的实现,这样非常麻烦,也不是所有的Performance都有这个特性的。也不是所有的第三方代码可以让你修改的。

这时候,我们可以使用aop切面来实现这个功能,非侵入式。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值