小易说IT 小易说IT

Java 代理模式(Proxy)示例代码

代理模式分为静态代理JDK 动态代理(接口代理)CGLIB 动态代理(类代理,无需接口)

作用:通过代理对象控制对真实对象的访问,在不修改目标类代码的前提下增加日志、权限、事务等附加逻辑。

1. 业务接口 & 真实目标类

// 业务接口
public interface UserService {
    void addUser(String username);
    void deleteUser(String username);
}

// 真实业务实现类(被代理对象)
public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String username) {
        System.out.println("【真实对象】执行新增用户:" + username);
    }

    @Override
    public void deleteUser(String username) {
        System.out.println("【真实对象】执行删除用户:" + username);
    }
}

2. 静态代理

代理类手动编写,包装目标对象,调用前后做增强。

/**
 * 静态代理类,实现相同业务接口
 */
public class UserServiceStaticProxy implements UserService {

    // 持有真实目标对象
    private final UserService target;

    public UserServiceStaticProxy(UserService target) {
        this.target = target;
    }

    @Override
    public void addUser(String username) {
        // 前置增强
        System.out.println("【静态代理】前置:记录操作日志");
        // 调用真实方法
        target.addUser(username);
        // 后置增强
        System.out.println("【静态代理】后置:操作结束\n");
    }

    @Override
    public void deleteUser(String username) {
        System.out.println("【静态代理】前置:权限校验");
        target.deleteUser(username);
        System.out.println("【静态代理】后置:操作结束\n");
    }
}

// 测试静态代理
class StaticProxyTest {
    public static void main(String[] args) {
        UserService target = new UserServiceImpl();
        UserService proxy = new UserServiceStaticProxy(target);

        proxy.addUser("张三");
        proxy.deleteUser("李四");
    }
}

缺点:每一个目标类都要手写代理类,接口变更时代理类也要同步修改。

3. JDK 动态代理(基于接口)

java.lang.reflect.Proxy目标必须实现接口,运行时动态生成代理类字节码。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * JDK动态代理调用处理器
 */
public class JdkInvocationHandler implements InvocationHandler {

    // 被代理的真实对象
    private final Object target;

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

    /**
     * 代理对象调用任何方法,都会进入invoke
     * @param proxy 代理对象
     * @param method 被调用的方法
     * @param args 方法入参
     * @return 方法返回值
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("【JDK动态代理】前置增强:方法名称=" + method.getName());
        // 执行真实对象方法
        Object result = method.invoke(target, args);
        System.out.println("【JDK动态代理】后置增强\n");
        return result;
    }

    // 获取代理实例
    public Object getProxyInstance() {
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                this
        );
    }
}

// JDK动态代理测试
class JdkProxyTest {
    public static void main(String[] args) {
        UserService target = new UserServiceImpl();
        JdkInvocationHandler handler = new JdkInvocationHandler(target);
        // 强转为接口类型
        UserService proxy = (UserService) handler.getProxyInstance();

        proxy.addUser("王五");
        proxy.deleteUser("赵六");
    }
}

4. CGLIB 动态代理(无需接口,继承目标类)

JDK 代理要求接口;CGLIB 通过继承目标类生成子类做代理,目标类不能是 final。 Spring AOP:有接口用 JDK,无接口用 CGLIB。

需要引入依赖(Maven)

<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;

/**
 * CGLIB 方法拦截器
 */
public class CglibMethodInterceptor implements MethodInterceptor {

    private final Object target;

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

    /**
     * 所有方法调用都会进入intercept
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("【CGLIB代理】前置增强:" + method.getName());
        // 调用父类(真实对象)方法
        Object res = proxy.invokeSuper(obj, args);
        System.out.println("【CGLIB代理】后置增强\n");
        return res;
    }

    // 创建CGLIB代理对象
    public Object getCglibProxy() {
        Enhancer enhancer = new Enhancer();
        // 设置父类(被代理类)
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }
}

// CGLIB测试
class CglibProxyTest {
    public static void main(String[] args) {
        // 不需要实现接口的普通类也可以代理
        UserServiceImpl target = new UserServiceImpl();
        CglibMethodInterceptor interceptor = new CglibMethodInterceptor(target);
        UserServiceImpl proxy = (UserServiceImpl) interceptor.getCglibProxy();

        proxy.addUser("小明");
        proxy.deleteUser("小红");
    }
}

三种代理对比

代理类型

实现方式

条件

优点

缺点

静态代理

手写代理类

目标实现接口

简单直观

类爆炸,维护成本高

JDK 动态代理

JDK Proxy+InvocationHandler

必须实现接口

JDK 原生无需依赖

不能代理没有接口的类

CGLIB 动态代理

ASM 字节码生成子类

目标类不能 final

不需要接口

第三方库,final 类无法代理

Spring AOP 默认逻辑:

  1. 如果目标对象实现接口,使用 JDK 动态代理;

  2. 如果目标没有实现接口,使用 CGLIB;

  3. SpringBoot 可以配置强制使用 CGLIB。


本文原创作者:易君召,详见:https://www.yijunzhao.cc/about,转载请注明出处。

原文链接 https://www.yijunzhao.cc/archives/java-proxy-pattern-example-code

欢迎访问 https://www.yijunzhao.cc/

https://www.yijunzhao.cc/