代理模式
代理模式又分为两大类:静态代理和动态代理。其中动态代理又分为JDK代理和CGLIB代理。
1. 静态代理
1.1实现方式
代理类与委托类实现同一接口。在代理类中需要硬编码接口。
1.2 优点
实现简单,容易理解。
1.3 缺点
代理类需要硬编码接口,在实际应用中可能会导致重复编码,浪费存储空间并且效率较低。
1.4 实现代码
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");
}
}
2. JDK动态代理
2.1实现方式
代理类与委托类实现同一接口。代理类要实现 InvocationHandler 接口并重写 invoke 方法。invoke 方法中可以对委托方法进行增强处理。
2.2 优点
不需要硬编码接口,代码复用率高。
2.3 缺点
只能够代理实现了接口的委托类。
2.4 特点
底层使用反射机制进行方法的调用。
2.5 实现代码
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;
}
}
结果:
2.6 总结
JDK动态代理只能代理接口实现类,原因是JDK动态代理是基于接口实现的,代理对象的类型由接口列表决定。如果你想代理一个类而不是一个接口,你需要使用其他的代理技术,比如CGLIB动态代理。
CGLIB 动态代理
3.1实现方式
代理类将委托类作为自己的父类并为其中的非 final 委托方法创建两个方法,一个是与委托方法签名相同的方法,它在方法中会通过 super 调用委托方法;另一个是代理类独有的方法。在代理方法中,它会判断是否存在实现了MethodInterceptor 接口的对象,若存在则将调用 intercept 方法对委托方法进行代理。
3.2 优点
可以在运行时对类或者是接口进行增强操作,且委托类无需实现接口。
3.3 缺点
不能对 final 类以及 final 方法进行代理。
3.4 特点
底层将方法全部存入一个数组中,通过数组索引直接进行方法调用。
3.5 代码实现
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;
}
}
结果:
两者之间的优缺点
JDK动态代理的优点:
-
JDK动态代理是Java标准库的一部分,因此它不需要引入任何外部依赖。
-
JDK动态代理只需要实现接口即可生成代理对象,不需要改变原有类的结构。
-
由于JDK动态代理是基于接口实现的,因此它更适合用于代理接口实现类的场景。
JDK动态代理的缺点:
-
JDK动态代理只能代理实现了接口的类,无法代理没有实现接口的类。
-
JDK动态代理在生成代理对象时,需要使用反射机制,因此它的效率相对较低。
CGLIB代理的优点:
-
CGLIB代理是基于字节码技术实现的,因此它的效率比JDK动态代理更高。
-
CGLIB代理可以代理没有实现接口的类。
CGLIB代理的缺点:
-
CGLIB代理需要引入外部依赖。
-
CGLIB代理在生成代理对象时,需要改变原有类的结构,因此它可能会引起一些问题,例如无法代理final类或final方法等问题。