Java动态代理原理分析

405 阅读4分钟

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

为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。Java 动态代理机制以巧妙的方式近乎完美地实践了代理模式的设计理念。

  Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:

 (1)Interface InvocationHandler:

该接口中仅定义了一个方法 

 public object invoke(Object obj,Method method, Object[] args) 

在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的request(),args为该方法的参数数组。这个抽象方法在代理类中动态实现。

 (2)Proxy:该类即为动态代理类,其中主要包含以下内容: 

 protected Proxy(InvocationHandler h):构造函数,用于给内部的h赋值。 

 static Class getProxyClass (ClassLoaderloader, Class[] interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。

 static Object newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)

动态代理步骤: 

1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法 

2.创建被代理的类以及接口 

3.通过Proxy的静态方法 newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)创建一个代理 

4.通过代理调用方法

    public static void main(String[] args)
    {
        //代理的真实对象
        Subject realSubject = new RealSubject();
        
        /**
         * InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
         * 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用.
         * 即:要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法
         */
        InvocationHandler handler = new InvocationHandlerImpl(realSubject);
 
        ClassLoader loader = realSubject.getClass().getClassLoader();
        Class[] interfaces = realSubject.getClass().getInterfaces();
        /**
         * 该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
         */
        Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);
 
        System.out.println("动态代理对象的类型:"+subject.getClass().getName());
 
        String hello = subject.SayHello("hello");
        System.out.println(hello);
    }




@CallerSensitive 
public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
		//检查h 不为空,否则抛异常
        Objects.requireNonNull(h);
 
        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }
 
        /*
         * 获得与指定类装载器和一组接口相关的代理类类型对象
         */
        Class<?> cl = getProxyClass0(loader, intfs);
 
        /*
         * 通过反射获取构造函数对象并生成代理类实例
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
			//获取代理对象的构造方法(也就是$Proxy0(InvocationHandler 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;
                    }
                });
            }
			//生成代理类的实例并把InvocationHandlerImpl的实例传给它的构造方法
            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);
        }
    }

关键方法为getProxyClass0,该方法先从缓存中查找是否已经生成代理类,如果没有则生成代理类字节码,然后加入到缓存中,方便下次查找到,从而提升程序的运行效率。

//生成字节码

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);

//加载字节码

try {
                return 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());
            }
  • 总结:

  • 动态代理类:在程序运行时,通过反射机制动态生成。

  • 动态代理类通常代理接口下的所有类。

  • 动态代理事先不知道要代理的是什么,只有在运行的时候才能确定。

  • 动态代理的调用处理程序必须事先InvocationHandler接口,及使用Proxy类中的newProxyInstance方法动态的创建代理类。

  • Java动态代理只能代理接口,要代理类需要使用第三方的CLIGB等类库。

Java动态代理的优势是实现无侵入式的代码扩展,也就是方法的增强; 让你可以在不用修改源码的情况下,增强一些方法; 在方法的前后你可以做你任何想做的事情(甚至不去执行这个方法就可以)。 此外,也可以减少代码量,如果采用静态代理,类的方法比较多的时候,得手写大量代码。