讲一些你所不知道的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

原文地址:https://blog.51cto.com/13754022/2388419

时间: 2024-10-10 13:44:40

讲一些你所不知道的Java动态代理的相关文章

深入洞见:你所不知道的Java 对象序列化的5件事儿

关于 Java 对象序列化的一些有用的小知识 不易理解,但对于解决 Java 编程挑战迟早有用. 将 Java 对象序列化 API它从一开始就存在于 JDK 1.1 中.本文介绍的关于序列化的 几件事情将说服您重新审视那些标准 Java API. Java 序列化简介 Java 对象序列化是 JDK 1.1 中引入的一组开创性特性之一,用于作为一种将 Java 对象的状态转换为字节数组,以便存储或传输的机制,以后,仍可以将字节数组转换回 Java 对象原有的状态. 到现在为止,还没有看到什么新鲜

你所不知道的java编程思想

读thinking in java这本书的时候,有这么一句话“在编译单元的内部,可以有一个公共(public)类,它必须拥有与文件相同的名字” 有以下疑问: 在一个类中说可以有一个public类,那是不是也就是说可以没有呢? 抱着这个问题进行了以下的实验:(文件名是Test1.java) class Test1 { public static void main(String[] args){ if(args != null && args.length > 0){ System.o

【总结】你所不知道的Java序列化

我们都知道,Java序列化可以让我们记录下运行时的对象状态(对象实例域的值),也就是我们经常说的对象持久化 .这个过程其实是非常复杂的,这里我们就好好理解一下Java的对象序列化. 1. 首先我们要搞清楚,Java对象序列化是将 对象的实例域数据( 包括private私有域) 进行持久化存储.而并非是将整个对象所属的类信息进行存储. 其实了解JVM的话,我们就能明白这一点了.实际上堆中所存储的对象包含了实例域数据值以及指向类信息的地址,而对象所属的类信息却存放在方法区中.当我们要对持久层数据反序

你所不知道的Java序列化

我们都知道,Java序列化可以让我们记录下运行时的对象状态(对象实例域的值),也就是我们经常说的对象持久化 .这个过程其实是非常复杂的,这里我们就好好理解一下Java的对象序列化. 1. 首先我们要搞清楚,Java对象序列化是将 对象的实例域数据( 包括private私有域) 进行持久化存储.而并非是将整个对象所属的类信息进行存储. 其实了解JVM的话,我们就能明白这一点了.实际上堆中所存储的对象包含了实例域数据值以及指向类信息的地址,而对象所属的类信息却存放在方法区中.当我们要对持久层数据反序

你所不知道的 Java 之 HashCode

之所以写HashCode,是因为平时我们总听到它.但你真的了解hashcode吗?它会在哪里使用?它应该怎样写? 相信阅读完本文,能让你看到不一样的hashcode. 使用hashcode的目的在于:使用一个对象查找另一个对象.对于使用散列的数据结构,如 HashSet.HashMap.LinkedHashSet.LinkedHashMap ,如果没有很好的覆写键的hashcode()和equals()方法,那么将无法正确的处理键. 请对以下代码中 Person 覆写hashcode()方法,看

你所不知道的五件事情--java.util.concurrent(第二部分)

这是Ted Neward在IBM developerWorks中5 things系列文章中的一篇,仍然讲述了关于Java并发集合API的一些应用窍门,值得大家学习.(2010.06.17最后更新) 摘要:除了便于编写并发应用的集合API外,java.util.concurrent还引入了其它的预置程序组件,这些组件能辅助你在多线程应用中控制和执行线程.Ted Neward再介绍了五个来自于java.util.concurrent的Java编程必备窍门. 通过提供线程安全,性能良好的数据结构,并发

Java 动态代理机制分析及扩展,第 1 部分

引言 Java 动态代理机制的出现,使得 Java 开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类.代理类会负责将所有的方法调用分派到委托对象上反射执行,在分派执行的过程中,开发人员还可以按需调整委托类对象及其功能,这是一套非常灵活有弹性的代理框架.通过阅读本文,读者将会对 Java 动态代理机制有更加深入的理解.本文首先从 Java 动态代理的运行机制和特点出发,对其代码进行了分析,推演了动态生成类的内部实现. 回页首 代理:设计模式 代理是一种常用的设计

Android中Context详解 ---- 你所不知道的Context

转载至 :http://blog.csdn.net/qinjuning 前言:本文是我读<Android内核剖析>第7章 后形成的读书笔记 ,在此向欲了解Android框架的书籍推荐此书. 大家好,  今天给大家介绍下我们在应用开发中最熟悉而陌生的朋友-----Context类 ,说它熟悉,是应为我们在开发中 时刻的在与它打交道,例如:Service.BroadcastReceiver.Activity等都会利用到Context的相关方法 : 说它陌生,完全是 因为我们真正的不懂Context

Android中Context详解 ---- 你所不知道的Context (转载)

Android中Context详解 ---- 你所不知道的Context (转载) http://blog.csdn.net/qinjuning 大家好,  今天给大家介绍下我们在应用开发中最熟悉而陌生的朋友-----Context类 ,说它熟悉,是应为我们在开发中 时刻的在与它打交道,例如:Service.BroadcastReceiver.Activity等都会利用到Context的相关方法 : 说它陌生,完全是 因为我们真正的不懂Context的原理.类结构关系.一个简单的问题是,一个应用