Loading

Java 中的动态代理使用方法

使用方法

一、Child.class

package proxys;

public interface Child {
    void eat();
}

二、ChildImpl.class

package proxys;

public class ChildImpl implements Child {
    @Override
    public void eat() {
        System.out.println("Child eating...");
    }
}

三、MyProxy.class

package proxys;

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

/**
 * 基于接口的动态代理: Proxy, InvocationHandler
 */
public class MyProxy {

    /**
     * Test Method
     * @param args 参数列表
     */
    public static void main(String[] args) {
        // 未使用代理模式
        ChildImpl child = new ChildImpl();
        child.eat();

        // 使用动态代理添加打印信息
        System.out.println();
        Child proxyObj = getProxyObj(child, Child.class);
        proxyObj.eat();
    }

    /**
     * 获取代理对象
     * @param obj 被代理的对象
     * @param interfaceType 被代理方法所属的接口类型
     * @return 代理对象
     */
    public static <I> I getProxyObj(Object obj, Class<I> interfaceType) {
        // 接口类型检查, 因为 Proxy 是基于接口的动态代理
        Class<?>[] proxyInterfaces = null;
        Class<?>[] interfaces = obj.getClass().getInterfaces();
        for (Class<?> anInterface : interfaces) {
            if (anInterface.equals(interfaceType)) {
                proxyInterfaces = new Class[]{anInterface};
                break;
            }
        }
        if (null == proxyInterfaces) {
            throw new ClassCastException("无法为对象 " + obj + " 创建 " + interfaceType.getName() + " 代理");
        }

        // 核心代码
        Object o = Proxy.newProxyInstance(obj.getClass().getClassLoader(), proxyInterfaces,
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 增强调用前的功能
                        System.out.println("[" + method.getName() + "] Proxy begin...");
                        // 调用被代理对象的方法
                        Object ret = method.invoke(obj, args);
                        // 增强调用后的功能
                        System.out.println("[" + method.getName() + "] Proxy end...");
                        return ret;
                    }
        });

        return (I) o;
    }
}

运行结果:

Child eating...

[eat] Proxy begin...
Child eating...
[eat] Proxy end...

实现方式

  1. JDK 中基于接口的动态代理方式: Proxy + InvocationHandler
    • 原理: 生成实现了指定接口列表的实现类
    • 缺点: 必须借助接口才能产生代理目标对象
  2. CGLib(Code Generation Library)
    • 原理: 生成继承自目标类的实现类
    • 缺点: 无法代理 final 关键字修饰的类

其中 JDK Proxy 是通过直接生成字节码的方法来创建代理对象的,而 CGLib 是使用 ASM 操作字节码来创建代理对象的,因此在代理对象的创建上 JDK Proxy 的效率是更高的。但是在动态代理对象的方法调用上,JDK Proxy 是通过反射来完成的,而 CGLib 则是直接通过生成的字节码来调用目标方法的,因此在动态代理对象的方法调用上,CGLib 的效率是更高的。

posted @ 2021-08-20 15:52  xtyuns  阅读(97)  评论(0编辑  收藏  举报