Spring学习笔记:AOP基础-动态代理

背景

首先根据一个例子,阐述一下AOP(面向切面编程)使用的背景。
假设有一个业务方法,流程大致如下:

Created with Raphaël 2.1.2 打印log 执行业务逻辑 打印log并计算执行时间

但是这样的代码有两个有待优化的地方,其一,我们希望业务类只专注于业务逻辑的处理,打印log这样的非业务逻辑是不是可以放在别的地方?其二,如果这样的业务方法有很多,每个方法都需要打印log并计算执行时间,岂不是就会出现很多重复代码?
这就是AOP派上用场的地方了。
AOP是怎么工作的呢?首先把以上的非业务逻辑(即打印log、计算执行时间之类的代码)移到别的地方,然后在运行期间,调用到业务方法时,再动态地向业务方法里织入这些被移走的代码。
具体的实现则要用到动态代理技术。

动态代理

Spring使用了JDK动态代理和CGLib动态代理两种代理机制实现AOP。这里暂时只讲JDK动态代理。
JDK动态代理的关键在于Proxy类和InvocationHandler接口。
Proxy的静态方法newProxyInstance()可以创建一个动态代理对象,该方法有三个参数:
1.一个类加载器,用于定义代理对象
2.代理对象需要实现的所有接口
3.一个实现了InvocationHandler接口的类。
这个InvocationHandler接口里只有一个方法:
public Object invoke(Object proxy, Method method, Object[] args)
Proxy类创建的动态代理对象会实现参数2的所有接口,而当我们调用这些接口的任何一个方法时,其实它执行的都是InvocationHandler的invoke方法。注意这个方法里的第二个参数method,我们调用代理对象实现的任何一个方法时,都会把这个方法对应的Method实例传入invoke方法里(java的反射机制)。
举一个例子。

//Study.java
public interface Study {
    public void signIn();
}
//Student.java
public class StudentA implements Study {
    public void signIn(){
        System.out.println("A has signed in.");
    }
}

一个学生类,实现了Study接口,其中有一个签到方法。现在我通过Proxy创建一个代理:

StudentA a = new StudentA();
Study proxy = Proxy.newProxyInstance(a.getClass().getClassLoader(),
    a.getClass().getInterfaces(),
    new MyInvocationHandler(a));
proxy.signIn();

因为代理对象proxy是实现了StudentA所有接口的,因此可以直接调用signIn这个方法。但是前面我们说了,调用的这些方法,其实执行的都是InvocationHandler接口的实现类(这个例子里是MyInvocationHandler)的invoke方法。
这个MyInvocationHandler是我们自定义的一个类,我们可以这样写:

public class MyInvocationHandler implements InvocationHandler {
    private Object target;
    public MyInvocationHandler(Object target){
        this.target = target;
    }
    //实现InvocationHandler的invoke方法
    public Object invoke(Object proxy, Method method, Object[] args){
        Object object = method.invoke(target,args);
        return object;
    }
}

前面说了,invoke方法的第二个参数method,其实对应的就是我们调用proxy的那个方法,也就signIn。而又因为我们在创建MyInvocationHandler实例时传入了StudentA的实例a,这里调用method.invoke()方法,其实就是调用了a.signIn()。
通过一个代理对象去调用方法,在这个简单的例子里并没有什么意义,但是如果我们套用在最开始的那个流程图上呢?Student类即是我们的业务类,signIn()方法就是我们的业务逻辑代码,而非业务逻辑代码(打印log)我们则写进MyInvocationHandler的invoke类里,变成了这样:

public Object invoke(Object proxy, Method method, Object[] args){
    //打印log,省略具体代码
    Object object = method.invoke(target,args); //调用业务逻辑代码
    //打印log并计算执行时间,省略具体代码
    return object;
}

这样一来,我们便通过创建动态代理实现了AOP,不仅将围绕业务逻辑的非业务逻辑移除了出来,还避免了非业务逻辑代码的重复。把非业务逻辑动态地添加到业务类中,这一过程就属于AOP的“织入(Weaving)”。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值