java动态代理原理及解析

代理:设计模式

代理模式是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个真实对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。

通过代理层这一中间层,有效的控制对于真实委托类对象的直接访问,同时可以实现自定义的控制策略(Spring的AOP机制),设计上获得更大的灵活性。

java动态代理的类和接口(jdk1.6源码)

1,java.lang.reflect.Proxy:动态代理机制的主类,提供一组静态方法为一组接口动态的生成对象和代理类。

// 方法 1: 该方法用于获取指定代理对象所关联的调用处理器
public static InvocationHandler getInvocationHandler(Object proxy) 

// 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象
public static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces)

// 方法 3:该方法用于判断指定类对象是否是一个动态代理类
public static boolean isProxyClass(Class<?> cl) 

// 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
public static Object newProxyInstance(ClassLoader loader,
 Class<?>[] interfaces,InvocationHandler h)

2,java.lang.reflect.InvocationHandler:调用处理器接口,自定义invokle方法,用于实现对于真正委托类的代理访问。

/**
 该方法负责集中处理动态代理类上的所有方法调用。
 第一个参数既是代理类实例,
 第二个参数是被调用的方法对象
 第三个方法是调用参数。
 调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行
*/
public Object invoke(Object proxy, Method method, Object[] args)
    throws Throwable;

3,java.lang.ClassLoader:类装载器类,将类的字节码装载到 Java 虚拟机(JVM)中并为其定义类对象,然后该类才能被使用。Proxy类与普通类的唯一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中

每次生成动态代理类对象时都需要指定一个类装载器对象:newProxyInstance()方法第一个参数

动态代理机制

java动态代理创建对象的过程为如下步骤:

1,通过实现 InvocationHandler 接口创建自己的调用处理器;

// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
// 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用
InvocationHandler handler = new InvocationHandlerImpl(..); 

2,通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;

// 通过 Proxy 为包括 Interface 接口在内的一组接口动态创建代理类的类对象
Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... }); 

3,通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;

// 通过反射从生成的类对象获得构造函数对象
Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });

4,通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

// 通过构造函数对象创建动态代理类实例
Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });

为了简化对象创建过程,Proxy类中的newProxyInstance方法封装了2~4,只需两步即可完成代理对象的创建。

// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
InvocationHandler handler = new InvocationHandlerImpl(..); 

// 通过 Proxy 直接创建动态代理类实例
Interface proxy = (Interface)Proxy.newProxyInstance( classLoader,
     new Class[] { Interface.class },
     handler );

动态代理的注意点:

1,包:代理接口是public,则代理类被定义在顶层包(package为空),否则(default),代理类被定义在该接口所在包,

2,生成的代理类为public final,不能被继承,

3,类名:格式是“$ProxyN”,N是逐一递增的数字,代表Proxy被第N次动态生成的代理类,要注意,对于同一组接口(接口的排列顺序也相同),不会重复创建动态代理类,而是返回一个先前已经创建并缓存了的代理类对象。提高了效率。

4,类继承关系:

Proxy 类是它的父类,这个规则适用于所有由 Proxy 创建的动态代理类。(也算是java动态代理的一处缺陷,java不支持多继承,所以无法实现对class的动态代理,只能对于Interface的代理)而且该类还实现了其所代理的一组接口,这就是为什么它能够被安全地类型转换到其所代理的某接口的根本原因。

5,代理类的根类 java.lang.Object 中有三个方法也同样会被分派到调用处理器的 invoke 方法执行,它们是 hashCode,equals 和 toString,

代码在反编译中

一个动态代理的demo

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

public class HelloServiceProxy implements InvocationHandler {  

    private Object target;
    /**
     * 绑定委托对象并返回一个【代理占位】
     * @param target 真实对象
     * @return  代理对象【占位】
     */
    public  Object bind(Object target, Class[] interfaces) {
        this.target = target;
        //取得代理对象
       return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
    }    

    @Override
    /**
     * 同过代理对象调用方法首先进入这个方法.
     * @param proxy --代理对象
     * @param method -- 方法,被调用方法.
     * @param args -- 方法的参数
     */
    public Object invoke(Object proxy , Method method, Object[] args) throws Throwable {
        System.err.println("############我是JDK动态代理################");
        Object result = null;
        //反射方法前调用
        System.err.println("我准备说hello。");
        //反射执行方法  相当于调用target.sayHelllo;
        result=method.invoke(target, args);
        //反射方法后调用.
        System.err.println("我说过hello了");
        return result;
    }
} 

其中,bind方法中的newProxyInstanc方法,就是生成一个代理对象,第一个参数是类加载器,第二个参数是真实委托对象所实现的的接口(代理对象挂在那个接口下),第三个参数this代表当前HelloServiceProxy类,换句话说是使用HelloServiceProxy作为对象的代理。

invoke方法有三个参数:第一个proxy是代理对象,第二个是当前调用那个方法,第三个是方法的参数。

public class ProxyTest {  

    public static void main(String[] args) {
        HelloServiceProxy proxy = new HelloServiceProxy();
        HelloService service = new HelloServiceImpl();
        //绑定代理对象。
        service = (HelloService) proxy.bind(service, new Class[] {HelloService.class});
        //这里service经过绑定,就会进入invoke方法里面了。
        service.sayHello("张三");
    }
}  

测试结果:

############我是JDK动态代理################
我准备说hello。
hello 张三
我说过hello了

源码跟踪

Proxy 类

// 映射表:用于维护类装载器对象到其对应的代理类缓存
private static Map loaderToCache = new WeakHashMap(); 

// 标记:用于标记一个动态代理类正在被创建中
private static Object pendingGenerationMarker = new Object(); 

// 同步表:记录已经被创建的动态代理类类型,主要被方法 isProxyClass 进行相关的判断
private static Map proxyClasses = Collections.synchronizedMap(new WeakHashMap()); 

// 关联的调用处理器引用
protected InvocationHandler h;

Proxy 静态方法 newProxyInstance

public static Object newProxyInstance(ClassLoader loader,
            Class<?>[] interfaces,
            InvocationHandler h)
            throws IllegalArgumentException { 

    // 检查 h 不为空,否则抛异常
    if (h == null) {
        throw new NullPointerException();
    } 

    // 获得与制定类装载器和一组接口相关的代理类类型对象
    /*
     * Look up or generate the designated proxy class.
     */
        Class<?> cl = getProxyClass0(loader, interfaces); 

    // 通过反射获取构造函数对象并生成代理类实例
    /*
     * Invoke its constructor with the designated invocation handler.
     */
    try {
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            SecurityManager sm = System.getSecurityManager();
            if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
                // create proxy instance with doPrivilege as the proxy class may
                // implement non-public interfaces that requires a special permission
                return AccessController.doPrivileged(new PrivilegedAction<Object>() {
                    public Object run() {
                        return newInstance(cons, ih);
                    }
                });
            } else {
                return newInstance(cons, ih);
            }
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString());
    }
    }

private static Object newInstance(Constructor<?> cons, InvocationHandler h) {
        try {
            return cons.newInstance(new Object[] {h} );
        } catch (IllegalAccessException e) {
            throw new InternalError(e.toString());
        } catch (InstantiationException e) {
            throw new InternalError(e.toString());
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString());
            }
        }
    }

动态代理真正的关键是在 getProxyClass0 方法,

getProxyClass0方法分析

方法分为四个步骤:

1,对这组接口进行一定程度的安全检查

检查接口类对象是否对类装载器可见并且与类装载器所能识别的接口类对象是完全相同的,还会检查确保是 interface 类型而不是 class 类型。

2,从 loaderToCache 映射表中获取以类装载器对象为关键字所对应的缓存表,如果不存在就创建一个新的缓存表并更新到 loaderToCache。

loaderToCache存放键值对(接口名字列表,动态生成的代理类的类对象引用)。当代理类正在被创建时它会临时保存(接口名字列表,pendingGenerationMarker)。标记 pendingGenerationMarke 的作用是通知后续的同类请求(接口数组相同且组内接口排列顺序也相同)代理类正在被创建,请保持等待直至创建完成。

/*
     * Find or create the proxy class cache for the class loader.
     */
    Map cache;
    synchronized (loaderToCache) {
        cache = (Map) loaderToCache.get(loader);
        if (cache == null) {
        cache = new HashMap();
        loaderToCache.put(loader, cache);
        }
     }
。。。。。
do {
    // 以接口名字列表作为关键字获得对应 cache 值
    Object value = cache.get(key);
    if (value instanceof Reference) {
        proxyClass = (Class) ((Reference) value).get();
    }
    if (proxyClass != null) {
        // 如果已经创建,直接返回
        return proxyClass;
    } else if (value == pendingGenerationMarker) {
        // 代理类正在被创建,保持等待
        try {
            cache.wait();
        } catch (InterruptedException e) {
        }
        // 等待被唤醒,继续循环并通过二次检查以确保创建完成,否则重新等待
        continue;
    } else {
        // 标记代理类正在被创建
        cache.put(key, pendingGenerationMarker);
        // break 跳出循环已进入创建过程
        break;
} while (true);

3,动态创建代理类的class对象

/**
         * Choose a name for the proxy class to generate.
         */
        long num;
        synchronized (nextUniqueNumberLock) {
            num = nextUniqueNumber++;
        }
        String proxyName = proxyPkg + proxyClassNamePrefix + num;
        /*
         * Verify that the class loader hasn‘t already
         * defined a class with the chosen name.
         */

        // 动态地生成代理类的字节码数组
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
            proxyName, interfaces);
        try {
        // 动态地定义新生成的代理类
            proxyClass = defineClass0(loader, proxyName,
            proxyClassFile, 0, proxyClassFile.length);
        } catch (ClassFormatError e) {
            /*
             * A ClassFormatError here means that (barring bugs in the
             * proxy class generation code) there was some other
             * invalid aspect of the arguments supplied to the proxy
             * class creation (such as virtual machine limitations
             * exceeded).
             */
            throw new IllegalArgumentException(e.toString());
        }
        // 把生成的代理类的类对象记录进 proxyClasses 表
        proxyClasses.put(proxyClass, null);             

首先根据规则(接口public与否),生成代理类的名称,$ProxyN格式,然后动态生成代理类。

所有的代码生成的工作都由 ProxyGenerator 所完成了,该类在rt.jar中,需要反编译

public static byte[] generateProxyClass(final String name,
                                           Class[] interfaces)
   {
       ProxyGenerator gen = new ProxyGenerator(name, interfaces);
    // 这里动态生成代理类的字节码,由于比较复杂就不进去看了
       final byte[] classFile = gen.generateClassFile();  

    // 如果saveGeneratedFiles的值为true,则会把所生成的代理类的字节码保存到硬盘上
       if (saveGeneratedFiles) {
           java.security.AccessController.doPrivileged(
           new java.security.PrivilegedAction<Void>() {
               public Void run() {
                   try {
                       FileOutputStream file =
                           new FileOutputStream(dotToSlash(name) + ".class");
                       file.write(classFile);
                       file.close();
                       return null;
                   } catch (IOException e) {
                       throw new InternalError(
                           "I/O exception saving generated file: " + e);
                   }
               }
           });
       }  

    // 返回代理类的字节码
       return classFile;
   }

4,代码生成过程进入结尾部分,根据结果更新缓存表,如果成功则将代理类的类对象引用更新进缓存表,否则清楚缓存表中对应关键值,最后唤醒所有可能的正在等待的线程。

finally {
        /*
         * We must clean up the "pending generation" state of the proxy
         * class cache entry somehow.  If a proxy class was successfully
         * generated, store it in the cache (with a weak reference);
         * otherwise, remove the reserved entry.  In all cases, notify
         * all waiters on reserved entries in this cache.
         */
        synchronized (cache) {
        if (proxyClass != null) {
            cache.put(key, new WeakReference(proxyClass));
        } else {
            cache.remove(key);
        }
        cache.notifyAll();
        }
    }
    return proxyClass;

InvocationHandler解析

代码参考:http://rejoy.iteye.com/blog/1627405

通过getProxyClass0方法中生成具体的class文件的过程,定义path,讲class文件写到指定的磁盘中,反编译生成的代理class文件。

发现在静态代码块中获取了的方法有:Object中的equals方法、Object中的hashCode方法、Object中toString方法 , 以及invoke的接口方法。

后语

至此,JDK是动态生成代理类,并通过调用解析器,执行接口实现的方法的原理已经一目了然。动态代理加上反射,是很多框架的基础。比如Spring的AOP机制,自定义前置后置通知等控制策略,以及mybatis中的运用反射和动态代理来实现插件技术等等。

文章参考:

http://www.ibm.com/developerworks/cn/java/j-lo-proxy1/index.html

http://blog.csdn.net/ykzhen2015/article/details/50312651

时间: 2024-08-02 06:42:46

java动态代理原理及解析的相关文章

JAVA 动态代理原理和实现

在 Java 中动态代理和代理都很常见,几乎是所有主流框架都用到过的知识.在面试中也是经常被提到的话题,于是便总结了本文. Java动态代理的基本原理为:被代理对象需要实现某个接口(这是前提),代理对象会拦截对被代理对象的方法调用,在其中可以全然抛弃被代理对象的方法实现而完成另外的功能,也可以在被代理对象方法调用的前后增加一些额外的功能. 动态代理可以为其他对象提供一个代理以控制对某个对象的访问. 代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理. JDK

Java动态代理原理

关于Java中的动态代理,我们首先需要了解的是一种常用的设计模式--代理模式,而对于代理,根据创建代理类的时间点,又可以分为静态代理和动态代理. 一.代理模式    代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务.简单的说就是

java动态代理原理解析

DamInterface; public interface DamInterface { public void sayHello(); public int sayHelloa(); } DamImpl; public class DamImpl implements DamInterface { @Override   public void sayHello() {     System.out.println("Hello");   } @Override   public 

alljoyn:基于java动态代理的RPC实现原理分析

alljoyn是由高通开源,allseen组织下,作为IOT的一个开源软件框架. 本文分析它的core部分的远程调用方法的实现过程. 以android core sdk的release版本中的simple程序为例子. (eg alljoyn-14.06.00a-android-sdk-rel\alljoyn-android\core\alljoyn-14.06.00a-rel\java\samples\simple\client) 1. 下面是一个定义为alljoyn接口,并定义了一个远程调用方

java高级----&gt;Java动态代理的原理

Java动态代理机制的出现,使得 Java 开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类.代理类会负责将所有的方法调用分派到委托对象上反射执行,在分派执行的过程中,开发人员还可以按需调整委托类对象及其功能,这是一套非常灵活有弹性的代理框架.下面我们开始动态代理的学习. 目录导航   动态代理的简要说明 简单的Java代理 Java的动态代理 Java动态代理的原理 友情链接 动态代理的简要说明 在java的动态代理机制中,有两个重要的类或接口,一个是 In

Java动态代理实现原理浅析

代码编写 接口编写 public interface TargetInterface { void targetMethod(); } 实现类编写 public class Target implements TargetInterface { @Override public void targetMethod() { System.out.println("target method invoking ..."); } } 增强类编写 public class ProxyHandl

java动态代理实现与原理详细分析

一.代理模式代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务.简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途.在后面我会 解释这种间

【转载】Java 动态代理

Java 动态代理 本文为 Android 开源项目源码解析 公共技术点中的 动态代理 部分项目地址:Jave Proxy,分析的版本:openjdk 1.6,Demo 地址:Proxy Demo分析者:Caij,校对者:Trinea,校对状态:完成 1. 相关概念 1.1 代理 在某些情况下,我们不希望或是不能直接访问对象 A,而是通过访问一个中介对象 B,由 B 去访问 A 达成目的,这种方式我们就称为代理.这里对象 A 所属类我们称为委托类,也称为被代理类,对象 B 所属类称为代理类.代理

Java动态代理 深度详解

代理模式是设计模式中非常重要的一种类型,而设计模式又是编程中非常重要的知识点,特别是在业务系统的重构中,更是有举足轻重的地位.代理模式从类型上来说,可以分为静态代理和动态代理两种类型. 今天我将用非常简单易懂的例子向大家介绍动态代理的两种类型,接着重点介绍动态代理的两种实现方式(Java 动态代理和 CGLib 动态代理),最后深入剖析这两种实现方式的异同,最后说说动态代理在我们周边框架中的应用. 在开始之前,我们先假设这样一个场景:有一个蛋糕店,它们都是使用蛋糕机来做蛋糕的,而且不同种类的蛋糕