代理模式(Proxy Pattern)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象,这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
代理模式又分为两大类:静态代理和动态代理。其中动态代理又分为JDK代理和CGLIB代理。
代理类与委托类实现同一接口。在代理类中需要硬编码接口。
实现简单,容易理解。
代理类需要硬编码接口,在实际应用中可能会导致重复编码,浪费存储空间并且效率较低。
package com.wnhz.smart.order.proxy.sta;
/**
* @author Hao
* @date 2023-11-28 20:10
*/
public class StaticProxyDemo {
public static void main(String[] args) {
// 被代理对象
RealObject realObject = new RealObject();
// 创建代理对象
StaticProxy proxy = new StaticProxy(realObject);
// 实现静态代理,调用代理对象的方法
proxy.doSomething();
}
}
interface MyInterface {
void doSomething();
}
class RealObject implements MyInterface {
@Override
public void doSomething() {
System.out.println("RealObject doSomething");
}
}
class StaticProxy implements MyInterface {
private RealObject realObject;
public StaticProxy(RealObject realObject) {
this.realObject = realObject;
}
@Override
public void doSomething() {
System.out.println("Before method invocation");
realObject.doSomething();
System.out.println("After method invocation");
}
}
代理类与委托类实现同一接口。代理类要实现 InvocationHandler 接口并重写 invoke 方法。invoke 方法中可以对委托方法进行增强处理。
不需要硬编码接口,代码复用率高。
只能够代理实现了接口的委托类。
底层使用反射机制进行方法的调用。
在下面的代码中,RealObject实现了MyInterface接口,它是我们要代理的实际对象。DynamicProxy类实现了InvocationHandler接口,并在invoke()方法中添加了额外的逻辑,用于在代理对象方法调用前后执行。
在main()方法中,我们使用Proxy.newProxyInstance()方法创建代理对象。我们指定了MyInterface接口作为代理对象类型,并将DynamicProxy对象作为代理对象的InvocationHandler。
最后,我们调用代理对象的doSomething()方法,并观察控制台输出的结果。
需要注意的是,代理对象的方法调用都会被转发到DynamicProxy类的invoke()方法中进行处理,因此在这个示例中,实际的RealObject对象的doSomething()方法的执行是在invoke()方法中通过反射进行的。
package com.proxy.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author Hao
* @date 2023-11-28 19:34
*/
public class JDKProxyDemo {
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 {
@Override
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;
}
}
结果:
Before method invocation
RealObject doSomething
After method invocation
JDK动态代理只能代理接口实现类,原因是JDK动态代理是基于接口实现的,代理对象的类型由接口列表决定。如果你想代理一个类而不是一个接口,你需要使用其他的代理技术,比如CGLIB动态代理。
代理类将委托类作为自己的父类并为其中的非 final 委托方法创建两个方法,一个是与委托方法签名相同的方法,它在方法中会通过 super 调用委托方法;另一个是代理类独有的方法。在代理方法中,它会判断是否存在实现了MethodInterceptor 接口的对象,若存在则将调用 intercept 方法对委托方法进行代理。
可以在运行时对类或者是接口进行增强操作,且委托类无需实现接口。
不能对 final 类以及 final 方法进行代理。
底层将方法全部存入一个数组中,通过数组索引直接进行方法调用。
在下面的示例中,我们使用CGLIB的Enhancer类和MethodInterceptor接口来创建代理对象。RealObject类不再需要实现接口,而是直接作为代理对象的类型。在CGLIBProxy类中,我们实现了MethodInterceptor接口,并在intercept()方法中添加了额外的逻辑。
在main()方法中,我们使用Enhancer.create()方法创建代理对象。我们指定了RealObject类作为代理对象类型,并将CGLIBProxy对象作为代理对象的MethodInterceptor。最后,我们调用代理对象的doSomething()方法,并观察控制台输出的结果。
需要注意的是,CGLIB代理使用字节码技术来生成代理对象,因此它的效率比JDK动态代理要高,但是它也需要额外的库依赖。
package com.proxy.cglib;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @author Hao
* @date 2023-11-28 19:58
*/
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 RealObject {
public void doSomething() {
System.out.println("RealObject doSomething");
}
}
class CGLIBProxy implements MethodInterceptor {
private Object target;
public CGLIBProxy(Object target) {
this.target = target;
}
@Override
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;
}
}
结果:
Before method invocation
RealObject doSomething
After method invocation
JDK动态代理是Java标准库的一部分,因此它不需要引入任何外部依赖。
JDK动态代理只需要实现接口即可生成代理对象,不需要改变原有类的结构。
由于JDK动态代理是基于接口实现的,因此它更适合用于代理接口实现类的场景。
JDK动态代理只能代理实现了接口的类,无法代理没有实现接口的类。
JDK动态代理在生成代理对象时,需要使用反射机制,因此它的效率相对较低。
CGLIB代理是基于字节码技术实现的,因此它的效率比JDK动态代理更高。
CGLIB代理可以代理没有实现接口的类。
CGLIB代理需要引入外部依赖。
CGLIB代理在生成代理对象时,需要改变原有类的结构,因此它可能会引起一些问题,例如无法代理final类或final方法等问题。
综上所述,JDK动态代理适用于代理接口实现类的场景,而CGLIB代理适用于代理没有实现接口的类的场景。如果你需要代理接口实现类,而且不想引入额外的依赖,那么JDK动态代理是一个不错的选择;如果你需要代理没有实现接口的类,那么CGLIB代理可能更适合你的需求。