Java那些事:动态代理

       动态代理是对代理模式的一种实现,JDK支持动态代理。静态代理是由编写人员自己编写编译,动态代理的代理类则是由代码帮助生成。本文将探索如何使用动态代理以及其原理,做到知其然知其所以然。

      

         Java动态代理相关的类和接口:

         java.lang.reflect中的InterfaceInvocationHandler,

         其中有且仅有有 Objectinvoke(Object proxy,Method method,Object[] args) throws Throwable这一个方法

 

         Proxy类

         其中提供了四个静态方法

                   getInvocationHandler(Objectproxy) 获取代理对象的handler

                   getProxyClass(ClassLoaderloader,Class<?>… interfaces)  获取代理类,需要传入加载代理类的类加载器和需要代理类实现的一组接口

                   isProxyClass(Class<?>cl)  检查某个类是否是代理类

                   newProxyInstance(ClassLoaderloader,Class<?>[] interfaces,InvocationHandler h) 被委托类(真实类)的类加载器,需要代理类实现的一组接口,关联的handler,这个方法是对其他方法的封装,返回一个代理类的实例。

         每个代理类实例都有一个实现了invocationhandler的对象与之关联,代理实例会调用关联的Handler的invoke方法,最大问题出现---代理类在哪里?

在代码中,使用JDK提供的动态代理还是相当的简便,代码如下:

        

         公共接口:

public interface ShouldDoSomeThing {
	/**
	 * 实现此接口的类,需要实现这个方法
	 */
	public void doSomeThing();
}

      Handler类:

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

public class HandlerClass implements InvocationHandler{

	private Object o;
	public HandlerClass(Object o){
		this.o = o;
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		System.out.println("do before!");
		method.invoke(o, args);
		System.out.println("do after!");
		return null;
	}
}

需要特别提醒:这个handler类在上面已经有叙述,其并不是代理类。

 

     然后是客户端类:

public class Main {
	public static void main(String[] args) {
		RealClass rc = new RealClass();
		HandlerClass pc = new HandlerClass(rc);
		//这里必须转型到某个接口
		ShouldDoSomeThing ProxyTest = (ShouldDoSomeThing) Proxy.newProxyInstance(rc.getClass().getClassLoader(), rc.getClass().getInterfaces(), pc);
		ProxyTest.doSomeThing();
	}
}

输出:
do before!
do some thing!
do after!

     从这里可以看出,代理类实例可以安全的转型到被委托类实现了接口的类型。那代理类到底在哪里?输出为什么会是这样?

首先根据客户端的使用流程说下整个过程:

         被委托类对象被创建,这个类是实际操作的执行者。然后Handler被创建,并且传入了被委托类对象,接下来,proxy对象被创建。

         调用doSomeThing方法,此时,代理类对象所实现的相应的接口方法被调用,在代理类方法的内部,调用了与之相关联的handler的invoke方法,而invoke方法中调用了被委托类的相应接口的方法,所以实际的操作被执行,相应在invoke方法中附加的相关操作也被执行。

上面的描述,可以使用一个UML图来表示:





        图中展示了整个动态代理的过程,可以看出,代理类是由Proxy来创建,而且客户端是和代理类交流,可见,JDK的动态代理是严格遵守代理模式。其中的$ProxyN虽然是个类,但是其在外存上并不会存在.class文件,其在运行过程中动态生成,存在于内存中的某个角落。但是要理解RealClass是如何和Handler关联,以及代理类是如何调用invoke方法,还必须深入到源代码。


首先看看创建Proxy对象的源代码

 /**
     * 
     * @param loader 类加载器
     * @param interfaces 需要代理类所实现的一组接口
     * @param h 与代理类关联的handler(需要执行invoke方法的handler对象)
     * @return 代理类对象
     * @throws IllegalArgumentException
     */
    
    
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        
        // 安全检查
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, interfaces);
        }

        
        /*
         * 查看是否已经存在代理类,如果存在,就返回它,如果不存在,就创建
         */
        
        Class<?> cl = getProxyClass0(loader, interfaces);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
        	
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            
            //这是关键代码,首先获得了其构造器,带有h参数的那个构造器
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            //设置权限
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            
            //返回这个代理对象
            return cons.newInstance(new Object[]{h});
            
            
            
            
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

    接下来去看看如何创建的代理类$ProxyN:

首先是getProxyClass0


/*
     * 
     * @param loader 类加载器
     * @param interfaces 需要实现的一组接口
     * @return 代理类的Class对象
     */
    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        
        //如果在map中存在,就返回,不存在创建
        return proxyClassCache.get(loader, interfaces);
    }

      忽略相关细节,直接来看如何创建代理类的:


public static Class<?> getProxyClass(ClassLoader loader,   
                                         Class<?>... interfaces)  
    throws IllegalArgumentException  
    {  
    // 如果目标类实现的接口数大于65535个则抛出异常,真的会存在这么多的接口吗?
    if (interfaces.length > 65535) {  
        throw new IllegalArgumentException("interface limit exceeded");  
    }  
  
    // 声明代表proxy类的class对象  
    Class proxyClass = null;  
  
    String[] interfaceNames = new String[interfaces.length];  
  
    Set interfaceSet = new HashSet();   // for detecting duplicates  
  
    // 遍历目标类所实现的接口,并将其写入到内存中
    for (int i = 0; i < interfaces.length; i++) {  
          
        // 获得需要代理类所实现的接口的名称  
        String interfaceName = interfaces[i].getName();  
        Class interfaceClass = null;  
        try {  
        // 加载需要代理类实现的接口到内存中  
        interfaceClass = Class.forName(interfaceName, false, loader);  
        } catch (ClassNotFoundException e) {  
        }  
        if (interfaceClass != interfaces[i]) {  
        throw new IllegalArgumentException(  
            interfaces[i] + " is not visible from class loader");  
        }  
  
        //省略一些无关紧要的代码 .......  
          
        // 把目标类实现的接口代表的Class对象放到Set中  
        interfaceSet.add(interfaceClass);  
  
        interfaceNames[i] = interfaceName;  
    }  
  
    // 把目标类实现的接口名称作为缓存(Map)中的key  
    Object key = Arrays.asList(interfaceNames);  
  
    Map cache;  
      
    synchronized (loaderToCache) {  
        // 从缓存中获取cache  
        cache = (Map) loaderToCache.get(loader);  
        if (cache == null) {  
        // 如果获取不到,则new个HashMap实例  
        cache = new HashMap();  
        // 把HashMap实例和当前加载器放到缓存中  
        loaderToCache.put(loader, cache);  
        }  
  
    }  
  
    synchronized (cache) {  
  
        do {  
        // 根据接口的名称从缓存中获取对象  
        Object value = cache.get(key);  
        if (value instanceof Reference) {  
            proxyClass = (Class) ((Reference) value).get();  
        }  
        if (proxyClass != null) {  
            // 如果代理对象的Class实例已经存在,则直接返回  
            return proxyClass;  
        } else if (value == pendingGenerationMarker) {  
            try {  
            cache.wait();  
            } catch (InterruptedException e) {  
            }  
            continue;  
        } else {  
            cache.put(key, pendingGenerationMarker);  
            break;  
        }  
        } while (true);  
    }  
  
    try {  
        // 中间省略部分代码 .......  
          
        // 这里就是动态生成代理对象的最关键的地方  
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(  
            proxyName, interfaces);  
        try {  
            // 根据代理类的字节码生成代理类的实例  
            proxyClass = defineClass0(loader, proxyName,  
            proxyClassFile, 0, proxyClassFile.length);  
        } catch (ClassFormatError e) {  
            throw new IllegalArgumentException(e.toString());  
        }  
        }  
        // add to set of all generated proxy classes, for isProxyClass  
        proxyClasses.put(proxyClass, null);  
  
    }   
    // 中间省略了一些代码 .......  
      
    return proxyClass;  
    }  

       从中可以看出,它遍历了传入的所有接口并获取了它们的名称,接下来就是利用字节码操作生成相关的class字节码,然后看看获取字节码的generateProxyClass类:


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;  
   }  

     上面的两个类可以说明JDK是如何生成的代理类的字节码,现在需要明白的就是,它们之间的关系是如何建立的。这个就需要查看代理类的源代码,直接利用创建字节码类来生成其.class文件,然后反编译得到源代码。

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", true)

源代码如下:


public final class $Proxy11 extends Proxy implements ShouldDoSomeThing {

	// 构造方法,参数就是刚才传过来的MyInvocationHandler类的实例
	public $Proxy11(InvocationHandler invocationhandler) {
		super(invocationhandler);
	}

	public final boolean equals(Object obj) {
		try {
			return ((Boolean) super.h.invoke(this, m1, new Object[] { obj }))
					.booleanValue();
		} catch (Error _ex) {
		} catch (Throwable throwable) {
			throw new UndeclaredThrowableException(throwable);
		}
	}

	/*
	 * 实现了相关的接口
	 */

	@Override
	public void doIt(String str) {
		try {
			// 从这里可以看出,在代理类内部所实现的需要实现的接口是调用了
			// 与之相关联的handler对象的invoke方法,那handler对象
			// 哪里来的?还记得在newInstance的时候传入的pc吗?它就来
			// 自那里!
			super.h.invoke(this, m3, null);
			return;
		} catch (Error _ex) {
		} catch (Throwable throwable) {
			throw new UndeclaredThrowableException(throwable);
		}
	}

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

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

	private static Method m1;
	private static Method m3;
	private static Method m0;
	private static Method m2;

	// 这里利用反射,获得了代理类其中关键的是m3这个方法,而且m3这个方法还是委	// 托类的,同时这个m3作为参数,传递给了handler的invoke方法
	static {
		try {
			m1 = Class.forName("java.lang.Object").getMethod("equals",
					new Class[] { Class.forName("java.lang.Object") });
			m3 = Class.forName("dp.ShouldDoSomeThing").getMethod(
					"ShouldDoSomeThing", new Class[0]);
			m0 = Class.forName("java.lang.Object").getMethod("hashCode",
					new Class[0]);
			m2 = Class.forName("java.lang.Object").getMethod("toString",
					new Class[0]);
		} catch (NoSuchMethodException nosuchmethodexception) {
			throw new NoSuchMethodError(nosuchmethodexception.getMessage());
		} catch (ClassNotFoundException classnotfoundexception) {
			throw new NoClassDefFoundError(classnotfoundexception.getMessage());
		}
	}
}

        到目前为止,整个原理都已经解释,其中一些和思想无关的代码并没有描述。JDK的动态代理使用了字节码操作生成代理类的字节码,使用反射来执行代理,现在再看上面的图如果能够感觉到思路清晰,那么动态代理的思想也就基本OK了,但是JDK动态代理只能是代理接口,并不能代理类,CGLIB 是弥补了这一缺点。

         由于最近需要搭建一个基础的Web框架,所以就看了下动态代理在AOP方面的运用,当然,代理不仅仅是AOP方面才有运用,代理是对委托对象访问的一种控制,在许多方面都有用,“设计”这种东西有时候真心很费脑细胞,实现反而更加简单。

         JDK1.8对Proxy的改进相当大,由于我windows的JDK是1.8,那个真心复杂了很多!不利于对动态代理整体的掌握,所以本文参考了http://rejoy.iteye.com/blog/1627405#comments的部分代码并有部分改变。

         再说下那个代理类名字的问题:$Proxy是所有代理类的名字前缀,其后N代表一个数字,每次在map中新增一个class对象的时候都会使数字自增1,,所以代理类的名字可能是这样的----$Proxy20

         薄言有误,还请指出一起学习改进。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值