likes
comments
collection
share

JDK动态代理是如何通过反射机制实现的?源码解析说到动态代理必须得先说Class对象。 一个Class对象(可通过Cla

作者站长头像
站长
· 阅读数 53

什么是Class对象?

说到动态代理必须得先说Class对象。

一个Class对象(可通过Class.forName("")获取),代表正在运行中的java应用的类和接口,通过反射机制可以在运行时获取类信息,如:类名、成员属性、构造方法等。

其中 枚举类(enum)是一种特殊的类,注解 属于 一种接口 ,java中所有注解(@interface)都隐形地继承Annotation接口。

数组类也属于Class类,他的Class类对象被同类型数组(元素类型、数组维度相同)共享,具体看Class#getName()的说明。

什么是反射机制?

Java 反射机制是在运行时分析类、获取类信息、访问类的成员变量和方法的一种机制。可以动态地创建对象、调用方法、修改属性,增加了程序的灵活性和可扩展性。

什么是JDK动态代理?

JDK 动态代理是一种在运行时动态生成代理对象的机制。它基于接口实现,通过反射机制在运行时创建代理类并拦截对目标对象方法的调用,可在方法调用前后添加额外逻辑,如日志记录、权限校验等,实现对目标对象的功能增强,而无需修改目标对象的代码。

假设有这么个接口和实现类

接口IService

public interface IService {
    public void hello(); 
    public void say(String name);
    public void goods();
}

接口IService的实现类 ServiceImpl

public class ServiceImpl implements IService {

	@Override
	public void hello() {
		System.out.println("Hello China!");
	}

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

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

如果客户要求你在ServiceImpl 中的每个方法 执行前和执行后 分别输出 "目标方法执行前的输出。”和"目标方法执行后的输出。”,你是不是会想到这样:

public class ServiceProxy {
       
       private IService service;
       
       public ServiceProxy(IService service){
           this.service=service;
       }
       
	
            public void hello() {
                    System.out.println("目标方法执行前的输出。");
              service.hello();
              System.out.println("目标方法执行后的输出。");
            }

            @Override
            public void say(String name) {
                   System.out.println("目标方法执行前的输出。");
              service.say(name);
              System.out.println("目标方法执行后的输出。");
            }

            @Override
            public void goods() {
                    System.out.println("目标方法执行前的输出。");
              service.goods();
              System.out.println("目标方法执行后的输出。");
            }
}

            //然后再main方法中执行
            IService service = new ServiceImpl();
            ServiceProxy proxy = new ServiceProxy(service);
            proxy.hello();
            proxy.say("hello");
            proxy.goods();

固然可以实现需求,但这无疑造成了代码冗余,也不利于后期代码的维护。

那么我们是不是可以在运行时拦截目标方法,然后将额外的需求织入其中并生成代理对象,最后通过反射机制,使用Method对象、目标对象以及方法参数来定位目标方法并执行?这就是接下来讲的JDK动态代理。

实现步骤

首先我们的代理类要实现 InvocationHandler 接口

public class ServiceProxy implements InvocationHandler {
	private IService realService;

	public ServiceProxy(IService realService) {
		this.realService = realService;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("目标方法执行前的输出。");
                //通过反射来动态的调用目标方法
		Object result = method.invoke(realService, args);
		System.out.println("目标方法执行后的输出。");
		return result;
	}
//获取代理对象
	public Object getProxy() {
		return Proxy.newProxyInstance(realService.getClass().getClassLoader(), realService.getClass().getInterfaces(),
				this);
	}
}

然后编写测试类

public class ProxyTest {
	public static void main(String[] args) {
                //实例化目标对象
		IService real = new ServiceImpl();
                //传入目标对象,创建代理类
		ServiceProxy proxy = new ServiceProxy(real);
                //生成代理对象
		IService service = (IService) proxy.getProxy();
                //通过代理对象调用增强后的方法以及目标方法
		service.hello();
                //将生成的代理类的 class 文件保存到本地,方便反编译后查看
		createProxyClassFile();
	}

	@SuppressWarnings("unused")
	private static void createProxyClassFile() { // 将jdk生成的动态代理类的.class文件存放到本地
		String name = "$ServiceProxy";
		byte[] data = ProxyGenerator.generateProxyClass(name, new Class[] { IService.class });
		FileOutputStream out = null;
		try {
			out = new FileOutputStream(name + ".class");
			System.out.println((new File(name)).getAbsolutePath());
			out.write(data);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (null != out)
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
	}
}

源码解析

查看反编译后的代理类代码

public final class $ServiceProxy extends Proxy implements IService {
          private static Method m1;

          private static Method m5;

          private static Method m4;

          private static Method m2;

          private static Method m3;

          private static Method m0;

          public $ServiceProxy(InvocationHandler paramInvocationHandler) {
            super(paramInvocationHandler);
          }

          public final boolean equals(Object paramObject) {
            try {
              return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
            } catch (Error|RuntimeException error) {
              throw null;
            } catch (Throwable throwable) {
              throw new UndeclaredThrowableException(throwable);
            } 
          }

          public final void goods() {
            try {
              this.h.invoke(this, m5, null);
              return;
            } catch (Error|RuntimeException error) {
              throw null;
            } catch (Throwable throwable) {
              throw new UndeclaredThrowableException(throwable);
            } 
          }

          public final void hello() {
            try {
              this.h.invoke(this, m4, null);
              return;
            } catch (Error|RuntimeException error) {
              throw null;
            } catch (Throwable throwable) {
              throw new UndeclaredThrowableException(throwable);
            } 
          }

          public final String toString() {
            try {
              return (String)this.h.invoke(this, m2, null);
            } catch (Error|RuntimeException error) {
              throw null;
            } catch (Throwable throwable) {
              throw new UndeclaredThrowableException(throwable);
            } 
          }

          public final void say(String paramString) {
            try {
              this.h.invoke(this, m3, new Object[] { paramString });
              return;
            } catch (Error|RuntimeException error) {
              throw null;
            } catch (Throwable throwable) {
              throw new UndeclaredThrowableException(throwable);
            } 
          }

          public final int hashCode() {
            try {
              return ((Integer)this.h.invoke(this, m0, null)).intValue();
            } catch (Error|RuntimeException error) {
              throw null;
            } catch (Throwable throwable) {
              throw new UndeclaredThrowableException(throwable);
            } 
          }

          static {
            try {
              m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
              m5 = Class.forName("com.cen.proxy.IService").getMethod("goods", new Class[0]);
              m4 = Class.forName("com.cen.proxy.IService").getMethod("hello", new Class[0]);
              m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
              m3 = Class.forName("com.cen.proxy.IService").getMethod("say", new Class[] { Class.forName("java.lang.String") });
              m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
              return;
            } catch (NoSuchMethodException noSuchMethodException) {
              throw new NoSuchMethodError(noSuchMethodException.getMessage());
            } catch (ClassNotFoundException classNotFoundException) {
              throw new NoClassDefFoundError(classNotFoundException.getMessage());
            } 
          }
}

我们可以看到,代理类先通过Class.forName 去获取被代理接口的Class对象,然后再调用Class对象的getMethod方法,通过方法名和方法传参来返回一个目标方法的Method对象

Class.forName("com.cen.proxy.IService").getMethod("hello", new Class[0])

该Method对象会在$ServiceProxy调用方法的时候使用到,他将会作为参数传入

ServiceProxy#invoke(Object proxy, Method method, Object[] args)

并被使用

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(realService, args); 
    return result; 
}

例如: hello()的调用,调用链是这样的:

步骤1. main方法

service.hello()

步骤2. $ServiceProxy的static代码块

m4 = Class.forName("com.cen.proxy.IService").getMethod("hello", new Class[0])

步骤3. $ServiceProxy#hello()

public final void hello() {
            try {
              this.h.invoke(this, m4, null);
              return;
            } catch (Error|RuntimeException error) {
              throw null;
            } catch (Throwable throwable) {
              throw new UndeclaredThrowableException(throwable);
            } 
          }

步骤4. ServiceProxy#invoke

   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("目标方法执行前的输出。"); 
        Object proxyObj = method.invoke(realService, args); 
        System.out.println("目标方法执行后的输出。"); return proxyObj; 
}

大概就这样吧~ 欢迎交流学习!

转载自:https://juejin.cn/post/7422548463235153930
评论
请登录