Java中的代理模式
代理模式
给某一个对象提供一个代理, 并由代理对象控制对原对象的引用。
就像我们在网上卖东西,不是直接卖给消费者,而是通过淘宝,京东等购物平台代理,让它们帮我们把东西卖给消费者(例子可能不是很恰当)。
代理的好处是我们无需修改委托类的实现,就可以修改代理后的细节,实现委托类与目标类的解耦。
静态代理
代理类在程序运行之前就已经存在,这种情况下的代理类是我们已经在代码中定义好了的。一般情况下,代理类和委托类会实现同一个接口或者继承自相同的父类,下面是一个简单的静态代理的例子:
首先定义代理类和委托类共同实现的接口
//代理类和委托类共同实现的接口
public interface Subject {
void doSomething();
void doAnotherThing();
}
然后是委托类的实现
//委托类
public class RealSubject implements Subject{
@Override
public void doSomething() {
System.out.println("RealSubject doSomething...");
}
@Override
public void doAnotherThing() {
System.out.println("RealSubject doAnotherThing...");
}
}
接下来实现代理类,通过聚合实现,在构造函数中传入被代理对象
//代理类
public class Proxy implements Subject {
private Subject realSubject;
//委托对象用接口实例,这样可以代理多个对象
public Proxy(Subject realSubject) {
this.realSubject = realSubject;
}
@Override
public void doSomething() {
if (realSubject instanceof RealSubject) {
System.out.println("in Proxy");
realSubject.doSomething();
}
}
@Override
public void doAnotherThing() {
if (realSubject instanceof RealSubject) {
System.out.println("in Proxy");
realSubject.doAnotherThing();
}
}
}
这样就可以在Main函数中调用了
public static void main(String[] args) {
Subject subject = new RealSubject();
Proxy proxy = new Proxy(subject);
proxy.doSomething();
subject.doAnotherThing();
}
打印结果如下
in Proxy
RealSubject doSomething...
RealSubject doAnotherThing...
动态代理
静态代理很方便,但是也存在一个问题,如果需要代理的接口有几十几百种方法需要实现,而我们只需要代理其中的一种两种方法,因为我们仍需要去实现哪些不需代理的方法,这种静态代理方法就显得相当累赘了,代理类的可读性也大大降低。Java提供了一种动态代理的方式来方便实现某一个方法的代理。
假如我们现在想在所有方法执行前打印一个method begin
日志,如果采用静态代理,如果被代理类实现的接口方法很多,要在每个方法执行前都加上System.out.println("method begin")
方法,如果采用动态代理,方法就比较简单了。
那么要如何使用动态代理呢?
使用动态代理
1.定义委托类和公共接口
这一点我们就利用之前静态代理定义的Subject接口和RealSubject委托类。
2.定义一个中介类实现InvocationHandler接口
中介类定义如下
public class SubjectDynamicProxy implements InvocationHandler {
private Object subject;
public SubjectDynamicProxy(Object subject) {
this.subject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName()+":method begin");
Object result = method.invoke(subject, args);
return result;
}
}
这个中介类的主要作用是在程序运行时生成的代理类的方法调用都会转到这个接口的invoke方法中,在中介类的构造函数中把要代理的对象传入,然后在invoke方法中添加统一的方法处理逻辑。
invoke方法中:
第一个参数proxy表示通过 Proxy.newProxyInstance()(下文会介绍)生成的代理类对象。
第二个参数method表示代理对象被调用的函数。
第三个参数args表示代理对象被调用的函数的参数。
而method.invoke(subject, args);
方法调用了函数的具体处理逻辑,subject表示被代理对象,调用代理对象的每个函数实际最终都是调用了InvocationHandler的invoke函数。这里我们在invoke实现中打印了方法名和method begin
3.生成代理对象
public static void main(String[] args) {
SubjectDynamicProxy dynamicProxy = new SubjectDynamicProxy(new RealSubject());
//加上这句将会产生一个$Proxy0.class文件,这个文件即为动态生成的代理类文件
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
Subject subject = (Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(),
new Class[]{Subject.class}, dynamicProxy);
subject.doSomething();
subject.doAnotherThing();
}
在main函数中通过Proxy.newProxyInstance()
方法生成代理对象,这个方法的声明如下
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
这个方法第一个参数loader表示类加载器对象,系统利用这个类加载器对象把代理类加载到JVM的方法区。
第二个参数interfaces表示这个代理类实现的接口列表
第三个参数h就是我们上面定义的实现InvocationHandler的接口对象
通过Proxy.newProxyInstance()
返回的对象就是真正的代理对象,我们调用代理对象的doSomething()
和doAnotherThing
方法,输出结果如下
doSomething:method begin
RealSubject doSomething...
doAnotherThing:method begin
RealSubject doAnotherThing...
可以看到通过动态代理确实实现了在代理方法中添加操作。
动态代理原理
在上述代码的main函数中
//加上这句将会产生一个$Proxy0.class文件,这个文件即为动态生成的代理类文件
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
会把生成的代理类的.class文件生成出来,用jd-gui反编译如下
package com.sun.proxy;
import com.example.proxy.Subject;
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 Subject
{
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m4;
private static Method m0;
public $Proxy0(InvocationHandler paramInvocationHandler)
{
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject)
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void doSomething()
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void doAnotherThing()
{
try
{
this.h.invoke(this, m4, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("com.example.proxy.Subject").getMethod("doSomething", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m4 = Class.forName("com.example.proxy.Subject").getMethod("doAnotherThing", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
可以看到,代理类先用反射的方式在类加载时获取到被代理类的方法名,然后在方法实现中调用this.h.invoke来交由InvocationHandler的实现类来处理这个方法。这就是我们在InvocationHandler的实现类的invoke()方法中代理逻辑能实现的原因。