Java代理的力量

在本文中,我将向您展示导致真正的Java功能(代理的使用)的途径。

它们无处不在,但只有少数人知道它们。 Hibernate用于延迟加载实体, Spring用于AOPLambdaJ用于DSL ,仅举几例:它们都使用了隐藏的魔术。 这些是什么? 它们是Java的动态代理。

每个人都知道GOF 代理设计模式:

通过充当传递实体或占位符对象,可以进行对象级访问控制。

同样,在Java中,动态代理是一个实例,它充当对真实对象的传递。 这种强大的模式使您可以从调用者的角度更改实际行为,因为方法调用可以被代理拦截。

纯Java代理

纯Java代理具有一些有趣的属性:

  • 它们基于接口的运行时实现
  • 它们是公开的,最终的而不是抽象的
  • 它们扩展了java.lang.reflect.Proxy

在Java中,代理本身并不像代理的行为那么重要。 后者是在java.lang.reflect.InvocationHandler的实现中完成的。 它只有一个方法可以实现:

publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)
  • proxy :在其上调用方法的代理实例
  • method :与在代理实例上调用的接口方法相对应的Method实例。 Method对象的声明类将是在其中声明该Method的接口,它可以是代理类通过其继承该方法的代理接口的超接口。
  • args :对象数组,其中包含在代理实例的方法调用中传递的参数的值;如果接口方法不接受任何参数,则为null 基本类型的参数包装在适当的基本包装器类的实例中,例如java.lang.Integerjava.lang.Boolean

让我们举一个简单的例子:假设我们想要一个不能添加元素的List 。 第一步是创建调用处理程序:

publicclassNoOpAddInvocationHandlerimplementsInvocationHandler{

  privatefinalListproxied;

  publicNoOpAddInvocationHandler(Listproxied){
    this.proxied=proxied;
  }

  publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{
    if(method.getName().startsWith("add")){
      returnfalse;
    }
    returnmethod.invoke(proxied,args);
  }
}

invoke方法将拦截方法调用,并且如果该方法以“ add”开头,则不执行任何操作。 否则,它将把调用传递给真实的代理对象。 这是一个非常粗糙的例子,但足以让我们了解其背后的魔力。

请注意,如果希望您的方法调用通过,则需要在真实对象上调用该方法。 为此,您将需要后者的引用,而invoke方法提供该内容。 这就是为什么在大多数情况下,将其传递给构造函数并将其存储为属性的一个好主意。

在任何情况下都不应在代理本身上调用该方法,因为调用处理程序将再次拦截该方法,并且您将面临StackOverflowError。

要创建代理本身:

Listproxy=(List)Proxy.newProxyInstance(
  NoOpAddInvocationHandlerTest.class.getClassLoader(),
  newClass[]{List.class},
  newNoOpAddInvocationHandler(list));

newProxyInstance方法采用3个参数:

  • 类加载器
  • 代理将实现的接口数组
  • 调用处理程序形式的王位背后的权力

现在,如果您尝试通过调用任何添加方法将元素添加到代理中,则它将没有任何效果。

CGLib代理

Java代理是接口的运行时实现。 对象不一定实现接口,并且对象的集合不一定共享相同的接口。 面对这样的需求,Java代理无法提供答案。

从这里开始CGLib的领域。 CGlib是基于ASM提供的字节码操作的第三方框架,可以帮助解决先前的限制。 首先提个建议,CGLib的文档与其功能不符:没有教程也没有文档。 您可以指望一些JavaDocs。 这表示CGLib放弃了纯Java代理所实施的许多限制:

  • 您不需要实现接口
  • 你可以延长课程

例如,由于Hibernate实体是POJO,因此Java代理无法用于延迟加载; CGLib代理可以。

纯Java代理和CGLib代理之间存在匹配:使用Proxy地方使用net.sf.cglib.proxy.Enhancer类,使用InvocationHandler地方使用net.sf.cglib.proxy.Callback 。 两个主要区别是Enhancer具有公共构造函数,并且不能仅通过其子接口之一使用Callback

  • Dispatcher :分派增强器回调
  • FixedValue :增强器回调,仅返回要从代理方法返回的值
  • LazyLoader :延迟加载增强器回调
  • MethodInterceptor :通用增强器回调,提供“周围建议”
  • NoOp :使用此Enhancer回调的方法将直接委托给基类中的默认(超级)实现

作为一个介绍性示例,让我们创建一个代理,无论后面的真实对象是什么,该代理都为哈希码返回相同的值。 该功能看起来像MethodInterceptor ,所以让我们这样实现它:

publicclassHashCodeAlwaysZeroMethodInterceptorimplementsMethodInterceptor{
  publicObjectintercept(Objectobject,Methodmethod,Object[]args,
    MethodProxymethodProxy)throwsThrowable{
    if("hashCode".equals(method.getName())){
      return0;
    }
    returnmethodProxy.invokeSuper(object,args);
  }
}

看起来非常类似于Java调用处理程序,不是吗? 现在,为了创建代理本身:

Objectproxy=Enhancer.create(
  Object.class,
  newHashCodeAlwaysZeroMethodInterceptor());

同样,代理创建也不令人惊讶。 真正的区别是:

  • 在此过程中不涉及任何界面
  • 代理创建过程还会创建代理对象。 从呼叫者的角度来看,代理和代理之间没有明确的界限
  • 因此,回调方法可以提供代理对象,而无需在您自己的代码中创建和存储它

结论

本文仅介绍了代理可以完成的工作。 无论如何,我希望它能让您看到Java具有一些有趣的功能和扩展点,无论是开箱即用还是来自某些第三方框架

您可以在此处找到Eclipse / Maven格式的本文资源

翻译自: https://blog.frankel.ch/the-power-of-proxies-in-java/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值