《看不懂你打我系列》之 —— 极简JDK动态代理

本文详细介绍了JDK动态代理的工作原理及其实现过程,包括如何使用InvocationHandler接口和Proxy类生成代理对象,以及为何需要接口作为纽带。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

大白话来讲,代理就是一个类代替另一个类去做一些事,在现实生活中好比媒婆代理你,去给你找对象,或者在你科学上网时,876服务器代理你的电脑去为你打开世界的大门。归纳起来,代理的过程无非就两点:谁需要被代理?代理他的人需要为他做些什么?

用稍微专业一些的话来说,只需要明确2点:1.目标类(被代理类) 2.增强(代理类需要做的事)。

代理又分为静态代理动态代理,静态代理是在编译期就确定了目标类,直接编译生成代理类;而动态代理,在编译期不能确定目标类,需要在运行时根据需要进行代理。动态代理是通过java的反射技术来实现的,具体实现方式又分为JDK动态代理CGLib动态代理,今天咱讨论的主角是JDK动态代理。

要实现jdk的动态代理,关键在于InvocationHandler接口

简单来说,我们需要编写一个实现类,实现这个InvocationHandler接口,并重写invoke方法,在这个invoke方法里实现具体的代理逻辑。其次,通过调用Proxy.newProxyInstance()方法,传入InvocationHandler实现类,生成代理对象,即可通过代理对象完成代理。

JDK动态代理是基于接口的,被代理类必须要实现某个接口,以这个接口作为被代理类代理类之间的纽带。那么为什么一定要接口呢?别急,先来看一个demo

//被代理类
public interface SomeService {
    void doService(String name);
}

public class SomeServiceImpl implements SomeService {
    @Override
    public void doService(String name) {
        System.out.println("Services is being provided..Enjoy it.."+name);
    }
}
//InvocationHandler实现类
public class MyJDKProxyFactory implements InvocationHandler {
    private Object target; //被代理对象
    private MyJDKProxyFactory(Object target){
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("method " + method.getName() + " is being invoked");
        Object returnValue = method.invoke(target,args);
        System.out.println("method " + method.getName() + " finished");
        return returnValue;
    }

    public static Object getMyProxy(Object target){
        MyJDKProxyFactory proxyFactory = new MyJDKProxyFactory(target);
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),proxyFactory);
    }
}
//测试类
public class Test {
    public static void main(String[] args){
        SomeService happyService = new SomeServiceImpl();
        //获得一个代理类
        SomeService proxy = (SomeService) MyJDKProxyFactory.getMyProxy(happyService);
        //代理类开始做事
        proxy.doService("yogurt");
    }
}

结果:

method doService is being invoked
Services is being provided..Enjoy it..yogurt
method doService finished

可见好像确实是调用了InvocationHandler实现类中的invoke方法,我们将生成的代理类的class文件保存下来,反编译看看

//默认情况,jdk动态代理生成的代理类的class文件,存在内存中
//下面将生成的代理类的class文件持久化到磁盘
public static void generateProxyClassFile(String proxyName,Class clazz){
        byte[] bs = ProxyGenerator.generateProxyClass(proxyName,clazz.getInterfaces());
        String path = clazz.getResource(".").getPath();
        FileOutputStream fileOut = null;
        try{
            fileOut = new FileOutputStream(path + proxyName+".class");
            fileOut.write(bs);
            fileOut.flush();
            fileOut.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
}
public static void main(String[] args){
    SomeService happyService = new SomeServiceImpl();
    generateProxyClassFile("$happyProxy",happyService.getClass());
}

下面是生成的$happyProxy.class文件反编译的结果

import com.yogurt.SomeService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $happyProxy extends Proxy implements SomeService {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $happyProxy(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void doService(String var1) throws  {
        try {
            super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.yogurt.SomeService").getMethod("doService", Class.forName("java.lang.String"));
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

粗略一看,生成的代理类,继承了Proxy类,并实现了SomeService接口,调用代理类的doService()方法时,实际是调用了Proxy类中的InvocationHandler对象的invoke()方法,故我们需要编写一个InvocationHandler实现类,并作为入参传递给Proxy.newProxyInstance()方法,并且,为什么被代理类必须要实现某个接口,也能找到答案了,因为生成的代理类,本身继承了Proxy,而出于安全考虑,java是不允许多继承的,故只能用接口来作为被代理类与代理类之间的纽带

进一步深究,我们可以进入Proxy的源码,看看Proxy.newProxyInstance()究竟做了什么

public class Proxy implements java.io.Serializable {
	/** parameter types of a proxy class constructor */
    private static final Class<?>[] constructorParams =
        { InvocationHandler.class };
    /**
     * a cache of proxy classes
     */
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
    /**
     * the invocation handler for this proxy instance.
     * @serial
     */
    protected InvocationHandler h;
    
    //.....
    /** 省略其余源码*/
    
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }
}

可以看到,newProxyInstance()的入参有3个,一个ClassLoader,一个interface数组,一个InvocationHandler。函数内部的大致流程如下:

首先对InvocationHandler对象进行了非空检测,随后是一些安全性检测,然后调用Class c1 = getProxyClass0(loader,intfs),根据入参的ClassLoaderinterface数组去生成代理类(回看前面我们手动生成代理类的class文件时的操作,这里的操作类似),随后调用final Constructor<?> cons = cl.getConstructor(constructorParams); 获取代理类的一个构造函数,这个构造函数的入参是一个InvocationHandler(因为constructorParams变量为{InvocationHandler.class}),最后执行cons.newInstance(new Object[]{h}),调用(代理类的)构造方法,将入参中的InvocationHandler对象传入,生成代理类实例,并返回该实例。

总结

  • JDK的动态代理是以接口为纽带,通过java.lang.reflect包下的ProxyInvocationHandler来实现的

  • 实现JDK动态代理,需要满足:

    • 被代理类需要实现某个接口

    • 需要一个实现InvocationHandler的类(假设类名为MyProxyFactory

      • 需要注入一个Object对象target,是目标类(被代理类)的一个实例,用于在invoke中调用它的方法

      • 重写invoke()方法,在invoke方法中执行代理的逻辑(一般是执行method.invoke(target)(调用目标类的方法),并在执行这行代码之前或之后进行一些额外操作(其实就是AOP的前置通知和后置通知))

      • 一般还会提供一个静态方法,用于获取代理类,如下

        public static Object getProxy(Object target){
            InvocationHandler h = new MyProxyFactory();
            return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),h);
        }
        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值