引子
代理模式是java中很常见的一种设计模式,诸如Spring框架中用到的AOP编程思想遵循的就是代理模式的设计原则。用大白话来讲,即A对象中会调用B对象的方法,但是开发人员不想让A对象自己去做这件事,这时候就可以用代理模式的设计思想设计一个中间代理类,让代理类帮助A对象去实现对B对象中方法的调用。联系生活就会发现有很多的例子可以类比成代理模式,比如部分程序员要找女朋友要通过相亲的途径,这时候中间人(媒婆)就相当于一个代理。
重要概念
- 代理模式中涉及到两个必须存在的角色,即代理类和委托类。
- 代理类需要持有委托类的实例对象的引用。
- 按照代理类产生的时间节点,代理模式可被划分为静态代理模式和动态代理模式。
下面结合代码分析代理模式
代码分析
我们先来模拟一个简单的场景,一个人要找房子(买房子,租房子都可以),首先是他自己找。鉴于代理模式中的委托类必须要实现上层接口,所以我们先定义一个接口Person,在接口中定义两个方法。
package com.huang.proxy;
/**
* @Auther: huang.zh
* @Date: 2018/12/13 08:59
* @Description:
*/
public interface Person {
public void findHouse();
public void buyHouse();
}
然后是委托类,实现我们定义的接口
package com.huang.proxy;
/**
* @Auther: huang.zh
* @Date: 2018/12/13 08:59
* @Description:
*/
public class Huang implements Person {
private String sex ;
public Huang(String sex) {
this.sex = sex;
}
@Override
public void findHouse() {
System.out.println("面积为120平");
}
@Override
public void buyHouse() {
System.out.println("buy this house");
}
}
然后是测试类
package com.huang.proxy;
/**
* @Auther: huang.zh
* @Date: 2018/12/13 09:00
* @Description:
*/
public class TestProxy {
public static void main(String[] args) {
Person huang = new Huang("男");
huang.findHouse();
}
}
运行结果如下

这是我们在编程中习惯的方式,也是面向对象编程的体现。即当我们调用一个对象的属性或是方法时,习惯性的会用new关键字去创建这个对象的实例,通过该实例去调用。此时如果我们要在findHouse前后添加一些逻辑判断(如增加日志等操作),则不得不修改findHouse方法或者是在接口中定义新方法来调用,这就违背了开闭原则(对扩展开放,对修改封闭)。代理模式能很好地解决这一问题。
静态代理
在开发是就定义好代理类的模式被称作静态代理,即代理类由开发人员直接创建。
我们创建一个代理类staticProxy
package com.huang.proxy;
/**
* @Auther: huang.zh
* @Date: 2018/12/13 13:59
* @Description:
*/
public class StaticProxy implements Person{
private Person target;
public StaticProxy(Person target) {
this.target = target;
}
@Override
public void findHouse() {
System.out.println("find 120 area house by static proxy");
target.findHouse();
}
@Override
public void buyHouse() {
System.out.println("buy this house by static proxy");
target.buyHouse();
}
}
可以看到它实现了Person接口,通过构造函数获取委托类实例的引用,在其findHouse和buyHouse方法内部可以加入一些额外的逻辑,并调用了委托类的方法。
测试类中可以这样写
package com.huang.proxy;
/**
* @Auther: huang.zh
* @Date: 2018/12/13 09:00
* @Description:
*/
public class TestProxy {
public static void main(String[] args) {
/*Person huang = new Huang();
huang.findHouse();*/
Person huang = new Huang("男");
Person staticProxy = new StaticProxy(huang);
staticProxy.findHouse();
System.out.println("------------------------");
staticProxy.buyHouse();
}
}
输出结果如下,可以看到在调用委托类方法前先执行了额外加入的逻辑。

如果我们将调用委托类的方法这一步看做一个切点,则我们在切点前后想加入一些额外的操作(如日志,验证等),就可以只在代理类中进行添加。
动态代理
与静态代理不同的是,动态代理的代理对象是动态生成的,即不需要开发时去指定。
我们创建一个代理类HouseProxy
package com.huang.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @Auther: huang.zh
* @Date: 2018/12/13 09:05
* @Description:
*/
public class HouseProxy implements InvocationHandler {
private Person target;
public HouseProxy(Person target) {
this.target = target;
}
public Object newInstance(Person target){
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Dynamic proxy start find house");
if ("findHouse".equals(method.getName())){
return method.invoke(this.target,args);
}
return target.getClass().getMethod("buyHouse").invoke(this.target,null);
}
}
其中newInstance方法就是动态生成代理对象的方法。
在测试类中进行如下调用
package com.huang.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
/**
* @Auther: huang.zh
* @Date: 2018/12/13 09:00
* @Description:
*/
public class TestProxy {
public static void main(String[] args) {
/*Person huang = new Huang();
huang.findHouse();*/
/*Person huang = new Huang("男");
Person staticProxy = new StaticProxy(huang);
staticProxy.findHouse();
System.out.println("------------------------");
staticProxy.buyHouse();*/
//创建委托类实例
Person huang = new Huang("男");
//代理类需要持有委托类实例的引用
InvocationHandler houseProxy = new HouseProxy(huang);
//动态生成代理对象,注意与静态代理不同,静态代理中的代理对象直接是通过new关键字创建
Person proxy = (Person) Proxy.newProxyInstance(huang.getClass().getClassLoader(),huang.getClass().getInterfaces(),houseProxy);
proxy.findHouse();
System.out.println("------------------------");
proxy.buyHouse();
}
}
输出结果如下

在切点前后也执行了相应的操作,如果是统一的操作,只需要在代理类的invoke方法中增加逻辑,非常的灵活,易于后期维护。动态代理的运用也非常的常见,最显而易见的就是Spring中的AOP(面向切面编程),它就是借助于动态代理来实现松耦合。
走的更远
接下来我们继续深入,先来看看Proxy.newProxyInstance的源码
@CallerSensitive
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) {
//安全检测
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
//核心方法,生成代理对象
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
//通过生成的代理对象获取构造函数
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 (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
可见核心方法为getProxyClass0,其源码如下
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
}
其中proxyClassCache为代理类缓存变量,即如果缓存中存有该代理类,会将该代理类直接返回。那如果缓存中不存在呢?
/**
* a cache of proxy classes
*/
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
很明显,如果缓存中不存在需要的代理类,则会调用代理类工厂ProxyClassFactory生成代理类。
我们自己实现一个ProxyGenerator类,其作用为将我们需要的代理类编译后的class文件输出到本地。代码如下
package com.huang.proxy;
import java.io.FileOutputStream;
/**
* @Auther: huang.zh
* @Date: 2018/12/13 10:01
* @Description:
*/
public class ProxyGenerator {
public static void main(String[] args) {
//生成代理类字节码
byte[] classContent = sun.misc.ProxyGenerator.generateProxyClass("$Proxy0",Huang.class.getInterfaces());
String filePath = "D://$Proxy0.class";
try{
//将代理类字节码写入磁盘目录
FileOutputStream fos = new FileOutputStream(filePath);
fos.write(classContent);
fos.close();
System.out.println("success");
} catch (Exception e){
System.out.println("failed");
}
}
}
运行后会在D盘生成代理类的class文件,用反编译工具XJad打开会看到如下结果
// Decompiled by Jad v1.5.8e2. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://kpdus.tripod.com/jad.html
// Decompiler options: packimports(3) fieldsfirst ansi space
import com.huang.proxy.Person;
import java.lang.reflect.*;
public final class $Proxy0 extends Proxy
implements Person
{
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m4;
private static Method m0;
public $Proxy0(InvocationHandler invocationhandler)
{
super(invocationhandler);
}
public final boolean equals(Object obj)
{
try
{
return ((Boolean)super.h.invoke(this, m1, new Object[] {
obj
})).booleanValue();
}
catch (Error ) { }
catch (Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final void findHouse()
{
try
{
//super.h即为父类Proxy中的Invacation实例
super.h.invoke(this, m3, null);
return;
}
catch (Error ) { }
catch (Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString()
{
try
{
return (String)super.h.invoke(this, m2, null);
}
catch (Error ) { }
catch (Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final void buyHouse()
{
try
{
//super.h即为父类Proxy中的Invacation实例
super.h.invoke(this, m4, null);
return;
}
catch (Error ) { }
catch (Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode()
{
try
{
return ((Integer)super.h.invoke(this, m0, null)).intValue();
}
catch (Error ) { }
catch (Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
Class.forName("java.lang.Object")
});
//通过反射机制获取findHouse的Method对象
m3 = Class.forName("com.huang.proxy.Person").getMethod("findHouse", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
//通过反射机制获取buyHouse的Method对象
m4 = Class.forName("com.huang.proxy.Person").getMethod("buyHouse", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
}
catch (NoSuchMethodException nosuchmethodexception)
{
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
}
catch (ClassNotFoundException classnotfoundexception)
{
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
}
可以看到所有委托类方法的执行都是在调用Invovation.invoke方法去完成的,即使用了动态代理后对委托类方法的调用全部转为invoke方法的调用。
总结
关于代理模式,其中的静态代理和动态代理并无好坏之分,开发人员应该结合场景和自身经验选择一种最合适的。设计模式的存在自有它存在和被推崇的原因,需要我们养成良好的编程思想,多运用设计模式实现代码的高度重用和松耦合。
网友评论