jdk动态代理底层原理

首先我们明确静态代理的缺点:重用性不强:

  1. 假若一个系统中有很多个目标类(service),就要一个一个的为这些service创建代理

  1. 一个service中的很多方法都需要增强,这时候代理中就会有很多的重复代码

静态代理的缺点可以通过动态代理来解决:

jdk动态代理实现:

  1. 创建目标StudentService接口并创建目标类StudentServiceImpl

public interface StudentService {
    
    int sNums();
    
}
public class StudentServiceImpl implements StudentService{
    @Override
    public int sNums() {

        int num = 3;
        System.out.println("查询学生数量...");

        return num;
    }
}
  1. 创建实现了InvocationHandler接口实现类EnhanceHandler,通过实现接口中的invoke方法进行对StudentServiceImpl中sNums方法的增强

InvocationHandler中的invoke()方法有三个参数:

1):Object proxy :生成的代理对象

2):Method method:执行目标方法(就是需要增强的方法),通过反射中的invoke执行,

注意,这里的invoke和InvocationHandler中的invoke不一样,前者是反射中执行Method的方法

3):Object[] args:参数数组,用作method中的参数

public class EnhanceHandler implements InvocationHandler {

    //要生成代理的类
    Object obj = null;

    public EnhanceHandler(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        int res;
        //判断当前方法是否是sNums方法,是的话对其进行增强
        if("sNums".equals(method.getName())) {
            //利用反射调用原本的sNums方法
            res = (Integer)method.invoke(obj, args);

            //对method方法(sNums)进行功能增强
            System.out.println("功能增强...");
            res = res + 4;
        }else{
            //如果不是sNums方法就不进行增强
            res = (Integer)method.invoke(obj,args);
        }
        return res;
    }
}
  1. 测试

通过jdk反射包下的Proxy类中的newProxyInstance()方法创建StudentServiceImpl的代理对象

newProxyInstance()方法中需要三个参数

①:需要创建代理的类加载器

②:目标类实现的接口

③:实现了InvocationHandler接口的实现类

通过这三个参数jdk帮助我们生成StudentServiceImpl的代理对象

public class test {
    public static void main(String[] args) {
        //目标执行类
        StudentService studentService = new StudentServiceImpl();
        //自定义的实现了InvocationHandler接口的增强处理器类
        EnhanceHandler handler = new EnhanceHandler(studentService);

        //利用jdk反射中的代理Proxy类创建代理,强转为StudentService
        //jdk代理类与目标类是兄弟关系,所以只能强转为接口类型
        StudentService studentServiceProxy = (StudentService) Proxy.newProxyInstance(StudentServiceImpl.class.getClassLoader(),
                StudentServiceImpl.class.getInterfaces(), handler);

        //调用sNums()
        int sNums = studentServiceProxy.sNums();
        System.out.println(sNums);
    }
}

//执行结果
/*
    查询学生数量...
    功能增强...
    7
*/

jdk动态代理它的底层原理到底是什么,通过获取生成的代理对象,观察底层原理


/*
        可以看见生成的代理类继承了Proxy并实现了StudentService接口,这就是为什么jdk动态代理中
    需要生成代理的类必须实现接口,代理类通过实现目标类的实现的接口,使用反射获取到接口中的方
    法,通过重写来进行目标类方法的增强。
*/
public final class $Proxy1 extends Proxy implements StudentService {

    /*
    可以看到其实jdk通过反射帮助我们实现了StudentService接口中的方法,并重写了equals()
    hashCode(),toString()这些方法的
    */
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;
    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("LKKKK.service.StudentService").getMethod("sNums");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }

    /*
    我们发现重写后的sNums()方法内部调用了父类中的h.invoke()方法,这里的h是什么呢?通过查看
    父类Proxy发现这里的h,就是我们自定义的实现了InvocationHandler接口的EnhanceHandler对象

        protected InvocationHandler h;
        */
    public final int sNums() throws  {
        try {
        /*这里也就发现我们通过生成的StudentServiceProxy代理对象调用sNums()方法其实就是调用我们
    自定义的InvocationHandler中重写的invoke()方法,invoke()中的三个参数分别是this,(生成的
    代理对象(本身),m3,(通过反射生成的m3 = Class.forName("LKKKK.service.StudentService").getMethod("sNums");),)
    (Object[]),null其实就是sNums中的参数,这不过我们的代码中sNums()方法中并没有传参,所以
    这里为null*/
            return (Integer)super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    //通过有参构造,将传进来的InvocationHandler对象给父类Proxy中的h属性
    public $Proxy1(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 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);
        }
    }
}

下面方法可以获取生成的代理对象

public static void ProxyClass(String path){
        byte[] $proxy1 = ProxyGenerator.generateProxyClass("$Proxy1", StudentServiceImpl.class.getInterfaces());
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(new File(path + "$Proxy1.class"));
            fos.write($proxy1);
        }catch (Exception e){

        }finally {
            if(fos != null){
                try {
                    fos.close();
                }catch (IOException e){

                }
            }
        }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值