动态代理(Dynamicproxies)是Java1.3引入的特性,广泛应用于J2EE远程调用。给定一个抽象接口和该接口的具体实现,可以通过创建两个附加类来实现该接口的远程调用(例如,跨JVM)。首先在源头JVM上实现相应的接口,将调用细节序列化并通过网络传输。然后,在目标JVM上,获取序列化调用的详细信息,并将其分配给具体的类进行调用。如果没有动态代理和反射,开发人员将不得不为每个远程接口提供两个类。动态代理是在运行时生成的实现一个或多个接口的类。接口中每个方法的调用都会自动转换为java.runtime.InvocationHandler提供的方法调用:在运行时使用方法的有效信息,包括注解、参数类型和方法返回类型。这允许实现通用逻辑来定义方法调用的分派。一旦你写了一个InvocationHandler,你就可以调用代理类的handler来完成所有接口中的方法,而不用为每个接口单独写一个实现。Remoting近年来已经失宠,因为开发人员需要了解方法调用分派和网络请求分派之间在语义和故障模式上的根本区别,但动态代理仍然保留在语言中。在本文中,我将讨论动态代理的其他方面的作用。在下一篇文章中,我们将讨论由于在Java8中引入lambda表达式和默认方法而出现的动态代理的新实现技术。MagicMatchers我多年来一直使用“Magic”对象来编写整洁流畅的测试。我定义了一个“神奇”的接口,然后通过动态代理实现目标行为。更具体地说,在测试时使用“magicbuilders”来生成测试值,然后使用“magicmatchers”来表达断言属性测试的结果。我们这里只关注匹配器。我们有一个支持的Person类,这是一个典型的bean-成员变量是私有的,并通过getter和setter方法公开。publicclassPerson{privateStringname;privateintage;//insertgettersandsettershere}使用了一个简单的Hamcrest类,我们有两种方法来断言这个类的实例。一种方法是单独提取每个值,分别断言。assertThat(person.getName(),containsString("Smith"));assertThat(person.getAge(),greaterThan(30));另一种方法是使用allOf和hasProperty方法通过一组期望来匹配对象作为一个整体。assertThat(person,allOf(hasProperty("name",containsString("Smith")),hasProperty("age",greaterThan(30)));这工作正常,但这样Hamcrest描述整体匹配和错误匹配不没什么帮助。预期:(hasProperty("name",astringcontaining"Putey")andhasProperty("age",avaluegreaterthan<43>))but:hasProperty("age",avaluegreaterthan<43>)property'age'<42>waslessthan<43>hasProperty的匹配在类型一致性的检测上也很弱:我们可以这样写hasProperty("age",containsString("Smith")),这样类型检测就不会拒绝了。我们真正想要的是一个流畅的API,可以像这样使用:assertThat(person,aPerson().withName("ArthurPutey").withAge(greaterThan(43)));并很好地报告错误匹配:预期:名称:字符串包含“Putey”年龄:avaluegreaterthan<43>但是:年龄:<42>waslessthan<43>为上述功能编写自定义匹配器很容易,但必须繁琐的写了很多遍。好在动态代理可以帮我们解决。首先,我们定义一个流畅的接口,它包含以下方法:然后,我们在一个名为MagicMatcher的类上使用一个静态方法来获取实现此接口的动态代理,然后通过方法调用获取协调表达式:staticPersonMatcherPerson(){returnMagicMatcher.proxying(PersonMatcher.class);}每个方法调用都是通过代理类的“解释”方法实现的,该方法从方法(“withAge”)中获取属性(“age”)并指定调用匹配对象上的方法(“getAge”)获取属性值。在调用代理类的match或describeMismatch方法之前,会一直保存属性名和match中对应的值(这也是接口需要继承Matcher的原因)。需要在调用时提取和测试对象的属性,必要时创建不匹配报告。这种方法是轻量级的,我们可以引入任何新的自定义接口并在测试中重用它,因此编写自定义Hamcrest匹配器非常有益,因为无需编写接口的实现。接口中定义的所有需要??生成的匹配器行为只需要实现一次,我们使用合适的InvocationHandler来完成逻辑功能的实现。在下一篇文章中,我将创建一个小而有用的库,我们使用Java8的动态代理来完成各种功能,并演示一些实现各种代理行为的方法,包括接口和“魔法”对象生成。这个库的源代码,包括这篇文章中讨论的MagicMatcher类的实现,可以在github上找到。
