`
frank-liu
  • 浏览: 1684198 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

java Proxy类的讨论

 
阅读更多

前言

    我们常用的一些proxy的手法或者设计模式都是本质上通过一个中间的代理类来引用实际的功能实现类。这个代理类提供和实际功能类同样的功能接口,所以从使用者的角度来说看起来是一样的,这样也就看不出区别来。和proxy模式有相似思想的设计模式包括有decorator, adaptor等模式。代理类很多时候并不仅仅只是一个简单的功能转发,有的时候它也可以做一些适当的功能增强。当然,这里很多时候实现的代理是基于一个已知类的声明和功能,然后静态的代码实现相应的规约。所以一个代理类只能代理一个具体的功能类。如果有变化的话,我们需要再静态的去实现适配的类。在这个时候,我们可能会考虑是否有一种可以动态生成的代理类呢?如果我们可以实现动态的生成代理类的话,需要一些什么条件呢?这里我们会讨论proxy模式以及java中Proxy类使用的思路。

Proxy模式思想介绍

    我们一般讨论到Proxy相关的模式时,从字面上就很容易理解,既然是一种代理的模式,肯定是我们本来希望一些使用的功能由某个类来实现,但是由于某些原因我们将一些具体的实现的功能交给另外一个类来做。这个使用那些其他类提供功能的类就称其为代理。在使用的时候,我们用这个代理类或者原来的类是看起来没什么区别的。通常对应的一种关系如下:

    这里的关系也比较好理解,我们既然从用户使用的角度来说,Proxy类和具体实现类看起来没什么区别。那必然从用户的角度他们是可以互换的。所以他们必然需要实现同一个接口才可能。而这种模式典型的示例代码如下:

Contract interface:

public interface Contract {
    public void doSomething();
}

 Impl:

public class Impl implements Contract {
    public void doSomething() {
        System.out.println("Do something in Implementation");
    }
}

Proxy:

public class Proxy implements Contract {
    private Contract contract;

    public Proxy(Contract contract) {
        this.contract = ocntract;
    }

    public void doSomething() {
        // Can do something overhead
        contract.doSomething();
        // Do something afterward...
    }
}

    从使用者的角度来说,如果我们引用Proxy类,它本身将一些功能实现转发给了具体的Impl类,同时它也可以针对实现做一些增强和调整。如果现在我们来看Proxy模式的作用,我们可以发现,对于一个需要被代理的类来说,它的某些需要被代理的功能最好能够提取到某个接口中来以方便建立代理。对于代理的具体实现,我们可以添加自己的可定制部分。另外,这边还有一个问题就是,比如前面的类里有多个方法需要被代理,具体的代理类也需要写很多个对应的代理方法。这些写法不难,只是感觉很重复和琐碎。

和其他模式及应用的关系

    透过前面写的代码和类图关系,我们会发现Proxy模式和一些常用的设计模式有很多相似的地方。一些比较典型的有Decorator, Adaptor。另外,我们从代码里可以看到,在一些代理方法执行的地方,Proxy类可以做一些其他特性的定制,既可以在方法执行前也可以在方法执行后。这些东西和java EE里的AOP概念很接近。

    我们先来看看Decorator模式的类关系描述:

    这里,每个Decorator都有一个指向同样接口的引用。它本身只是做一些代理。继承Decorator的类会针对具体特性做一些增强。这个Decorator的类本身就相当于是一个Proxy。关于Decorator pattern的详细描述可以参考我的这篇文章。从这些关系我们可以看到,Decorator pattern增加的继承可以使得代理增加的特性更加灵活和丰富。

    至于Adaptor pattern的描述,其本身更加简单:

    我们这里的Adapter就是一个Proxy,他本身要引用另外一个Adaptee的特性,只是要使得他本身符合接口Target的规约而已。

    现在,到这一步的时候,我们发现其实原来很多的应用方式都用到了Proxy模式的思想,只是平时不太留意到而已。我们手工编码实现的Proxy需要有明确的接口规约,这样才能模拟出这样的结果来。在一些情况下,如果我们需要动态的生成一些Proxy类的话,有没有什么好的办法呢?因为对于一些接口来说,每次我们通过手工的去实现他们其实从写代码的角度来说挺没劲的,来来去去就是那么个套路。既然这些事情是如此无趣,能不能让程序给我们生成呢?

Proxy类介绍

    在Java里有专门用于动态代理类生成的方法,就是java.lang.reflect.Proxy。它采用反射的机制来调用原来的被封装方法。我们以前面的示例为基础来看看用Proxy类封装代理之后的常用做法。

    前面的示例里我们首先定义了接口Contract和具体的实现Impl类。这里就不重复。为了能够代理这个类的功能,我们首先定义一个类DynamicProxyHandler:

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

public class DynamicProxyHandler implements InvocationHandler {
    private Object proxied;

    public DynamicProxyHandler(Object proxied) {
        this.proxied = proxied;
    }

    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable {
        System.out.println("Proxy: " + proxied);
        System.out.println("Method name: " + method.getName());
        System.out.println("Args: " + args);

        return method.invoke(proxied, args);
    }
}

    这个ProxyHandler类实现接口InvocationHandler。我们在实现的invoke方法里添加了一些自己定义的信息。然后通过method.invoke方法来调用目标对象的方法,并返回调用的结果。我们可以说这里是我们定义自己定制部分特性的切入点,在代理类被调用的时候,触发的就是这个方法。同时被代理的对象将作为构造函数的参数传递进来。

    我们再来看是怎么使用这个DynamicProxyHandler将目标对象包装起来:

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

public class ProxyDemo {
    public static void main(String[] args) {
        // Original way without proxy
        System.out.println("Run without proxy");
        Contract contract = new Impl();
        contract.doSomething();

        // With proxy
        System.out.println("Run with proxy");
        InvocationHandler handler = new DynamicProxyHandler(contract);
        Contract cont = (Contract)Proxy.newProxyInstance(
            contract.getClass().getClassLoader(), 
            new Class[] { Contract.class }, handler);
        cont.doSomething();
    }
}

    这部分代理使用Proxy部分比较有意思的是我们通过Proxy.newProxyInstance可以返回一个被封装的Contract对象。我们需要将Contract的具体对象的,以及它的Class对象和我们定义的handler传入。在调用的时候我们会发现如下的结果:

Do something in implementation!
Proxy: Impl@6e1b0caf
Method name: doSomething
Args: null
Do something in implementation!

    我们如果查阅Proxy.newProxyInstance的官方文档,会发现它的方法原型 如下:

newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

    这里的classLoader是定义要代理的那个类的classLoader,一般我们通过该类的Class.getClassLoader()来获得。 而这个interfaces则表示我们要代理的类实现的接口,比如这里我们要代理的类Impl实现了接口Contract,如果它实现了多个而且我们需要都代理的话,则需要放在这里。最后的这个InvocationHandler则是方法被代理执行的对象。一般通过实现InvocationHandler来实现。

    现在我们回过头来看看这种Proxy的实现,这里定义的Proxy对象并没有去直接声明实现某个代理类的接口。另外,虽然我们这里传入了需要被代理的对象,如果将传入对象封装的过程封装成方法的话,我们可以传入任何实现该指定接口的对象,而不只是固定的某一个对象。这样我们就有了另外一个好处,那就是只要将实现该接口的对象传进来,我们就可以代理,不需要再手工的编码创建类。我们在开发的时候会简单一些。

和AOP的比较

    如果很多对AOP比较了解的都知道,AOP的实现可以分为静态切入和动态织入两种方式。典型静态切入的库有aspectJ,而动态的主要是CGLib。这几种织入的方式和Proxy比起来更加强大的地方在于他们可以在方法执行的不同阶段切入时并不要求这个类方法是实现某个接口的。而这里Proxy还是受限于接口的定义限制。所以说,可以将Proxy的功能增强当作一个弱化版的AOP。

Proxy的实现

    既然前面我们使用了Proxy的newProxyInstance方法来封装被代理对象,那么在Java里面,它是怎么实现的呢?我们可以找到这个方法的代码:

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        if (h == null) {
            throw new NullPointerException();
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass(loader, interfaces);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            Constructor cons = cl.getConstructor(constructorParams);
            return cons.newInstance(new Object[] { h });
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString());
        } catch (IllegalAccessException e) {
            throw new InternalError(e.toString());
        } catch (InstantiationException e) {
            throw new InternalError(e.toString());
        } catch (InvocationTargetException e) {
            throw new InternalError(e.toString());
        }
    }

    这个方法其实比较简单,就是通过调用类本身的getProxyClass方法来获取到一个我们代理后生成的Class对象,然后再通过反射调用该对象的构造函数来生成目标对象。那么这个getProxyClass是怎么来生成目标Class对象呢?我们再深入的看看。

    getProxyClass方法本身就需要一个能够实现指定一系列接口的类,而且需要这个类是动态生成的。当然,这个类既然是动态生成的,我们就需要针对接口里所有的方法进行审核。如果我们对jvm再深入理解一点的话,会发现一般java里生成的Class文件有一个固定的格式可以遵循的。比如class文件里指明所使用的java的版本,这个类是接口还是类,里面的成员和方法等信息。既然我们需要一个这样的类,我们完全可以通过一个模板的类来生成这样的类文件。以后我们再通过类加载器将其加载进来就可以了。

    getProxyClass方法通过委托ProxyGenerator来生成这个文件,然后再加载进来。而getProxyClass方法主要是检查这些接口的列表是否重复了,这些接口是否都声明在同一个package里面。然后将生成后的Class对象放到一个weak reference的缓存里。这个几百行的方法里其实主要就是做一些检查工作,没什么特殊的地方。

 

总结

    Proxy模式以及它的一些应用其实平时使用的并不多。更多的时候是在一些J2EE的应用里,比如生成RMI的stub,以前要用工具类rmic来做,现在基本上就是依赖的Proxy。另外,Proxy的思想里也可以用到AOP的应用上。虽然这部分比较少见,但是它和jvm的很多地方关系比较密切,比如class文件的动态生成。以前我们要生成这些需要自己写出来然后用javac编译器来生成,这里自动按照指定的规约来生成,确实比较少见。

 

参考材料

core java volumn I

head first design patterns

http://www.ibm.com/developerworks/java/library/j-jtp08305/index.html

http://www.infoq.com/cn/articles/cf-java-reflection-dynamic-proxy

http://openjdk.java.net/

  • 大小: 8.7 KB
  • 大小: 4.7 KB
分享到:
评论

相关推荐

    java proxy demo 代理类的运用demo

    我们这里主要讨论的是动态代理,它基于Java的`java.lang.reflect.Proxy`类和`java.lang.reflect.InvocationHandler`接口。 在Java动态代理机制中,`Proxy`类是生成代理对象的工厂,而`InvocationHandler`接口定义了...

    代理模式java代码 Proxy(5) 2个代理类

    在这个主题“代理模式java代码 Proxy(5) 2个代理类”中,我们可以理解为将介绍两种不同的代理类实现。 1. 静态代理 在静态代理中,我们需要创建一个接口,然后让目标对象和代理对象都实现这个接口。代理类会持有...

    DotNet环境和Java环境的proxy配置文件

    在本案例中,我们将讨论如何配置`DotNet`和`Java`环境的`proxy`设置,特别是在遇到`arcgis server`网站证书不安全时,如何通过代理来绕过SSL(Secure Socket Layer)检测,以解决访问出错的问题。 首先,让我们理解...

    代理模式java代码 Proxy(1)

    Java提供了一种动态创建代理对象的方式,即`java.lang.reflect.Proxy`类和`java.lang.reflect.InvocationHandler`接口。动态代理可以在运行时创建代理对象,而无需预先编写代理类。`ProxyDisplay.java`可能就利用了...

    A Java proxy for MS SQL Server Reporting Services

    这篇文章主要讨论了Java Web应用程序如何与Microsoft SQL Server Reporting Services (RS)集成,以及RS作为一种企业级报告工具的潜力。首先,文章提到了在Web应用程序中生成高质量打印输出的挑战,指出浏览器在文档...

    Java 反射-动态代理

    接下来,我们讨论Java动态代理(Dynamic Proxy)。动态代理是在运行时创建代理类和代理对象,这些代理对象可以代替目标对象执行某些操作,同时提供额外的功能,如日志记录、事务管理等。Java提供两种方式实现动态...

    JAVA类的反射机制

    1. **动态代理**:Java的`Proxy`类利用反射机制创建动态代理对象,实现接口的方法调用可以被拦截并自定义处理。 2. **序列化和反序列化**:在序列化过程中,需要访问类的私有字段,反射提供了可能。 3. **框架开发**...

    java动态代理(2)

    接下来,我们讨论`Proxy`类。`Proxy`类是动态代理的工厂,它提供了创建动态代理对象的方法。`Proxy.newProxyInstance()`是关键方法,它需要三个参数:代理接口的类加载器、代理接口的Class数组以及一个实现了`...

    Spring_0300_JDKProxy

    标题"Spring_0300_JDKProxy"暗示我们将讨论Spring如何使用JDK的Proxy类来实现动态代理。在Spring中,`org.springframework.aop.framework.ProxyFactoryBean`或`org.springframework.aop.framework.ProxyFactory`可以...

    java-用Java动态代理实现AOP.pdf

    Java动态代理机制要求被代理的类必须实现至少一个接口,因为Java动态代理是通过JDK的java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来实现的。Proxy类用于生成代理对象,而InvocationHandler...

    java 使用委托控制权限

    接下来,我们讨论Java中的代理(Proxy)。Java代理机制允许我们在运行时动态创建一个实现了一组给定接口的新类,这个新类可以作为原类的代理,提供额外的功能,如日志、事务管理或者,如题目中所述,权限控制。JDK...

    Java动态代理示例代码.rar

    在这里,我们将主要讨论`Proxy`类的使用。 1. **Proxy类**:这是Java提供的标准动态代理实现。首先,你需要定义一个接口,这个接口将被代理类实现。例如,我们有一个名为`MyService`的接口: ```java public ...

    JDBC,MySQL和JDBCProxy联合实现Java数据库.pdf

    最后,我们将讨论如何使用 JDBC 和 JDBCProxy 来实现对 MySQL 数据库的访问和操作,并提供统一的 Java-MySQL 开发和测试方案。 JDBC 和 JDBCProxy 是实现对关系型数据库的访问和操作的重要工具。通过使用 JDBC,...

    Java动态代理机制详解[整理].pdf

    这里我们主要讨论`Proxy`类。 首先,让我们理解一下Java的类加载机制。Java虚拟机(JVM)负责加载类的字节码文件(.class文件)到内存中,并解析这些字节码,生成对应的Class对象。类加载器(ClassLoader)是这个...

    java动态代理 经典文章(word 2007格式的)

    `Proxy`类是Java动态代理的工厂,通过`newProxyInstance()`方法创建代理对象。该方法需要传入三个参数:类加载器、目标对象实现的接口列表和`InvocationHandler`实例。 4. **面向切面编程(AOP)**: AOP是一种...

    java基础,多线程,反射

    Java标准库中的`java.lang.reflect.Proxy`类和`InvocationHandler`接口可以帮助我们实现这一目标。 最后,交通灯管理系统和银行业务调度系统的面试题展示了实际项目中如何应用多线程和并发控制技术,以解决复杂的...

    java_jdk_api

    `java.lang.reflect.Proxy`则可以创建动态代理类,用于在运行时创建符合特定接口的新类型。 8. **模块系统**:Java 9引入了模块系统(Project Jigsaw),它通过明确地声明依赖关系和封装,提高了系统的可维护性和...

    代码性能Java比较

    在Java中,静态代理和动态代理(JDK Proxy或CGLIB)都有广泛应用。 5. **观察者模式**:定义对象间的一种一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。Java中的...

    一个用java编辑的设计模式演示程序,比较简单。

    在Java中,静态代理和动态代理(Java Proxy Class)都是实现方式。 “策略模式”(Strategy Pattern)定义了一系列算法,并将每个算法封装起来,使它们可以相互替换。这样可以让算法的变化独立于使用它的客户。在...

    利用Java的反射与代理实现IOC模式

    `java.lang.reflect.Proxy`类的`newProxyInstance()`方法用于创建代理对象。 2. CGLIB代理:如果目标类没有实现任何接口,我们可以使用CGLIB库(Code Generation Library)生成一个子类来创建代理。CGLIB是一个强大...

Global site tag (gtag.js) - Google Analytics