阅读 103

讲一些你所不知道的Java动态代理

简介

Proxy 是设计模式中的一种。当需要在已存在的 class 上添加或修改功能时,可以通过创建 proxy object 来实现

通常 proxy object 和被代理对象拥有相同的方法,并且拥有被代理对象的引用,可以调用其方法

代理模式应用场景包括

  • 在方法执行前后打印和记录日志
  • 认证、参数检查
  • lazy instantiation (Hibernate, Mybatis)
  • AOP (transaction)
  • mocking

代理有两种实现方式

  • 静态代理:在编译时期,创建代理对象
  • 动态代理:在运行时期,动态创建

对于重复性工作,如打印日志,静态代理需要为每个 class 都创建 proxy class,过程繁琐和低效,而动态代理通过使用反射在运行时生成 bytecode 的方式来实现,更加方便和强大

过程

因为 JDK 自带的 Dynamic proxy 只能够代理 interfaces,因此被代理对象需要实现一个或多个接口。

先来看一些概念:

  • proxy interface proxy class 实现的接口
  • proxy class 运行时创建的代理 class,并实现一个或多个 proxy interface
  • proxy instance proxy class 的实例
  • InvocationHandler 每个 proxy instance 都有一个关联的 invocation handler,当调用 proxy 对象的方法时,会统一封装,并转发到invoke()方法

InvocationHandler 接口的定义如下

package java.lang.reflect;
public interface InvocationHandler {
	public Object invoke(Object proxy, Method method, Object[] args)
	        throws Throwable;
}
复制代码

只定义了一个方法invoke(),参数含义如下

  • Object proxy生成的代理对象
  • Method method调用的方法,类型为 java.lang.reflect.Method
  • Object[] args调用方法的参数,array of objects

简单来说就是,调用 proxy object 上的方法,最终都会转换成对关联InvocationHandlerinvoke()方法的调用

可以使用java.lang.reflect.Proxy的静态方法newProxyInstance来创建Proxy object

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
	...
}
复制代码

参数说明

  • loader 定义 proxy class 的 ClassLoader
  • interfaces 需要代理的接口
  • h 关联的 InvocationHandler

例子

使用动态代理打印方法的执行耗时

定义代理接口

public interface Foo {
    String doSomething();
}
复制代码

实现接口

public class FooImpl implements Foo {
    @Override
    public String doSomething() {
        return "finished";
    }
}
复制代码

定义 InvocationHandler,target 为被代理对象的引用,在方法执行完后打印耗时

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class TimingInvocationHandler implements InvocationHandler {
	private Object target;
	public TimingInvocationHandler(Object target) {
		this.target = target;
	}
	public Object invoke(Object proxy, Method method, Object[] args)
	            throws Throwable {
		long start = System.nanoTime();
		Object result = method.invoke(target, args);
		long elapsed = System.nanoTime() - start;
		System.out.println(String.format("Executing %s finished in %d ns",
		                method.getName(),
		                elapsed));
		return result;
	}
}
复制代码

测试

import org.junit.Test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class DynamicProxyTest {
	@Test
	    public void test() {
		ClassLoader cl = DynamicProxyTest.class.getClassLoader();
		Class[] interfaces = new Class[]{Foo.class};
		FooImpl fooImpl = new FooImpl();
		InvocationHandler timingInvocationHandler = new TimingInvocationHandler(fooImpl);
		Foo foo = (Foo) Proxy.newProxyInstance(cl, interfaces, timingInvocationHandler);
		foo.doSomething();
	}
}
复制代码

执行完会打印类似

Executing doSomething finished in 23148 ns
复制代码

细节

生成 proxy class 的一些属性和细节

  • public, final, and not abstract.
  • 类名不确定,以 $Proxy 开头
  • 继承 java.lang.reflect.Proxy,且 Proxy 实现了 java.io.Serializable 接口,因此 proxy instance 是可以序列化的
  • 按照 Proxy.newProxyInstance() 传入 interfaces 参数中的接口顺序来实现接口
  • 在 proxy class 上调用 getInterfaces,getMethods,getMethod 方法,会返回实现的接口中定义的方法,顺序和创建时的参数保持一致
  • 当调用 proxy instance 同名、同 parameter signature 方法时,invoke() 方法的 Method 参数会是最早定义这个方法的 interface 的方法,无论实际调用的方法是什么
  • 当 Foo 为实现的代理接口之一时, proxy instanceof Foo 返 true,并且可以转换 (Foo) proxy
  • Proxy.getInvocationHandler 静态方法会返回 proxy object 关联的 invocation handler

读者福利

分享免费学习资料

针对于Java程序员,我这边准备免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)

为什么某些人会一直比你优秀,是因为他本身就很优秀还一直在持续努力变得更优秀,而你是不是还在满足于现状内心在窃喜!希望读到这的您能点个小赞和关注下我,以后还会更新技术干货,谢谢您的支持!

资料领取方式:加入Java技术交流群963944895点击加入群聊,私信管理员即可免费领取

如何成为一个有逼格的Java架构师

怎么提高代码质量?——来自阿里P8架构师的研发经验总结

阿里P8分享Java架构师的学习路线,第六点尤为重要

每个Java开发者应该知道的八个工具

想面试Java架构师?这些最基本的东西你都会了吗?

画个图来找你的核心竞争力,变中年危机为加油站

哪有什么中年危机,不过是把定目标当成了有计划

被裁员不是寒冬重点,重点是怎么破解职业瓶颈

关注下面的标签,发现更多相似文章
评论