大家好,我是鸭鸭!
此答案节选自鸭鸭最近弄的 面试鸭小程序,更多 大厂常问面试题,可以点击下面的小程序进行阅读哈!
JDK 动态代理是基于接口的,所以要求代理类一定是有定义接口的。
CGLIB 基于 ASM 字节码生成工具,它是通过继承的方式来实现代理类,所以要注意 final 方法。
它们之间的性能随着 JDK 版本的不同而不同,以下内容取自:haiq的博客
- jdk6 下,在运行次数较少的情况下,jdk动态代理与 cglib 差距不明显,甚至更快一些;而当调用次数增加之后, cglib 表现稍微更快一些
- jdk7 下,情况发生了逆转!在运行次数较少(1,000,000)的情况下,jdk动态代理比 cglib 快了差不多30%;而当调用次数增加之后(50,000,000), 动态代理比 cglib 快了接近1倍
- jdk8 表现和 jdk7 基本一致
JDK 动态代理是基于接口的代理,因此要求代理类一定是有定义的接口,使用 java.lang.reflect.Proxy
类和 java.lang.reflect.InvocationHandler
接口实现。
以下为一个简单 JDK 动态代理示例:
// 接口
public interface Service {
void perform();
}
// 需要被代理的实现类
public class ServiceImpl implements Service {
@Override
public void perform() {
System.out.println("mianshiya.com");
}
}
JDK 动态代理处理类:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class ServiceInvocationHandler implements InvocationHandler {
private final Object target;
public ServiceInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method invoke");
Object result = method.invoke(target, args);
System.out.println("After method invoke");
return result;
}
}
创建并使用动态代理对象:
import java.lang.reflect.Proxy;
public class DynamicProxyDemo {
public static void main(String[] args) {
Service target = new ServiceImpl();
Service proxy = (Service) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new ServiceInvocationHandler(target)
);
proxy.perform();
}
}
我们再看看 JDK 动态代理实现原理:
再深一点点就是代理类会现在静态块中通过反射把所有方法都拿到存在静态变量中,我之前反编译看过代理类,我盲写了一下,大致长这样:
这一套下来 JDK 动态代理原理应该就很清晰了。
CGLIB 基于 ASM 字节码生成工具,它是通过继承的方式来实现代理类,所以不需要接口,可以代理普通类,但需要注意 final 方法(不可继承)。
同样来看个示例:
public class Service {
public void perform() {
System.out.println("mianshiya.com");
}
}
CGLIB 动态代理处理类:
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class ServiceMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before method invoke");
Object result = proxy.invokeSuper(obj, args);
System.out.println("After method invoke");
return result;
}
}
创建并使用动态代理对象:
import net.sf.cglib.proxy.Enhancer;
public class CglibDynamicProxyDemo {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Service.class);
enhancer.setCallback(new ServiceMethodInterceptor());
Service proxy = (Service) enhancer.create();
proxy.perform();
}
}
它是通过字节码生成技术而不是反射来实现调用的逻辑,具体就不再深入了。
最后再推荐下鸭鸭目前努力在做面试小程序神器,已经有 3000 多道面试题目啦,欢迎大家来阅读!如果大家有不会的面试题,也可以在小程序内反馈!鸭鸭会第一时间为大家解答!
除了小程序版本,我们 web 端也上线啦:www.mianshiya.com
我是鸭鸭,我们下期见~