博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java反射-动态代理
阅读量:6037 次
发布时间:2019-06-20

本文共 2727 字,大约阅读时间需要 9 分钟。

使用反射可以在运行时动态实现接口。这可以使用类java.lang.reflect.Proxy。这个类的名称是我将这些动态接口实现称之为动态代理的原因。动态代理有多种不同的用途,例如,数据库连接和事务管理、用于单元测试的动态模拟对象、其他类似AOP的方法拦截。

创建代理

可以使用Proxy.newProxyInstance() 方法创建动态代理。newProxyInstance() 方法有3个参数:

  1. "load"动态代理类的ClassLoader
  2. 需要实现的接口数组
  3. 接收所有方法转发的InvocationHandler

示例如下:

InvocationHandler handler = new MyInvocationHandler();MyInterface proxy = (MyInterface) Proxy.newProxyInstance(                            MyInterface.class.getClassLoader(),                            new Class[] { MyInterface.class },                            handler);

运行代码后,变量proxy包含一个MyInterface接口的动态实现。所有对代理对象的调用将被转发到InvocationHandler接口的实现handlerInvocationHandler会在下一节介绍。

调用处理程序

前文已经提到,必须传一个InvocationHandler实现给Proxy.newProxyInstance()方法。所有动态代理调用都会传给InvocationHandler实现。InvocationHandler接口代码如下:

public interface InvocationHandler{  Object invoke(Object proxy, Method method, Object[] args)         throws Throwable;}

示例实现如下:

public class MyInvocationHandler implements InvocationHandler{  public Object invoke(Object proxy, Method method, Object[] args)  throws Throwable {    //do something "dynamic"  }}

传给invoke()方法的proxy参数是实现了对应接口的动态代理对象。通常情况下,你不需要这个对象。

传给invoke()方法的Method对象代表调用动态代理实现的接口的方法。从Method对象可以获得方法名称、参数类型、返回值类型等。节有关于方法更详细的描述。
Object[] args数组包含动态代理对象被调用的方法需要使用的参数。注意:基本类型(int、lang等)在动态代理中需要使用它们的包装类型(Integer、Long等)。

已知用例

已知动态代理至少用于以下目的:

  • 数据库连接和事务管理
  • 用于单元测试的动态对象模拟
  • 适配DI容器以自定义工厂接口
  • 类AOP的方法拦截

数据库连接与事务管理

Spring框架有一个事务代理,可以开启、提交/回滚事务。它是如何工作的,在文章中有详细讲解,所以这里只做简要介绍。调用序列和下面的流程类似:

web controller --> proxy.execute(...);  proxy --> connection.setAutoCommit(false);  proxy --> realAction.execute();    realAction does database work  proxy --> connection.commit();

单元测试动态对象模拟

利用动态代理实现单元测试的动态存根、代理和代理。当使用类B(真实接口)测试类A,可以传一个B的模拟实现给A以代替真实的B。所有的对B的方法调用都会被记录,也可以去设置B模拟对象的返回值。

此外,Butterfly Testing Tools允许您包装真实B为模拟B,所以对模拟B方法的调用都会被记录,并且可以转发到真实B。所以,这可以用来检测调用真实B的方法。例如,如果测试一个Dao,你可以包装数据库连接为一个模拟对象。Dao是不知道区别的,且Dao可以按常用方法读写数据,这是因为模拟对象转发给了数据库连接。但是,现在你也通过模拟对象检查Dao连接的属性,例如如果调用了connection.close()(或者没有调用),如果你期望的话。这通常不可能从DAO的返回值来确定。

DI容器与自定义工厂的适配

依赖注入容器有一个非常强大的特性,它允许你把整个容器注入到它创建的bean中。但是,你可能不希望在容器接口上有依赖,容器能自动适配你自定义的工厂接口。你只需要接口,不需要实现。因此,工厂接口和你的类可能像这样:

public interface IMyFactory {  Bean   bean1();  Person person();  ...}
public class MyAction{  protected IMyFactory myFactory= null;  public MyAction(IMyFactory factory){    this.myFactory = factory;  }  public void execute(){    Bean bean = this.myFactory.bean();    Person person = this.myFactory.person();  }}

MyAction类调用容器通过构造函数注入的IMyFactory实例的方法时,方法调用被转化为调用IContainer.instance()的方法,这是从容器的实例获取的方法。因此,一个对象可以把Butterfly Container作为一个运行时工厂使用,而不仅仅是在创建时注入。并且,这对Butterfly Container 接口没有特殊依赖。

类AOP的方法拦截

Spring框架可以拦截所有对bean的方法调用,假如bean实现了某些接口。Spring框架把bean包装成动态代理。所有bean调用都被动态代理截获。代理在把调用请求委派给bean之前或之后可以决定去调用其他对象的其他方法。

转载地址:http://lslhx.baihongyu.com/

你可能感兴趣的文章
iOS的AssetsLibrary框架访问所有相片
查看>>
读书笔记三
查看>>
数论 - 最小乘法逆元
查看>>
企业架构研究总结(22)——TOGAF架构开发方法(ADM)之信息系统架构阶段
查看>>
接口测试(三)--HTTP协议简介
查看>>
周志华《机器学习》课后答案——第4章.决策树
查看>>
frameset分帧问题
查看>>
特殊样式:ime-mode禁汉字,tabindex焦点
查看>>
linux
查看>>
Layout父元素点击不到的解决办法
查看>>
【面试次体验】堆糖前端开发实习生
查看>>
基于apache实现负载均衡调度请求至后端tomcat服务器集群的实现
查看>>
C#+QQEmail自动发送邮件
查看>>
[Hadoop]MapReduce多输出
查看>>
Android Activity详解(一)
查看>>
快准车服完成3000万元A+轮融资,年底将开始B轮融资
查看>>
让我去健身的不是漂亮小姐姐,居然是贝叶斯统计!
查看>>
MySQL 数据约束
查看>>
我的友情链接
查看>>
SERVLET容器简介与JSP的关系
查看>>