W
e
l
c
o
m
e
: )

Java代理模式

一、什么是代理模式?

代理模式(Proxy Pattern)是Java中非常经典的结构型设计模式。它的核心思想是: 通过引入一个“代理对象”来控制对“目标对象”的访问 。就像明星的经纪人,外界找明星工作必须通过经纪人,经纪人可以做过滤、谈判、排期,甚至代替明星处理简单事务。

二、静态代理(编译期确定)

原理 :代理类和目标类实现 同一个接口 ,代理类持有目标类的引用,在调用目标方法前后插入逻辑。

适用场景 :目标类很少,且不需要频繁更换代理逻辑。

缺点 :如果接口新增方法,代理类必须同步修改;如果有很多Service,每个都要写一个代理类,代码冗余严重。

/**
 * 接口定义
 */
public interface UserService {
    void saveUser(String name);
}
/**
 * 目标对象(真实业务)
 */
public class UserServiceImpl implements UserService{
    @Override
    public void saveUser(String name) {
        System.out.println("保存用户:" + name);
    }
}
/**
 * 静态代理类
 */
public class UserServiceProxy implements UserService{

    private UserService target;//持有目标对象

    public UserServiceProxy(UserService userService){
        this.target = userService;
    }

    @Override
    public void saveUser(String name) {
        System.out.println("【前置增强】...");
        target.saveUser(name);//调用真实业务
        System.out.println("【后置增强】...");
    }
}
public class Main {
    public static void main(String[] args) {
        UserService target = new UserServiceImpl();
        UserService proxy = new UserServiceProxy(target);
        proxy.saveUser("张三");
    }
}

三、动态代理(运行时生成)

为了解决静态代理的冗余问题,Java提供了动态代理,在运行时动态生成代理字节码。主要分为两种:

一、JDK动态代理(基于接口)

  • 要求 :目标对象必须实现至少一个接口
  • 核心java.lang.reflect.ProxyInvocationHandler
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JdkLogProxy implements InvocationHandler {

    private Object target;//目标对象

    public JdkLogProxy(Object target){
        this.target = target;
    }

    /**
     * 生成代理对象
     * @param target
     * @return
     */
    public static Object getProxy(Object target){
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new JdkLogProxy(target));
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("【JDK动态代理】前置...");
        Object result = method.invoke(target,args);//使用反射调用方法
        System.out.println("【JDK动态代理】后置...");
        return result;
    }
}
public class Main {
    public static void main(String[] args) {
        UserService target = new UserServiceImpl();
        UserService proxy = (UserService) JdkLogProxy.getProxy(target);
        proxy.saveUser("张三");
    }
}

二、CGLIB动态代理(基于继承)

  • 要求 :目标对象不需要实现接口 ,底层通过生成目标类的子类来实现(Enhancer)。
  • 注意 :目标类不能被 final 修饰,方法也不能是 final
<dependency>
     <groupId>cglib</groupId>
     <artifactId>cglib</artifactId>
     <version>3.3.0</version>
</dependency>
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 CglibLogProxy implements MethodInterceptor {

    /**
     * 生成代理对象(子类)
     * @param clazz
     * @return
     */
    public static Object getProxy(Class<?> clazz){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(new CglibLogProxy());
        return enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("【CGLIB动态代理】前置...");
        Object result = proxy.invokeSuper(obj, args);//调用父类(目标类)方法
        System.out.println("【CGLIB动态代理】后置...");
        return result;
    }
}
/**
 * 目标类(没有实现接口)
 */
public class UserService {
    public void saveUser(String name) {
        System.out.println("保存:" + name);
    }
}
public class Main {
    public static void main(String[] args) {
        UserService proxy = (UserService) CglibLogProxy.getProxy(UserService.class);
        proxy.saveUser("王五");
    }
}

需要注意的是:如果使用的是高版本的JDK,可能会报 InaccessibleObjectException,具体原因如下:

  • Java 9+ 模块化 :JDK 内部将 java.lang.ClassLoader 等核心类放在了 java.base 模块中。
  • 强封装策略 :从 Java 16/17 开始,JDK 严格禁止通过 setAccessible(true) 来强行调用 java.base 中未明确开放的内部包(java.lang)。
  • CGLIB 的底层逻辑 :CGLIB 为了动态生成代理类的字节码,需要调用 ClassLoader.defineClass() 这个 受保护的内部方法 。CGLIB 尝试用反射强行打开访问权限,结果被 JDK 直接拒绝,抛出 InaccessibleObjectException

解决方法:添加JVM启动参数,显式打开 java.lang 包供所有未命名模块访问。

--add-opens java.base/java.lang=ALL-UNNAMED

三、二者对比

维度 JDK 动态代理 CGLIB 动态代理
实现机制 反射 + 生成接口的匿名实现类 生成目标类的子类(ASM字节码技术)
目标要求 必须实现接口 无须接口 (普通类即可)
性能 反射调用效率相对低(JDK8+优化后差距缩小) 生成字节码快,调用性能略高
依赖 JDK原生,无外部依赖 需引入 cglib 包(Spring已内置)
无法代理 接口方法 final 修饰的类或方法
posted @ 2026-08-03 11:44  寒月静无光  阅读(3)  评论(0)    收藏  举报