代理模式式是常用的结构型设计模式之一,当无法直接获取到某个模块中某个类中的代码进行修改时,或像在一些好的模块中动态的添加一些其他功能时。则可以使用到代理模式。
代理模式又细分为动态代理、静态代理、远程代理等等。
举个生活中的例子进行类比,代理模式类似与现在流行的代购,客户想获取懂国外的某个品牌产品,但由于种种原因无法去直接获取到该产品,这时候代购就出现了,代理就像一个中间人,客户只需要跟代理进行沟通,到代理处获取这个产品,进而让代理再去完成那个跟那个品牌商拿货的动作,代理是不会生产那个品牌的产品的,代购也是依赖与品牌商那拿货。而在拿到货之前或之后,代理可以做些其他的业务操作,比如在拿到产品返回用户之前,收取用户一点代购费之类的。其实代理就是这么简单。商品代购过程如下图所示:
代理模式定义如下:
代理模式:给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问。 Proxy Pattern: Provide a surrogate or placeholder for another object to control access to it.
貌似官方的定义总是那么拗口,用我的话来理解就是,在代理模式中引入了一个新的代理对象,代理对象在客户端对象和目标对象之间起到中介的作用,它去掉客户不能看到的内容和服务或者增添客户需要的额外的新服务。
代理模式包含如下三个角色:
(1) Subject(抽象主题角色):它声明了真实主题和代理主题的共同接口,这样一来在任何使用真实主题的地方都可以使用代理主题,客户端通常需要针对抽象主题角色进行编程。
(2) Proxy(代理主题角色):它包含了对真实主题的引用,从而可以在任何时候操作真实主题对象;在代理主题角色中提供一个与真实主题角色相同的接口,以便在任何时候都可以替代真实主题;代理主题角色还可以控制对真实主题的使用,负责在需要的时候创建和删除真实主题对象,并对真实主题对象的使用加以约束。通常,在代理主题角色中,客户端在调用所引用的真实主题操作之前或之后还需要执行其他操作,而不仅仅是单纯调用真实主题对象中的操作。
(3) RealSubject(真实主题角色):它定义了代理角色所代表的真实对象,在真实主题角色中实现了真实的业务操作,客户端可以通过代理主题角色间接调用真实主题角色中定义的操作。
下面来看代码:
interface Subject
{
void request();
}
class RealSubject implements Subject
{
public override void request()
{
//业务方法具体实现代码
}
}
class Proxy implements Subject
{
//维持一个对真实主题对象的引用
private RealSubject realSubject = new RealSubject();
public void PreRequest()
{
…...
}
@override
public void request()
{
PreRequest();
realSubject.Request(); //调用真实主题对象的方法
PostRequest();
}
public void PostRequest()
{
……
}
}
还是说上面代购的例子,当不同的用户需要不同国家的不同商品时,如果采用静态代理的方法,将会要写很多这样的类似代码,代码冗余度增加,而且不便于拓展。 还有就是静态代理,代理类的生成需要一个确切的原始类,这在实际很多场景中都有局限性。这时动态代理就弥补了这些不足。
动态代理中所谓的“动态”,是针对使用java代码实际编写了代理类的“静态”代理而言的,它的优势不在于省去了编写代理类那一点工作量,更多的是实现了可以在原始类和接口还未知的时候,就确定代理类的代理行为,当代理类和原始类脱离直接联系后,就可以很灵活地重用于不同的应用场景之中。
对于很多框架而言,动态代理所带来的特性,尤为重要。比如Spring中AOP的实现,像这种框架在编写时,是不知道所需要代理的类是什么样的,如果采用静态代理的方式,将无法完成原始类的代理工作。
还是来通过代码来感受下,java提供的动态代理,是怎么在未知原始类和接口的情况下,完成代理工作的。
动态代理简单示例:
package com.javaSE.myproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class DynamicProxyTest {
interface IHello{
void sayHello();
}
static class Hello implements IHello{
@Override
public void sayHello() {
System.out.println("Hello word!!!");
}
}
//动态代理实现了在原始类和接口还未知的情况下就能先确定代理类的代理行为。这样就可以很灵活的运用在不同的场景中
static class DynamicProxy implements InvocationHandler{
T originalObj;
Object bind(T originalObj){
this.originalObj = originalObj;
return Proxy.newProxyInstance(originalObj.getClass().getClassLoader(), originalObj.getClass().getInterfaces(),this);
}
/*
* 调用被代理对象中的任何方法都会执行该方法
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("welcome!!!");
return method.invoke(originalObj, args);
}
}
public static void main(String[] args) {
//如果想看一看这个运行时产生的代理类中写了些什么,在以在执行时加入如下一行代码
System.getProperties().put("sum.misc.ProxyGenerator.saveGeneratedFiles", "true");
IHello proxy =(IHello)new DynamicProxy().bind(new Hello());
proxy.sayHello();
}
}
在这里还有几个小问题,为什么生成的代理类,执行该类的任意方法都是调用InvocationHandler 中的invoke方法呢?java虚拟机给我们生成的代理类到底是什么样子的呢?我们通过在执行时加入如下一行代码,找到JVM所生成的代理类。
System.getProperties().put("sum.misc.ProxyGenerator.saveGeneratedFiles", "true");
反编译$Proxy0.class动态代理类的代码
package com.javaSE.myproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements DynamicProxyTest.IHello {
private static Method m1;
private static Method m0;
private static Method m3;
private static Method m2;
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals",
new Class[] { Class.forName("java.lang.Object") });
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m3 = Class.forName("com.javaSE.myproxy.DynamicProxyTest$IHello").getMethod("sayHello", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
} catch (NoSuchMethodException nosuchmethodexception) {
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
} catch (ClassNotFoundException classnotfoundexception) {
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
public $Proxy0(InvocationHandler invocationhandler) {
super(invocationhandler);
}
@Override
public final boolean equals(Object obj) {
try {
return ((Boolean) this.h.invoke(this, m1, new Object[] { obj })).booleanValue();
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public final int hashCode() {
try {
return ((Integer) this.h.invoke(this, m0, null)).intValue();
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final void sayHello() {
try {
this.h.invoke(this, m3, null);
return;
} catch (Error e) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public final String toString() {
try {
return (String) this.h.invoke(this, m2, null);
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
}
这个代理类的实现代码也很简单,它为传入接口中的每一个方法,以及继承至Object中继承来的equals(),hashCode(),toString()方法都生成了对应的实现,并且统一调用了InvocationHandler对象的invoke( )方法(代码中的"this.h",就是父类Proxy中保存的InvocationHandler 实例变量)来实现这些方法的内容,各个方法的区别不过是传入的参数和Method对象有所不同而已,所以无论调用动态代理类的哪一个方法,实际上都是在执行InvocationHandler.invoke()中的代理逻辑。
<h6>至此,关于代理模式,动态代理的使用及原理都讲完了,是不是感觉并没有啥难的~</h6>