针对如题的区别问题,大多数同学第一回答基本都是JDK的动态代理需要被代理类实现接口,而Cglib动态代理是无须这个要求,由于继承所以无法代码初final修饰的类。
public interface UserService {
String name();
String forName();
}
public class UserServiceImpl implements UserService {
@Override
public String name() {
System.out.println("invoke name");
return "hello,world";
}
@Override
public String forName() {
System.out.println("invoke forName");
return name() + ",bye.";
}
}
如上代码所示,在forName的方法中又调用了name方法。
在JDK的代码实现:
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
UserService service = (UserService) Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("abc");
Object invoke = method.invoke(userService, args);
// Object invoke = method.invoke(proxy, args);
System.out.println("efg");
return invoke;
}
});
}
从打印结果分析,只有forName被代理,而name方法的调用并没有被代理。
CgLib代理
public class MyMethodInterceptor implements MethodInterceptor {
private Object target;
public MyMethodInterceptor(Object target) {
this.target = target;
}
@Override
public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("======插入前置通知======");
//CgLib是继承,所以需要调用父类方法
Object object = methodProxy.invokeSuper(sub, objects);
System.out.println("======插入后者通知======");
return object;
}
}
public class CglibProxyTest {
public static void main(String[] args) {
// 代理类class文件存入本地磁盘方便我们反编译查看源码
//System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");
// 通过CGLIB动态代理获取代理对象的过程
Enhancer enhancer = new Enhancer();
// 设置enhancer对象的父类
enhancer.setSuperclass(UserServiceImpl.class);
// 设置enhancer的回调对象
enhancer.setCallback(new MyMethodInterceptor(null));
// 创建代理对象
UserServiceImpl proxy = (UserServiceImpl) enhancer.create();
// 通过代理对象调用目标方法
proxy.forName();
}
}
通过打印的方法,可以看到forName和name都并代理执行了。
JDK动态代理对象,通过Debug可以确认
类名是形如$Proxy开头的名称,通过代码
byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{UserService.class});
FileOutputStream fos = new FileOutputStream("F:/$Proxy0.class");
fos.write(bytes);
fos.flush();
fos.close();
将内存中的类字节码输出到文件,并反编译,可以分析通过JDK动态代理生成的类结构。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
import com.zte.sdn.oscp.service.UserService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements UserService {
private static Method m1;
private static Method m3;
private static Method m4;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String name() throws {
try {
return (String)super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String forName() throws {
try {
return (String)super.h.invoke(this, m4, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("com.zte.sdn.oscp.service.UserService").getMethod("name");
m4 = Class.forName("com.zte.sdn.oscp.service.UserService").getMethod("forName");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
首先观察接口类中方法的实现
(String)super.h.invoke(this, m3, (Object[])null);
h变量即InvocationHandler,m3变量为通过反射获取的方法
m3 = Class.forName(“com.zte.sdn.oscp.service.UserService”).getMethod(“name”);
此外equals和hashCode以及toString方法都会被统代理
所以JDK的动态代理原理则十分清晰,通过继承接口,重写方法,回调InvocationHandler逻辑
那JDK为何不能嵌套调用原因也很简单,forName---->InvocationHandler------->原始对象的name方法
通过如下代码,可以将CgLib生成的继承类输出
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, “F:\”);
public class UserServiceImpl$$EnhancerByCGLIB$$2eb8ebd2 extends UserServiceImpl implements Factory {
继承类中重写了方法forName和name
public final String forName() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
return var10000 != null ? (String)var10000.intercept(this, CGLIB$forName$1$Method, CGLIB$emptyArgs, CGLIB$forName$1$Proxy) : super.forName();
}
一样地,也是传入的回调即intercept,但需要注意的是此时传入的对象this本身就是代理对象,那为什么他不会产生递归调用呢?原因即在于他针对方法提供了一个MethodProxy#invokerSuper,通过这个封装可以控制只调用父类的方法。
因此CGLIB生成的类为原始类的子类,第一次经过的方法会打印一次==插入前置通知==,然后调用原对象方法,如果这个方法里又嵌套了方法,这时,子类也复写了这个方法,所以仍然通过代理对象调用,所以会再走一次intercept,即再打印==插入前置通知==
模拟代码如下,在forName里强制调用父类的方法,调用name时回调子类的重写的name
public class ChildUserServiceImpl extends UserServiceImpl {
@Override
public String name() {
return "ChildUserName";
}
@Override
public String forName() {
return super.forName();
}
public static void main(String[] args) {
ChildUserServiceImpl service = new ChildUserServiceImpl();
String s = service.forName();
System.out.println(s);
}
}