代理
约 1045 字大约 3 分钟
2025-07-09
使用的是标准的代理模式,即实现类与代理类实现同一个接口,代理类中拥有一个接口类的成员变量(存放具体实现类),通过代理类来调用实现类实现方法的增强
// 接口类
public interface SmsService {
String send(String message);
}
// 实现类
public class SmsServiceImpl implements SmsService {
public String send(String message) {
System.out.println("send message:" + message);
return message;
}
}
// 代理类
public class SmsProxy implements SmsService {
private final SmsService smsService;
public SmsProxy(SmsService smsService) {
this.smsService = smsService;
}
// 方法的增强实现
@Override
public String send(String message) {
//调用方法之前,我们可以添加自己的操作
System.out.println("before method send()");
smsService.send(message);
//调用方法之后,我们同样可以添加自己的操作
System.out.println("after method send()");
return null;
}
}缺点:不灵活,需要对每个目标类单独创建一个代理类
动态代理
JDK 动态代理
动态代理中的核心为InvocationHandler接口以及Proxy
public interface SmsService {
String send(String message);
}
public class SmsServiceImpl implements SmsService {
public String send(String message) {
System.out.println("send message:" + message);
return message;
}
}
public class DebugInvocationHandler implements InvocationHandler {
/**
* 代理类中的真实对象
*/
private final Object target;
public DebugInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
//调用方法之前,我们可以添加自己的操作
System.out.println("before method " + method.getName());
Object result = method.invoke(target, args);
//调用方法之后,我们同样可以添加自己的操作
System.out.println("after method " + method.getName());
return result;
}
}
public class JdkProxyFactory {
public static Object getProxy(Object target) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 目标类的类加载器
target.getClass().getInterfaces(), // 代理需要实现的接口,可指定多个
new DebugInvocationHandler(target) // 代理对象对应的自定义 InvocationHandler
);
}
}动态代理的流程为:将实现类传入代理类工厂方法,代理类中加载一个InvocationHandler对象,而代理类的继承和实现与实现类一直,但将其中方法的具体实现交由InvocationHandler处理
为什么需要类加载器? 对于 JVM 而言,不同的类加载器加载出的对象是不一样的,不能相互转型,即便两者名字相同 为什么只需要传入实现的接口? 代理类需要知晓实现了哪些接口,才能进一步知道如何要生成哪些方法体
运行时生成的字节码逻辑如下
class $Proxy0 extends Proxy implements SmsService {
private InvocationHandler h;
public $Proxy0(InvocationHandler h) {
this.h = h;
}
public String send(String message) {
h.invoke(this, 方法对象, 参数); // 转发到你的 InvocationHandler
}
}从以上逻辑中不难发现,最后得到的代理类将转型为实现类的接口类,这也就意味着你只能调用接口类的方法而无法调用实现类自身或继承的方法;且代理类的创建必须要求存在实现接口,即没有实现接口的类无法被代理,这便是 JDK 代理的局限性
CGLIB 动态代理
为了解决 JDK 代理的局限性,我们可以使用 CGLIB 动态代理机制来避免 作为一个第三方库,需要引入额外的依赖 CGLIB 代理的对象为对象,而非接口,因此不要求目标类一定实现了接口
public class UserService {
public void addUser() {
System.out.println("添加用户");
}
public final void deleteFinalUser() {
System.out.println("删除 final 用户");
}
}
public class CglibProxyExample {
public static void main(String[] args) {
// 创建 Enhancer
Enhancer enhancer = new Enhancer();
// 设置目标类(要代理的类)
enhancer.setSuperclass(UserService.class);
// 设置拦截器
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("方法调用前:日志记录开始...");
// 调用原始方法(注意:不要用 method.invoke,用 proxy.invokeSuper 更高效)
Object result = proxy.invokeSuper(obj, args);
System.out.println("方法调用后:日志记录结束。");
return result;
}
});
// 创建代理对象(实际上是 UserService 的子类实例)
UserService proxy = (UserService) enhancer.create();
// 调用方法
proxy.addUser(); // ✅ 被拦截
// proxy.deleteFinalUser(); // ❌ final 方法无法被重写,不会被拦截
}
}CGLIB 的工作原理为创建一个目标类的子类,并重写其中的方法,从而实现在方法中插入增强逻辑,然后再用这个子类来替代目标对象 因为是子类重写,所以在以上代码块中deleteFinalUser()方法无法被重写
