6.5 代理

6.5 代理

  利用代理可以在运行时创建一个实现了一组给定接口的新类,这种功能只有在编译时无法确定需要实现哪个接口时才有必要使用。

  结社有一个表示接口的Class对象(有可能只包含一个接口),它的确切类型在编译时无法知道。要想构造一个实现这些接口的类,就需要使用newInstance方法或反射找出这个类的构造器。但是不能实例化一个接口,需要在程序处于运行状态时定义一个新类。

常规但比较笨重的解决方式:程序生成代码,将代码放置在一个文件中,调用编译器,然后再加载结果类文件。这样的速度比较慢,并且需要将编译器与程序放在一起。

代理机制的解决方式:代理类在运行时创建全新的类,这样的代理类就能够实现指定的接口。尤其是,它具有下列方法:

  • 指定接口所需要的全部方法;
  • Object类中的全部方法,例如,toString、equals等。

  不能在运行的时候定义这些方法的新代码,而需要提供一个调用处理器(invocation handler)。调用处理器是实现了InvocationHandler接口的类的对象,这个接口中只有一个方法:

Object invoke(Object proxy, Method method, Object[] args)

  无论何时调用代理对象的方法,调用处理器的invoke方法都会被调用,并向其传递Method对象和原理的调用参数,调用处理器必须给出处理调用的方式。

  想要创建一个代理对象,需要使用Proxy类中的newProxyInstance方法,这个方法具有三个参数:

  • 一个类加载器(class loader),作为java安全模型的一部分,对于系统类和从因特网下载下来的类,可以使用不同的类加载器,用null表示使用默认的类加载器;
  • 一个Class对象数组,每个元素都是需要实现的接口;
  • 一个调用处理器。

简单的代理实现(被代理对象必须提前存在)

package proxyDemo;

interface Subject
{
    abstract public void request();
}
 
class RealSubject implements Subject
{
    @Override
    public void request() {
        System.out.println("real subject");
    }
}
 
class ProxySubject implements Subject
{
    Subject subject;
    public ProxySubject() {
    }
    public ProxySubject(Subject subject)
    {
        this.subject=subject;
    }
    @Override
    public void request() 
    {
        System.out.println("预处理工作");
        subject.request();
        System.out.println("后续工作");
    }
}
public class ProxyModel 
{
     public static void main(String[] args)
     {
        Subject subject=new ProxySubject(new RealSubject());
        subject.request();
     }
}

输出结果:

预处理工作
real subject
后续工作

  客户实际需要调用的是RealSubject类的request()方法,现在用ProxySubject来代理 RealSubject类,同样达到目的,同时还可以封装其他方法(可以做一些预处理,和后续工作),可以处理一些其他问题。    另外,如果要按照上述的方法使用代理模式,那么真实角色必须是事先已经存在的,并将其作为代理对象的内部属性。但是实际使用时,一个真实角色必须 对应一个代理角色,如果大量使用会导致类的急剧膨胀;此外,如果事先并不知道真实角色,该如何使用代理呢?这个问题可以通过Java的动态代理类来解决。

动态代理

   java的动态代理位于java.lang.reflect包下,一般涉及到两个类:

  • Interface InvocationHandler:该接口仅定义一个方法Object:invoke(Object obj, Method method, Object[] args)。

obj:代理类

method:被代理的方法

args:该方法的参数数组

  • Proxy:该类即为动态代理类,作用类似于之前的ProxySubject,主要包含以下内容:  

      Protected Proxy(InvocationHandler h):构造函数,用于内部的h赋值

      Static Class getProxyClass(ClassLoader loader, Class[] interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全                                                                                                                          部接口的数组

      Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理类的一个实例,返回后的代理类可以                                                                                                                                                                当做被代理类使用

  所谓Dynamic Proxy是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些 interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然啦,这个Dynamic Proxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。

package ProxyDemo1;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface Subject
{
	abstract public void request();
}

class RealSubject implements Subject
{
	public RealSubject(){}
	public void request()
	{
		System.out.println("real subject");
	}
}

class ProxySubject implements InvocationHandler
{
	private Subject obj;
	public ProxySubject(){}
	public ProxySubject(Subject obj)
	{
		this.obj = obj;
	}
	
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
	{
		System.out.println("预处理工作");
		method.invoke(obj, args);
		System.out.println("后续工作");
		return null;
	}
}
public class ProxyDemo 
{
	 public static void main(String[] args) {
         RealSubject realSubject = new RealSubject();
         Class<?>cla = realSubject.getClass();
         InvocationHandler handler = new ProxySubject(realSubject);
         Subject subject = (Subject)Proxy.newProxyInstance(cla.getClassLoader(), cla.getInterfaces(), handler);
         subject.request();
     }

}

输出结果:

预处理工作
real subject
后续工作

  通过这种方式,被代理的对象(RealSubject)可以在运行时动态改变,需要控制的接口 (Subject接口)可以在运行时改变,控制的方式(DynamicSubject类)也可以动态改变,从而实 现了非常灵活的动态代理关系。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏博客园

Core官方DI解析(2)-ServiceProvider

ServiceProvider是我们用来获取服务实例对象的类型,它也是一个特别简单的类型,因为这个类型本身并没有做什么,其实以一种代理模式,其核心功能全部都在I...

1062
来自专栏雨尘分享

4.Block的类型

1615
来自专栏前端菜鸟变老鸟

截取url中的参数(支持截取一个或全部)

alert(JSON.stringify(that.getParamByUrl(url, ‘allparam’)));

1212
来自专栏流柯技术学院

TestNG 三 测试方法

测试方法是可以带有参数的。每个测试方法都可以带有任意数量的参数,并且可以通过使用TestNG的@Parameters向方法传递正确的参数。

1013
来自专栏大内老A

Dora.Interception, 为.NET Core度身打造的AOP框架:不一样的Interceptor定义方式

相较于社区其他主流的AOP框架,Dora.Interception在Interceptor提供了完全不同的编程方式。我们并没有为Interceptor定义一个接...

731
来自专栏Coding01

轻轻玩转 Laravel Helpers 函数

在使用 Laravel 函数时,我们都避免不了使用其提供的各种各样的全局函数,也称为辅助函数。

2341
来自专栏博客园

Core官方DI解析(2)-ServiceProvider

ServiceProvider是我们用来获取服务实例对象的类型,它也是一个特别简单的类型,因为这个类型本身并没有做什么,其实以一种代理模式,其核心功能全部都在I...

1354
来自专栏博岩Java大讲堂

Java虚拟机--线程上下文类加载器

2314
来自专栏大内老A

Dora.Interception, 为.NET Core度身打造的AOP框架:不一样的Interceptor定义方式

相较于社区其他主流的AOP框架,Dora.Interception在Interceptor提供了完全不同的编程方式。我们并没有为Interceptor定义一个接...

1075
来自专栏从零开始学 Web 前端

09 - JavaSE之线程

PS: 如果我们没有 new一个 Thread 对象出来,而是直接使用 MyThread 的 run 方法(mt.run()),这就是方法调用,而不是启动线程了...

1315

扫码关注云+社区

领取腾讯云代金券