- Creating Proxies
- InvocationHandler’s
- Known Use Cases
- Database Connection and Transaction Management
- Dynamic Mock Objects for Unit Testing
- Adaptation of DI Container to Custom Factory Interfaces
- AOP-like Method Interception
Java反射使得你可以在程序运行时能动态实现接口。你可以通过java.lang.reflect.Proxy
来实现。这个类的名称是为什么我将这些动态接口实现称为动态代理。动态代理在许多不同情形下回使用。比如数据库的链接,事务管理,单元测试的动态摸你对象,还有像AOP比如方法拦截。
创建代理
你可以使用Proxy.newProxyInstance()
方法创建代理。方法需要3个参数:
- 类加载器(ClssLoader)用于动态的加载类。
- 一组接口的数组将要被实现的。
- 一个用于所有方法去调用的代理的
InvocationHandler
。
例子如下:
1 | InvocationHandler handler = new MyInvocationHandler(); |
在运行了这段代码过后,proxy对象就动态的实现了MyInterface
接口。所有调用proxy的方法都传递到实现了InvoicationHanler
接口的handler
对象。接下来就讲解InvoicationHandler
。
InvoicationHanlder‘s
正如前文Proxy.newInstance()
方法必须提供一个InvoicationHanler
。对动态代理的所有方法调用都将转发到此InvocationHandler实现。InvoicationHanler
是这样定义的:
1 | public interface InvocationHandler{ |
一个实现的接口的列子:
1 | public class MyInvocationHandler implements InvocationHandler{ |
传递给invoke()
方法的参数proxy是实现了接口的动态代理对象。通常你不需要这个对象。
传递给invoke()
方法的参数Method对象表示的是调用代理实现方法的方法。对于这个Method
对象你可以获取到方法的名称,参数类型,返回类型等。参见前面的‘Method’文章。
这个Object[]
数组包含了方法调用的时候传递给proxy对象对应的参数。
注意:在实现的接口里面,基本的数据类型(int,long)被包裹成对应的对象(Intger,Long等)。
已知用例
动态代理被用于至少在以下的情形:
- 数据库连接和事务管理
- 单元测试模拟对象
- Adaptation of DI Container to Custom Factory Interfaces
- AOP方法拦截
数据库连接和事务管理
Spring框架为你提供了一个可以提交,回滚一个事物的事物代理。至于具体是怎样工作的在文章 Advanced Connection and Transaction Demarcation and Propagation有具体的描述,所以我只是简单的阐述一下。调用顺序如下:
1 | web controller --> proxy.execute(...); |
单元测试中动态模拟对象
Butterfly Testing Tools使用动态代理实现动态存根,模拟和代理单元测试。当测试类A用到了另外的类B(接口),你可以传递一个B的模拟实现到A而不是传递一个真的B。所有调用B的方法都被记录了,你可以设置返回的B的值。
Adaptation of DI Container to Custom Factory Interfaces
依赖注入容器Butterfly Container具有一个强大的功能,允许您将整个容器注入由其生成的bean中。但是如果您不希望依赖容器接口,容器能够适应您的设计的自定义工厂接口。你仅仅需要的是这些接口。没有被实现的。因此工厂的接口和你的类大概是这个样子:
1 | public interface IMyFactory { |
1 | public class MyAction{ |
当MyAction类调用通过MyAction类构造方法传递进来的IMyFactory对象的方法时,方法调用转换为对IContainer.instance()方法的调用,IContainer.instance()方法是用于从容器获取实例的方法。这就是为什么一个对象能够在运行时把Butterfly Container当做一个工厂,而不仅仅是在创建时将依赖注入自身。并且这在Butterfly Container没有任何的的依赖。
AOP-like Method Interception
在Spring框架中是有可能在指定的Bean调用方法之前拦截该方法的,理由是bean实现了某些接口方法。Spring框架将bean包装在动态代理中。所有对bean的bean调用都被拦截了。动态代理也能在对其他对象调用的时候进行检查,而不是在将方法调用委托给bean包装之后。