Java JDK动态代理

态代理动:

代理类在程序运行时创建,而非在程序运行前手动编码定义的。

动态代理主要分为两种:1.JDK动态代理(基于接口的代理,只能对实现了接口的类生成代理)

                                       2.CGLib(基于类的代理,代理类去继承目标类,然后重写其中目标类的方法)

JDK动态代理与CGLib对比:

1.Spring选择用JDK还是CGLib

如果Bean实现接口,会选择使用JDK动态代理。

如果Bean未实现接口,则使用CGLib。在Spring配置中加入<aop:aspectj-autoproxy proxy-target-class=“true”/>可配置强制使用CGLib.

2.JDK和CGLib的性能对比

CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,在JDK1.6之前比使用Java反射效率要高。

在JDK1.6、JDK1.7、JDK1.8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLib代理效率,只有当进行大量调用的时候,JDK1.6和JDK1.7比CGLib代理效率低一点,但是到JDK1.8的时候,JDK代理效率高于CGLib代理。

(本文主要分享一下JDK动态代理)

先把结论写上来:

java的单继承,动态生成的代理类已经继承了Proxy类,就不能再继承其他的类,所以只能靠实现被代理类的接口的形式生成代理类,所以JDK动态代理只能对实现了接口的类生成代理。

执行proxy.say() 时,为什么会自动自动调用ProxyServiceImpl的invoke方法?

因为JDK生成的最终真正的代理类,它继承自Proxy并实现了我们定义的ProxyService接口,每一个被代理接口的方法都会在代理类中生成一个对应的实现方法,并在实现方法中最终调用invocationHandler 的invoke方法,这就解释了为何执行代理类的方法会自动进入到我们自定义的invocationHandler的invoke方法中(MyInvocationHandler的invoke方法),然后在我们的invoke方法中再利用jdk反射的方式去调用真正的被代理类的业务方法,而且还可以在方法的前后去加一些我们自定义的逻辑。

JDK动态代理使用:

1.创建被代理的类以及接口
2.创建一个实现接口InvocationHandler的类,它必须实现invoke方法
3.通过Proxy的静态方法 newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)创建一个代理
4.通过代理调用方法

//需要动态代理的接口
public interface ProxyService {
    void say();
}

public class ProxyServiceImpl implements ProxyService{

    @Override
    public void say() {
        System.out.println("hello");
    }
}

public class MyInvocationHandler implements InvocationHandler {
    //这个就是我们要代理的真实对象
    private Object target;
    //构造方法,给我们要代理的真实对象赋初值
    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    /**
     * 该方法负责集中处理动态代理类上的所有方法调用。
     * 调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行
     *
     * @param proxy  代理类实例
     * @param method 被调用的方法对象
     * @param args   调用参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("invoke start");
        //当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
        Object result = method.invoke(target, args);
        System.out.println("invoke end");
        return result;
    }
}

public class Test {
    public static void main(String[] args) {
        //将生成的字节码保存到本地
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
        ProxyService proxy = (ProxyService) Proxy.newProxyInstance(Test.class.getClassLoader(),
                new Class[]{ProxyService.class}, new MyInvocationHandler(new ProxyServiceImpl()));
        proxy.say();
}

字节码反编译:

package com.sun.proxy;

import com.qijiu.hr.service.ProxyService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

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

    public $Proxy0(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 say() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    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.qijiu.hr.service.ProxyService").getMethod("say");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}
public final class $Proxy0 extends Proxy implements ProxyService

我们可以看到 生成的代理类 $Proxy0 继承了Proxy 并实现了 ProxyService。 因为它已经继承了Proxy。所以就解释了我们上面结论中的,JDK动态代理只能对实现了接口的类生成代理。

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

这里的 super.h.invoke(this, m3, (Object[])null); 其实调用的就是MyInvocationHandlerImpl 的 invoke方法。

 ProxyService proxy = (ProxyService) Proxy.newProxyInstance(AAAA.class.getClassLoader(),
                new Class[]{ProxyService.class}, new MyInvocationHandler(new ProxyServiceImpl()));
        proxy.say();

所以这就解释了,执行proxy.say();时 会会自动自动调用MyInvocationHandlerImpl 的invoke方法。

至此可以得知  jdk动态代理的大致逻辑即是:传入代理类 类加载器,与接口数组和自定义的InvocationHandler,然后通过分析接口信息生成java文件的字节码数据,然后调用本地方法将类加载到内存中,最后返回构造参数为InvocationHandler的代理类,该类实现代理接口,并继承Proxy类(所以jdk动态代理只能代理接口,java单继承),我们调用方法实际上是调用代理类的方法,代理类则可以通过我们传入的InvocationHandler反射调用原本的方法来实现无侵入的修改原有方法逻辑

 

关于CGLib 可参考以下博文:

https://blog.csdn.net/yhl_jxy/article/details/80633194

https://www.cnblogs.com/wyq1995/p/10945034.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值