前言:我们每次面试都要问到的一个问题:spring aop 的底层实现原理是什么,我们当然都知道是动态代理,然后还知道动态代理分为JDK动态代理和Cglib动态代理,并且还知道JDK依赖接口,Cglib不依赖,面试官一问为什么,卧槽,很尴尬不知道,下面我们就直接用例子来演示一下为什么?
JDK代理类的生产与结构
jdk动态代理是jre提供给我们的类库,可以直接使用,不依赖第三方。先看下jdk动态代理的使用代码,再理解原理。
首先有个“演员”接口类,有演戏功能:
public interface Actor {
/**
* 演戏
*/
void acting();
}
再来一个演员实现类
public class ZhengShuang implements Actor {
@Override
public void acting() {
System.out.println("开始表演!");
System.out.println("台词功底十分强悍,上来就123456789");
System.out.println("表演完了!");
}
}
演员演戏演戏前后需要联系演员排期以及结账,由于这些事情比较繁琐,演员自己不做这个工作,一般交给一个经纪人。便于理解,它的名字以Proxy结尾,但他不是代理类,原因是它没有实现我们的演员接口,无法对外服务,它仅仅是一个wrapper。
public class ActorProxy implements InvocationHandler {
/**
* 目标类,也就是被代理对象
*/
private Object target;
public void setTarget(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 这里可以做增强
System.out.println("找到经纪人联系演员准备开始演戏");
Object result = method.invoke(target, args);
System.out.println("付钱给演员经纪人,共计支付1.2亿元");
return result;
}
public Object creatProxyObj() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
}
上述例子中,方法creatProxyObj返回的对象才是我们的代理类,它需要三个参数,前两个参数的意思是在同一个classloader下通过接口创建出一个对象,该对象需要一个属性,也就是第三个参数,它是一个InvocationHandler。需要注意的是这个creatProxyObj方法不一定非得在我们的ActorProxy类中,往往放在一个工厂类中。上述代理的代码使用过程一般如下:
public class Test {
public static void main(String[] args) {
// 创建一个演员对象
Actor zs = new ZhengShuang();
// 创建一个InvocationHandler对象
ActorProxy proxy = new ActorProxy();
// 将演员对象传递到InvocationHandler对象中去
proxy.setTarget(zs);
// 调用creatProxyObj生成代理类
Object proxyObj = proxy.creatProxyObj();
// 将代理类转换成我们的演员类
Actor actor = (Actor)proxyObj;
// 执行对应方法
actor.acting();
}
}
看下输出吧
找到经纪人联系演员准备开始演戏 开始表演! 台词功底十分强悍,上来就123456789 表演完了! 付钱给演员经纪人,共计支付1.2亿元
但是为什么依赖借口呢,下面我们就来看一看这个creatProxyObj生成代理类是什么鬼东西,我们打个断点研究研究
可以看到代理类原来还穿了一层衣服,继承了Proxy,由于Java不支持多继承,所以,如果使用JDK动态代理来生成代理类的话不能使用普通的类来生成,必须要使用接口来生成。
Cglib代理类的生产与结构
使用cglib实现代理感觉的简单多了,下边直接上代码
public class CglibProxy implements MethodInterceptor {
@Override
public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// 这里可以做增强
System.out.println("找到经纪人联系演员准备开始演戏");
Object result = methodProxy.invokeSuper(object, objects);
System.out.println("付钱给演员经纪人,共计支付1.2亿元");
return result;
}
/**
* 根据一个类型产生代理类,此方法不要求一定放在MethodInterceptor中
*
* @param clazz
* @return
*/
public Object creatProxyObj(Class<?> clazz) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
}
紧接着我们生成一个爽子类的代理类测试一下吧
public class Test {
public static void main(String[] args) {
// 创建一个MethodInterceptor对象
CglibProxy proxy = new CglibProxy();
// 调用creatProxyObj生成ZhengShuang的代理类
Object shuagnZiProxyObj = proxy.creatProxyObj(ZhengShuang.class);
// 类型转换
Actor shuangZi = (Actor)shuagnZiProxyObj;
shuangZi.acting();
}
}
看下输出吧
找到经纪人联系演员准备开始演戏 开始表演! 台词功底十分强悍,上来就123456789 表演完了! 付钱给演员经纪人,共计支付1.2亿元
可以看到们对爽子直接生成了代理类,那我们也去看看爽子类的结构吧,debug
可以看到这个生成的代理类是继承我们被代理类的,所以就不会又必须实现接口这个限制了。
总结:
SpringAop实现代理类的方法有两种,JDK动态代理和Cglib动态代理,JDK针对实现接口的类进行代理,Cglib则针对所有类都可以使用。
JDK动态代理针对接口的限制是因为JDK动态代理生成的类是实现了Proxy的,由于Java不能多继承,所以不能使用JDK动态代理去处理没有实现接口的类。