资讯专栏INFORMATION COLUMN

Java反射-动态代理

Acceml / 780人阅读

摘要:动态代理有多种不同的用途,例如,数据库连接和事务管理用于单元测试的动态模拟对象其他类似的方法拦截。调用序列和下面的流程类似单元测试动态对象模拟利用动态代理实现单元测试的动态存根代理和代理。框架把包装成动态代理。

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

创建代理

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

"load"动态代理类的ClassLoader

需要实现的接口数组

接收所有方法转发的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对象可以获得方法名称、参数类型、返回值类型等。Methods 节有关于方法更详细的描述。
Object[] args数组包含动态代理对象被调用的方法需要使用的参数。注意:基本类型(int、lang等)在动态代理中需要使用它们的包装类型(Integer、Long等)。

已知用例

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

数据库连接和事务管理

用于单元测试的动态对象模拟

适配DI容器以自定义工厂接口

类AOP的方法拦截

数据库连接与事务管理

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

web controller --> proxy.execute(...);
  proxy --> connection.setAutoCommit(false);
  proxy --> realAction.execute();
    realAction does database work
  proxy --> connection.commit();
单元测试动态对象模拟

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

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

依赖注入容器Butterfly Container有一个非常强大的特性,它允许你把整个容器注入到它创建的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之前或之后可以决定去调用其他对象的其他方法。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/71693.html

相关文章

  • Java 动态反射代理

    摘要:静态方法生成动态代理类同样需要通过类装载器来进行装载才能使用,它与普通类的唯一区别就是其字节码是由在运行时动态生成的而非预存在于任何一个文件中。 代理:设计模式 代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。showImg(https://segmentfault...

    Thanatos 评论0 收藏0
  • MyBatis初步

    摘要:本章主要介绍的是的基础应用和源码涉及的相关等,主要包含的内容有的简介反射动态代理包含代理和代理使用和代码生成器等。组件生命周期,如图测试代码生成器代码生成器,又称逆向工程。 本章主要介绍的是MyBatis的基础应用和源码涉及的相关等,主要包含的内容有MyBatis的简介、反射、动态代理(包含JDK代理和cglib代理)、MyBatis使用和代码生成器等。 1.1 MyBatis简介 M...

    MASAILA 评论0 收藏0
  • Java反射学习小记

    摘要:反射使用类对象提供的基本元数据,能从类对象中找出方法或字段的名称,然后获取表示方法或字段的对象。常见的反射手段有反射和反射。以之前的反射为例其中指定了方法的返回类型,其实不止如此。 Java反射机制主要提供了以下功能: 在运行时判断任意一个对象所属的类 在运行时构造任意一个类的对象 在运行时判断任意一个类所具有的成员变量和方法 在运行时调用任意一个对象的方法 生成动态代理 很多框架...

    frank_fun 评论0 收藏0
  • Java高级程序员必备:反射动态代理

    摘要:相比硬编码,反射要复杂的多,但其给我们带来了更大的灵活性。实际上构造函数也是类的静态方法,因此使用关键字创建类的新对象也会被当做对类的静态引用,从而触发类加载器对类的加载。基础基础主要是为反射提供通用特性的接口或基类。 1. Java类型系统 获取Java类型系统,主要有两个方式:一种是传统的RTTI(Run-Time Type Identification),它假定我们在编译时已经知...

    church 评论0 收藏0
  • Java编程思想》笔记14.类型信息

    摘要:接口与类型信息关键字的一种重要目标就是允许程序员隔离构件,进而降低耦合性。如果你编写接口,那么就可以实现这一目标,但是通过类型信息,这种耦合性还是会传播出去接口并非是对解耦的一种无懈可击的保障。 点击进入我的博客 运行时类型信息使得你可以在运行时发现和使用类型信息,主要有两种方式: 传统的RTTI,它假定我们在编译时已经知道了所有的类型; 反射机制,它允许我们在运行时发现和使用类的...

    Hwg 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<