一、简单来说:
JDK动态代理只能对实现了接口的类生成代理,而不能针对类
CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法(继承)
二、Spring在选择用JDK还是CGLiB的依据:
(1)当Bean实现接口时,Spring就会用JDK的动态代理
(2)当Bean没有实现接口时,Spring使用CGlib是实现
(3)可以强制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)
三、CGlib比JDK快?
(1)使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。
(2)在对JDK动态代理与CGlib动态代理的代码实验中看,1W次执行下,JDK7及8的动态代理性能比CGlib要好20%左右。
以上内容转载自:https://www.cnblogs.com/bigmonkeys/p/7823268.html
这里主要介绍下jdk的动态代理
JDK最简单的创建代理对象的方法是用Proxy类的静态方法:
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
public class ProxyTest {
public static void main(String[] args) {
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
Class<Human>[] interfaceArray = new Class[]{Human.class};
Human h = (Human)Proxy.newProxyInstance(ProxyTest.class.getClassLoader(), interfaceArray, new MyInvocationHandler(new Student()));
h.say();
}
}
interface Human{
void say();
}
class Student implements Human{
@Override
public void say() {
System.out.println("student 说:");
}
}
class MyInvocationHandler implements InvocationHandler {
private Human human;
MyInvocationHandler(Human human){
this.human = human;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("清了清嗓子");
human.say();
System.out.println("大家好");
return null;
}
}
这是一个最简单的动态代理实现方式。
为了观察生成的代理类的内部结构,我们将生成的类导出
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
如图,运行main方法后会生成一个$Proxy0的类,这个就是代理类。
final class $Proxy0 extends Proxy implements Human {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
...
}
public final String toString() throws {
...
}
public final void say() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
...
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("com.ryy.test.Human").getMethod("say", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
发现生成的代理类中有4个Method类型的属性,这四个属性分别是 equals()、toString()、hashCode()和say()。
当调用生成的$Proxy0实例的say()方法时,实际上是调用了该实例中的InvocationHandler成员变量中的
invoke(Object proxy, Method method, Object[] args)方法,将本身实例this和say()方法的Method对象作为参数传入。
大体流程:
1.创建一个需要被代理的方法的实例,这里是Student。
2.创建一个MyInvocationHandler类(InvocationHandlet的实现类)的实例,并将第一步创建的Student实例作为构造参数传入。
3.动态创建一个$Proxy0的类,它继承了Proxy并且实现了自己定义的接口Human,$Proxy0中的InvocationHandler的成员变量就是步骤2创建的实例。
4.调用步骤3创建的$Proxy()实例的say()方法,$Proxy0实例的say()方法调用MyInvocationHandler的invoke()方法以达到动态代理的效果。
注意:MyInvocationHandler的invoke(Object proxy, Method method, Object[] args)方法体中,不能调用proxy中的方法,如果调用会形成一种 proxy.say()->MyInvocationHandler.invoke()->proxy.say()->MyInvocationHandler.invoke()->......这种死循环,导致栈溢出。
最后给出大概的类关系图: