动态代理--基础--02--jdk原理细节

动态代理–基础–02–jdk原理细节


代码位置

https://gitee.com/DanShenGuiZu/learnDemo/tree/master/动态代理/demo1

1、jdk动态代理的方法

1.1、Proxy.newProxyInstance(ClassLoader loader, Class< ?>[] interfaces, InvocationHandler h)

是 Java 反射 API 中的一个方法,用于动态地创建一个实现了指定接口的代理类实例。

1.1.1、参数:loader

  • 定义代理类的类加载器
  • 通常,你可以使用被代理对象的类加载器,以确保代理类和被代理类在同一个类加载器的命名空间中。
    • 例如:如果你正在为一个名为 MyClass 的对象创建代理,你可以使用 MyClass.class.getClassLoader() 作为此参数。

1.1.2、参数:interfaces

  • 这是一个接口数组,指定了代理类要实现的接口
    • 你必须至少指定一个接口。如果你的类实现了多个接口,你需要列出所有你想要代理的接口。
  • 代理类将实现这些接口,并将调用转发到 InvocationHandler。

1.1.3、参数:h

  • 这是一个实现了 InvocationHandler接口的对象,它负责处理在代理实例上调用方法时的逻辑。
  • 当通过代理实例调用方法时,invoke 方法将被调用,并传入代理实例、被调用的方法和方法参数。你可以在 invoke 方法中实现自定义的逻辑,比如记录日志、性能监控或改变方法的行为。

1.1.4、返回

一个带有代理类的指定调用处理程序的代理实例,它由指定的类加载器定义,并实现指定的接口
理解:返回一个实现接口的代理类,比如 Userdao userdao =new UserdaoImpl();

1.2、InvocationHandler.invoke(Object proxy, Method method, Object[] args)

用于处理代理实例上的方法调用

1.2.1、参数:proxy

  • 这是代理对象的引用。
  • 通常,在invoke 方法的实现中,你不需要直接使用这个参数,除非你需要调用代理对象的其他方法或进行其他与代理对象本身相关的操作。

1.2.2、method

这是被调用的方法的 Method 对象。它包含了被调用方法的名称、参数类型、返回类型等信息。你可以通过这个对象来获取方法的详细信息,并执行相应的逻辑。

1.2.3、args

  • 这是传递给被调用方法的参数数组。
  • 如果方法没有参数,那么这个数组为 null。否则,它包含了调用方法时传递的所有参数。

1.2.3、返回

返回 代理实例方法的执行结果

2、代码实现

 
package feizhou.demo1.business.demo.jdk;

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

/**
 * JDK动态代理需要实现InvocationHandler接口
 */
public class JdkProxy implements InvocationHandler {
    //需要代理的目标对象
    private Object target;


    //通过构造方法,为target赋值
    public JdkProxy(Object target) {
        this.target = target;
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("JDK动态代理,监听开始!");
        Object result = method.invoke(target, args);
        System.out.println("JDK动态代理,监听结束!");
        return result;
    }


    public static void main(String[] args) {

        UserImpl userImpl = new UserImpl();
        //实例化JDKProxy对象
        JdkProxy jdkProxy = new JdkProxy(userImpl);
        //JDK动态代理只能针对实现了接口的类进行代理,newProxyInstance 函数所需参数就可看出
        UserDao user = (UserDao) Proxy.newProxyInstance(userImpl.getClass().getClassLoader(), userImpl.getClass().getInterfaces(), jdkProxy);
        //执行新增方法
        user.addUser();
    }

}

 
输出:
JDK动态代理,监听开始!
调用了UserImpl的addUser
JDK动态代理,监听结束!
 
public interface UserDao {
    void addUser();
}



public class UserImpl implements UserDao{
    @Override
    public void addUser() {
        System.out.println("调用了UserImpl的addUser");
    }
}

3、源码分析

3.1、源码分析

我们从Proxy.newProxyInstance方法开始

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h).. {
                                      
    //......
    //获取代理的对象的class对象,这里使用缓存,如果代理对象字节码存在就直接返回,不存在就创建
    Class<?> cl = getProxyClass0(loader, intfs);
    //给class 对象构造函数传入InvocationHandler 实例化对象

    return newInstance(cons, ih);
    //....
}


通过上图可以得到ProxyClassFactory是默认就初始化了,接下来继续

ProxyGenerator.generateProxyClass(..):是生成字节流的方法,通过native方法生成Class对象

3.2、验证:ProxyGenerator.generateProxyClass会生成Class对象

 
package feizhou.demo1.business.demo.jdk;

 
import sun.misc.ProxyGenerator;

import java.io.FileOutputStream;
import java.io.IOException;


public class JDK2 {


    public static void main(String[] args) throws IOException {


        //构建代理类名称
        String proxyPkg = "com.sun.proxy.userDao";
        String proxyClassNamePrefix = "$Proxy";
        long num = 1234;
        String proxyName = proxyPkg + proxyClassNamePrefix + num;


        //被代理的类对象
        Class userDaoClass = UserDao.class;

        //生成字节码
        byte[] classFile = ProxyGenerator.generateProxyClass(proxyName, new Class[]{userDaoClass});

        //打印class文件路径
        String paths = userDaoClass.getResource(".").getPath();
        System.out.println(paths);

        FileOutputStream out = null;

        //保留到硬盘中
        out = new FileOutputStream(paths + proxyName + ".class");
        out.write(classFile);
        out.flush();
        out.close();
    }


}

生成的字节码文件(只显示重要的)

//继承Proxy ,实现业务接口
public final class userDao$Proxy1234 extends Proxy implements UserDao {

    private static Method m3;

    //构造方法
    public userDao$Proxy1234(InvocationHandler paramInvocationHandler) throws Exception {
        super(paramInvocationHandler);
    }

    //调用InvocationHandler的invoke方法
    public final void addUser()   throws Error {
        try {
            this.h.invoke(this, m3, null);
            return;
        } catch  ....
    }

    static静态块加载每个方法的Method对象
    static {
        try {
            m3 = Class.forName("cn.feizhou.Test.jdk.UserDao").getMethod("addUser", new Class[0]);
            return;
        } catch  ...
    }
}

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值