文章目录
- 前言
- JDK动态代理代码实例
- Cglib 代理代码实例
- 两者优缺点
前言
是否在面试过程中经常被问到Spring的代理的问题:比如说几种代理方式?两种代理方式的区别?或者问为什么JDK动态代理只能代理接口?
如果你能回答出来JDK动态代理的原理,然后引申Cglib 动态代理,那么这个面试官一定会对你刮目相看。
在Java中,动态代理是一种机制,允许在运行时动态地创建代理对象来代替某个实际对象,从而在其前后执行额外的逻辑。
为什么JDK动态代理只能代理接口实现类,原因是JDK动态代理是基于接口实现的。
当你使用Proxy
类创建代理对象时,你需要指定一个接口列表来表示代理对象所应该实现的接口,这些接口就成为代理对象的类型。
具体来说,代理对象的方法调用会被转发到实现InvocationHandler
接口的类中的invoke()
方法。这个invoke()
方法接受三个参数:代理对象本身、被调用的方法对象和方法的参数数组。invoke()
方法需要返回被代理方法调用的结果。
由于代理对象的类型是由接口列表决定的,因此只有实现了接口的类才能被代理。如果你想代理一个类而不是一个接口,你需要使用其他的代理技术,比如CGLIB。
JDK动态代理代码实例
下面是一个简单的示例代码,展示了如何使用JDK动态代理来创建代理对象。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyDemo {
public static void main(String[] args) {
RealObject real = new RealObject();
InvocationHandler handler = new DynamicProxy(real);
// 创建代理对象
MyInterface proxy = (MyInterface) Proxy.newProxyInstance(
MyInterface.class.getClassLoader(),
new Class<?>[] { MyInterface.class },
handler);
// 调用代理对象的方法
proxy.doSomething();
}
}
interface MyInterface {
void doSomething();
}
class RealObject implements MyInterface {
public void doSomething() {
System.out.println("RealObject doSomething");
}
}
class DynamicProxy implements InvocationHandler {
private Object target;
public DynamicProxy(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("Before method invocation");
Object result = method.invoke(target, args);
System.out.println("After method invocation");
return result;
}
}
在上面的代码中,RealObject
实现了MyInterface
接口,它是我们要代理的实际对象。DynamicProxy
类实现了InvocationHandler
接口,并在invoke()
方法中添加了额外的逻辑,用于在代理对象方法调用前后执行。
在main()
方法中,我们使用Proxy.newProxyInstance()
方法创建代理对象。我们指定了MyInterface
接口作为代理对象类型,并将DynamicProxy
对象作为代理对象的InvocationHandler
。
最后,我们调用代理对象的doSomething()
方法,并观察控制台输出的结果。
需要注意的是,代理对象的方法调用都会被转发到DynamicProxy
类的invoke()
方法中进行处理,因此在这个示例中,实际的RealObject
对象的doSomething()
方法的执行是在invoke()
方法中通过反射进行的。
总结一下,JDK动态代理只能代理接口实现类,原因是JDK动态代理是基于接口实现的,代理对象的类型由接口列表决定。如果你想代理一个类而不是一个接口,你需要使用其他的代理技术,比如CGLIB。
Cglib 代理代码实例
以下是CGLIB代理的示例代码。
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CGLIBProxyDemo {
public static void main(String[] args) {
RealObject real = new RealObject();
MethodInterceptor handler = new CGLIBProxy(real);
// 创建代理对象
RealObject proxy = (RealObject) Enhancer.create(
RealObject.class,
handler);
// 调用代理对象的方法
proxy.doSomething();
}
}
class CGLIBProxy implements MethodInterceptor {
private Object target;
public CGLIBProxy(Object target) {
this.target = target;
}
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before method invocation");
Object result = proxy.invoke(target, args);
System.out.println("After method invocation");
return result;
}
}
在上面的示例中,我们使用CGLIB的Enhancer
类和MethodInterceptor
接口来创建代理对象。RealObject
类不再需要实现接口,而是直接作为代理对象的类型。在CGLIBProxy
类中,我们实现了MethodInterceptor
接口,并在intercept()
方法中添加了额外的逻辑。
在main()
方法中,我们使用Enhancer.create()
方法创建代理对象。我们指定了RealObject
类作为代理对象类型,并将CGLIBProxy
对象作为代理对象的MethodInterceptor
。最后,我们调用代理对象的doSomething()
方法,并观察控制台输出的结果。
需要注意的是,CGLIB代理使用字节码技术来生成代理对象,因此它的效率比JDK动态代理要高,但是它也需要额外的库依赖。
两者优缺点
JDK动态代理和CGLIB代理都有它们自己的优缺点。
- JDK动态代理的优点:
- JDK动态代理是Java标准库的一部分,因此它不需要引入任何外部依赖。
- JDK动态代理只需要实现接口即可生成代理对象,不需要改变原有类的结构。
- 由于JDK动态代理是基于接口实现的,因此它更适合用于代理接口实现类的场景。
- JDK动态代理的缺点:
- JDK动态代理只能代理实现了接口的类,无法代理没有实现接口的类。
- JDK动态代理在生成代理对象时,需要使用反射机制,因此它的效率相对较低。
- CGLIB代理的优点:
- CGLIB代理是基于字节码技术实现的,因此它的效率比JDK动态代理更高。
- CGLIB代理可以代理没有实现接口的类。
- CGLIB代理的缺点:
- CGLIB代理需要引入外部依赖。
- CGLIB代理在生成代理对象时,需要改变原有类的结构,因此它可能会引起一些问题,例如无法代理final类或final方法等问题。
综上所述,JDK动态代理适用于代理接口实现类的场景,而CGLIB代理适用于代理没有实现接口的类的场景。如果你需要代理接口实现类,而且不想引入额外的依赖,那么JDK动态代理是一个不错的选择;如果你需要代理没有实现接口的类,那么CGLIB代理可能更适合你的需求。
--------------------------------------------------------------欢迎叨扰此地址---------------------------------------------------------------
本文作者:Java技术债务
原文链接:https://cuizb.top/myblog/article/detail/1689928106
版权声明: 本博客所有文章除特别声明外,均采用 CC BY 3.0 CN协议进行许可。转载请署名作者且注明文章出处。