最近要用AOP做一个BI打点上报,之前有看过AOP,但也只是看过而已,现在要用起来,还是得花时间好好看看的。首先Spring AOP底层就是通过动态代理机制实现的,所以现在先来了解一下动态代理的机制。
基于JDK的动态代理
说白了就是为目标类创建一个代理对象,这种方式主要涉及到java.lang.reflect包中的Proxy类和InvocationHandler接口。至于这两个是什么东西,我直接用一个实例来说明吧。 先是定义一个考试接口
public interface Exam{
void startExam(String s);
}
然后定义一个学生类,来实现上面的接口,这个就是我们的真实对象
public class Student implements Exam{
@Override
public void startExam(String s) {
System.out.println("开始考试了!!");
}
}
现在要为上面这个真实对象创建一个代理对象,首先这个动态代理类必须实现上面的InvocationHandler接口。所以新建一个DynamicProxy类
public class DynamicProxy implements InvocationHandler {
//目标类
private Object target;
public DynamicProxy(Object o){
target = o;
}
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
System.out.println("监考开始");
method.invoke (target, objects);
System.out.println("监考结束");
return target;
}
}
首先需要重写接口里面唯一的方法invoke(),方法里面有三个参数: Object o:我们创建的代理对象 Method method:我们需要调用的真实对象的某个方法 Object[] objects:上面方法传入的参数 当我们通过代理对象调用真实对象的某个方法时,就会执行这里的invoke()方法,在这个方法里面,打印“监考开始”和“监考结束”是我们新加的逻辑(不属于Student类里面的内容),这样就可以动态地将自己的逻辑和业务逻辑(Student里面的)编织在一起。 然后我们在看看主函数:
public class Test {
public static void main(String[] args){
Student target = new Student();
DynamicProxy handler = new DynamicProxy(target);
Exam proxy = (Exam) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler);
proxy.startExam();
}
}
先是把新建的Student类传到DynamicProxy的构造方法里面去,我们需要代理哪个类就传哪个,因为最后是通过该真实对象来调用其方法的 接着通过Proxy.newProxyInstance()来生成我们的代理对象,里面需要传入三个参数: ClassLoader loader:定义由哪个类加载器对生成的代理对象进行加载 Class<?>[] interfaces:表示我们要给这个代理对象提供一组什么接口,代理对象实现了这组接口,那么我们就 可以调用里面的方法了 InvocationHandler h:表示当代理对象调用方法时,会关联到哪一个InvocationHandler对象上 最后就是利用代理对象来调用真实对象的方法,来看一下输出: 可以看到,输出的内容除了Student类里面的逻辑,还有我们自己新加的逻辑 当我们通过代理对象来调用方法时,实际是委托其关联的InvocationHandler对象的invoke()方法来调用,并不是真实调用,这就是动态代理机制。 我们知道,除了基于JDK动态代理,还有一种是基于CGLib动态代理。基于JDK这么好用,为什么还要再弄一种新的出来呢?这是因为基于JDK的代理只能为接口创建代理实例,你可以从上面就能清楚看到,Proxy.newProxyInstance()方法里面第二个参数传的是需要实现的接口数组。还是不明白的话可以看代理对象的创建过程,我们创建出来的代理对象其实是继承了Proxy类的,而Java有一条大名鼎鼎的法则:单一继承原则。
基于CGLib动态代理
那么下面我们就来谈谈这种创建代理的方式 CGLib是采用非常底层的字节码技术,可以为一个类创建一个子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,并顺势织入我们新加的逻辑,确实有点酷。