介绍
代理指设计模式中的代理模式,它是为其它对象提供一种代理从而控制对这个对象的访问。当无法或不想直接访问某个对象可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,委托对象与代理对象需要实现相同的接口。
静态代理
代理模式需要有抽象主题类Subject,主要职责是声明真实主题与代理的共同接口方法;真实主题类RealSubject,表示被代理的真实对象,由它来执行具体的业务逻辑方法;代理类ProxySubject,该类持有一个对真实主题类的一个引用,在其所实现的接口方法中调用真实主题类中相应的接口方法执行,以此起到代理的作用;客户类Client,使用代理类的类型。
根据以上角色定义,编写一个简单的代理模式实现:
class Client {
public static void main(String[] args) {
IHello hello = new StaticProxy();
hello.hello();
}
interface IHello {
void hello();
}
static class Hello implements IHello
@Override
public void hello() {
System.out.println("hello");
}
}
static class StaticProxy implements IHello {
private Hello mHello;
public StaticProxy() {
mHello = new Hello();
}
@Override
public void hello() {
mHello.hello();
}
}
}
输出结果:
hello
StaticProxy代理了真实主题对象Hello,而客户端类通过StaticProxy来间接调用Hello的方法,得到了需要的结果。以上可以看出代理模式很简单,其主要还是一种委托机制,真实对象将方法的执行委托给代理对象,所以代理模式也可以称为委托模式。
动态代理
静态代理的示例代码如上,对于代理类来说,真实主题类型在编译期是可知的,而动态代理与静态代理相反,编译期不可知,到了运行期才可知。
为了实现动态代理,Java提供了一个接口:InvocationHandler。implement该接口并重写invoke方法。简单实现如下:
class Client {
public static void main(String[] args) {
DynamicProxy dynamicProxy = new DynamicProxy();
IHello hello = (IHello) dynamicProxy.bind(new Hello());
hello.hello();
}
interface IHello {
void hello();
}
static class Hello implements IHello {
@Override
public void hello() {
System.out.println("hello");
}
}
static class DynamicProxy implements InvocationHandler {
private Object mOriginalObj;
Object bind(Object originalObj) {
mOriginalObj = originalObj;
return Proxy.newProxyInstance(originalObj.getClass().getClassLoader()
, originalObj.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("welcome");
Object result = method.invoke(mOriginalObj, args);
return result;
}
}
}
输出结果:
welcome
hello
上面的代码有个小瑕疵,就是Client对象能够直接获取真实对象Hello,用代理模式有点多余。各位明白意思就好。
通过Proxy.newProxyInstance方法返回一个代理对象,通过这个代理对象调用真实方法时,会调用InvocationHandler的invoke方法,invoke方法会调用真实方法。除此之外,还可以在invoke方法里做额外的操作,如上面的例子中,就额外输出了welcome。
Java动态代理原理
上面的示例代码唯一不是自己写的就是Proxy.newProxyInstance,跟踪这个方法的源码:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
//验证,主要验证ClassLoader是否为null,传入的接口是否时public。
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* 生成代理类的字节码
*/
Class<?> cl = getProxyClass0(loader, intfs);
//根据新建的代理类,创建其对象。
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
} //catch略。。。
}
在getProxyClass0方法里,最终会调用ProxyGenerator.generateProxyClass方法来生成代理类的字节码的byte[]数组。
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
如果想要查看这个自动生成的类,可以输入:
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");//注意是字符串"true",而不是boolean true。
然后会在根目录下出现这个calss文件,反编译查看内容:
final class $Proxy0 extends Proxy implements IHello {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void hello() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("com.mezzsy.personalwebsite.Client$IHello").getMethod("hello");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
可以看到,这个自动生成的类为每个方法生成了对应的Method对象,包括Object中的equals、toString和hashCode方法。调用每个方法时都会调用InvocationHandler的invoke方法,InvocationHandler对象时通过构造方法传入的。
小结
Java的动态代理是通过InvocationHandler这个接口来完成的,用户通过重写其invoke方法来完成具体的代理逻,对于代理部分,真实类型是不可见的。这一实现原理就是运行时生成class。
类的代理
Java能不能代理类呢?更改一下上面的代码:
class Client {
public static void main(String[] args) {
DynamicProxy dynamicProxy = new DynamicProxy();
Hello hello = (Hello) dynamicProxy.bind(new Hello());
hello.hello();
}
static class Hello {
public void hello(){
System.out.println("hello");
}
}
static class DynamicProxy implements InvocationHandler {
private Object mOriginalObj;
Object bind(Object originalObj) {
mOriginalObj = originalObj;
return Proxy.newProxyInstance(originalObj.getClass().getClassLoader()
, new Class[]{mOriginalObj.getClass()}, this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("welcome");
Object result = method.invoke(mOriginalObj, args);
return result;
}
}
}
输出结果:
Exception in thread "main" java.lang.IllegalArgumentException: com.mezzsy.learnsomething.designpattern.proxy.demo1.Client$Hello is not an interface
at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:590)
at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:557)
at java.lang.reflect.WeakCache$Factory.get(WeakCache.java:230)
at java.lang.reflect.WeakCache.get(WeakCache.java:127)
at java.lang.reflect.Proxy.getProxyClass0(Proxy.java:419)
at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:719)
at com.mezzsy.learnsomething.designpattern.proxy.demo1.Client$DynamicProxy.bind(Client.java:30)
at com.mezzsy.learnsomething.designpattern.proxy.demo1.Client.main(Client.java:14)
可以看到出错了,并且错误提示是Hello is not an interface,就是说Hello并不是一个接口,所以Proxy只能代理接口类型。上面自动生成的类继承了Proxy类,而Java不允许多继承,这一点也能间接证明。
如果想要动态代理类,可以使用第三方库,如cglib。简单使用如下:
public class Client {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Hello.class);
enhancer.setCallback(new DynamicProxy(new Hello()));
Hello proxy = (Hello) enhancer.create();
proxy.hello();
}
static class Hello {
public void hello() {
System.out.println("hello");
}
}
static class DynamicProxy implements InvocationHandler {
private Object mOriginalObj;
public DynamicProxy(Object originalObj) {
mOriginalObj = originalObj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("welcome");
Object result = method.invoke(mOriginalObj, args);
return result;
}
}
}
输出结果:
welcome
hello
代码和Jdk的动态代理差不多,Enhancer类相当于Proxy,通过create方法返回一个代理对象,具体代理的逻辑也是由InvocationHandler来完成。
cglib动态代理的原理和jdk差不多,也是运行时生成字节码来完成的,只不过cglib用ASM库来生成字节码,在性能上优于jdk。
参考
- 《Android源码设计模式解析与实战》
- 《深入理解Java虚拟机 JVM高级特性与最佳实践》
- 《CGLIB原理及实现机制》