代理模式也称为委托模式,是一种结构型设计模式,所谓代理,就是一个人或者机构代表另一个人或者机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。为其他对象提供一种代理以控制对这个对象的访问。
当无法或者不想直接访问某个对象或者访问某个对象存在困难时可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,委托对象与代理对象需要实现相同的接口。
代理有两种
1)静态代理
2)动态代理
先看看静态代理
静态代理代理模式的UML类图:
抽象主题类Subject.java
该类的主要职责是声明真实主题与代理的共同接口方法,该类既可以是一个抽象类也可以是一个接口。
package com.test.proxy;
public abstract class Subject {
public abstract void request();
}
实现抽象主题的真实主题类RealSubject.java
该类也称为被委托类或被代理类,该类定义了代理所表示的真实对象,由其执行具体的具体的业务逻辑方法吗,而客户端则通过代理类间接地调用真实主题类中方法。
package com.test.proxy;
public class RealSubject extends Subject{
@Override
public void request() {
System.out.println("real subject");
}
}
代理类ProxySubject.java
该类也称为委托类或代理类,该类持有一个对真实主题类的引用,在其所实现的接口方法中调用真实主题类响应的接口方法执行,以此起到代理的作用。
package com.test.proxy;
public class ProxySubject extends Subject {
//持有真实主题的引用
private RealSubject realSubject;
public ProxySubject(RealSubject realSubject) {
this.realSubject = realSubject;
}
@Override
public void request() {
//通过真实主题引用的对象调用真实主题中的逻辑方法
realSubject.request();
}
客户端Client.java
使用代理类的类型。
package com.test.proxy;
public class Client {
public static void main(String[] args) {
//构造一个真实主题
RealSubject realSubject = new RealSubject();
//通过真实主题对象构造一个代理对象
ProxySubject proxySubject = new ProxySubject(realSubject);
//调用代理的相关方法
proxySubject.request();
}
}
输出结果很简单不用给出了。
下面再来看动态代理
其UML类图如图所示
动态代理通过反射机制动态的生成代理者的对象,也就是说我们在coding的时候根本不用管代理类代理谁,代理谁将会在执行阶段决定,JDK给我们提供了一个便捷的动态代理接口InvocationHandler,实现了该接口重写其invoke方法就可以。
重写InvocationHandler中的invoke()方法能够让DynamicProxy实例在运行时调用被代理类的“对外服务”,即调用被代理类需要对外实现的所有接口中的方法,也就是完成对真实方法的调用,Java帮助文档中称这些真实方法为处理程序。
按照上面所述,我们肯定必须先把被代理类RealSubject已实现的所有interface都加载到JVM中,不然JVM怎么能够找到这些方法呢?明白了这个道理,那么我们就可以创建一个被代理类的实例,获得该实例的类加载器ClassLoader。所谓的类加载器ClassLoader,就是具有某个类的类定义,即类的内部相关结构(包括继承树、方法区等等)。
更重要的是,动态代理模式可以使得我们在不改变原来已有的代码结构的情况下,对原来的“真实方法”进行扩展、增强其功能,并且可以达到控制被代理对象的行为的目的。请详看下面代码中的DynamicProxy类,其中必须实现的invoke()方法在调用被代理类的真实方法的前后都可进行一定的特殊操作。这是动态代理最明显的优点。
下面来看代码:
抽象主题类Subject,真实主题类RealSubject不用修改,增加DynamicProxy.java动态代理类。
package com.uestc.dynamicProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 该代理类的内部属性是Object类型,实际使用的时候通过该类的构造方法传递进来一个对象
* 此外,该类还实现了invoke方法,该方法中的method.invoke其实就是调用被代理对象的将要
* 执行的方法,方法参数是sub,表示该方法从属于sub,通过动态代理类,我们可以在执行真实对象的方法前后 加入自己的一些额外方法。
*
*/
public class DynamicProxy implements InvocationHandler {
private Object sub;
public DynamicProxy(Object obj) {
this.sub = obj;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before calling: " + method);
method.invoke(sub, args);// 相当于realSubject.request
System.out.println("after calling: " + method);
return null;
}
}
Client修改如下
package com.uestc.dynamicProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
//被代理类
RealSubject realSubject = new RealSubject();
//动态代理类 也就是InvocationHandler
DynamicProxy dynamicProxy = new DynamicProxy(realSubject);
//获取ClassLoader
ClassLoader loader = realSubject.getClass().getClassLoader();
// 下面的代码一次性生成代理
/*
* loader : 被代理类的类加载器
* interfaces :被代理类已实现的所有接口,而这些是动态代理类要实现的接口列表
* handler : 用被代理类的实例创建动态代理类的实例,用于真正调用处理程序
* return :返回实现了被代理类所实现的所有接口的Object对象,即动态代理,需要强制转型
*/
Subject subject = (Subject) Proxy.newProxyInstance(loader, //the class loader to define the proxy class
new Class[]{Subject.class},// the list of interfaces for the proxy class to implement
dynamicProxy// the invocation handler to dispatch method invocations to
);
subject.request();
// subject 为一个借口的代理实例
System.out.println(subject.getClass());
}
}
输出:
before calling: public abstract void com.uestc.dynamicProxy.Subject.request()
real subject
after calling: public abstract void com.uestc.dynamicProxy.Subject.request()
class com.sun.proxy.$Proxy0
运行结果与前静态代理的一样,另外before calling与after calling是我们对原来的“真实方法”进行扩展、增强其功能。
我们也发现这个动态代理的实例的名称为“$Proxy0”,前面的都是我所用的包名,记得以前学习内部类时,内部类编译之后生成的.class文件的默认命名方式是带有“$”。但是现在这个肯定不是内部类,因为“$”之前并没有任何一个外部类的名称。其实Proxy0这个类是动态生成的代理者的对象。