03.AOP

2020-04-10  本文已影响0人  apieceof2_d368

面向切面

假设这样的情况, 你有三个类, 需要这三个类都有 "日志" 的功能. 如果用一般面向对象的思维来做的话 , 也许需要这三个类都实现一个 "日志" 的接口, 或者继承一个父类, 这三个类中的方法的时候调用父类或接口中的日志方法.

这样听起来就很麻烦, 更好的方法是, 利用代理, 让一个代理类继承目标类的接口(为了让这个代理类拥有目标类的基本功能), 然后在调用方法的时候同时调用日志方法.

代理设计模式是Spring AOP 的核心. 如果使用了AOP 代理Bean, 当调用一个 Bean 的方法时, Spring 会拦截这个调用, 给这个方法的某一步(执行前, 返回前, 执行后, 抛出错误后, 或者全部)加上需要的功能. 再进行调用

AOP 的一些名词

了解了面向切面, 就应该了解一下有关的术语.

Spring对AOP的支持

第一种太经典了, 书里没讲

纯 POJO 切面用 XML 配置

@AspectJ注解挺好, 可以用注解完成, 不用XML

前三种都是基于代理的 AOP, 功能上仅限于方法拦截, 如果对 AOP 的需求有构造器或属性拦截. 或者要代理的目标类没有接口. 那么需要考虑 AspectJ

通过切点来选择连接点

Spring 借助 AspectJ 切点表达式来定义 Spring 切面.

比如说, 要给一个目标类的方法加上一个通知, 在 AOP 中不需要对目标类进行修改. 要编写一个类, 在这个类中定义切点, 再写一个方法作为通知, Spring 会把这个通知应用到切点所定位到的目标类方法中去

切点怎么写

假如有一个目标类, 他的接口是这样的

public interface Perfoormance{
    public void perform();
}

需要给 perform() 方法加上通知. AspectJ 表达式如下

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

第一个 * 表示 返回类型随意, concert.Performance.perform 表示方法名, 两个英文句号表示参数随意

编写切面

@Aspect
public class Audience{
    @Before("execution(** concert.Performance.perform(..))")
    public void silenceCellPhones(){
        
        System.out.println("Silencing cell phones");
    }
    
    @After("execution(** concert.Performance.perform(..))")
    public void after(){
        
        System.out.println("after");
    }
    
    @AfterReturning("execution(** concert.Performance.perform(..))")
    public void afterReturning(){
        
        System.out.println("after returning");
    }
    
    @AfterThrowing("execution(** concert.Performance.perform(..))")
    public void afterThrowing(){
        
        System.out.println("after Throwing");
    }
}

从这里看出来, 通知类型有before, after, afterreturning afterthrowing

如果要重复使用切点可以这样:

@Aspect
public class Audience{
    @Pointcut("execution(** concert.Performance.perform(..))")
    public void performance();
    
    @Before("performance")
    public void before(){
        System.out.println("before");
    }
}

完成以后, 还需要再配置文件中开启 AspectJ 自动代理

@Configuration
@EnableAspectJAutoProxy
@ComponentScan
public class Configuration(){}

用 XML 开启 AspectJ 自动代理

<aop:aspectj-autoproxy />

创建环绕通知

前面提到的几种通知类型, 都只是再方法的某一个位置进行通知. 而环绕通知是可以对方法全方位多角度地代理

@Aspect
public class Audience{
    // 这里声明一个 performance 的切点
    @Around("performance()")
    public void around(ProceedingJoinPoing jp){
        try{
            System.out.println("before");
            jp.proceed();
        }catch(Throwable e){
            // ..
            System.out.println("afterThrowing")
        }finally{
            System.out.println("After")
        }
    }
}

jp.proceed() 就是在运行原始方法, 在这行代码前面的部分相当于 before 通知, catch 中相当于 afterThrowing(); finally 中的相当于 after, 这里无法表现出 afterreturing, 如果如果报错, afterreturing是不会运行的

有参数的情况

如果目标类要被代理的方法有参数, 应该这么写

表达式

execution(* soundsystem.CompactDisc.playTract(int)) && args(trackNumber)

通知

@Before("pointcut(trackNumber)")
public void countTrack(int trackNumber){
    //...
}

首先, 表达式要匹配到这个有参数的方法, 然后, 方法的 @Before 标签要有一个参数, 这个参数是参数名, 方法的参数也需要接收一个参数名相同的参数.

上一篇下一篇

猜你喜欢

热点阅读