代理在Dubbo中用到很多,无论是服务暴露端生成的invoker代理或者是消费者端的代理调用。Dubbo中默认采用javassist代理在内存中动态生成所代理的字节码。
我们来看下JavassistProxyFactory中的代码
public T getProxy(Invoker invoker, Class>[] interfaces) {
return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}
public Invoker getInvoker(T proxy, Class type, URL url) {
// TODO Wrapper类不能正确处理带$的类名
final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
return new AbstractProxyInvoker(proxy, type, url) {
@Override
protected Object doInvoke(T proxy, String methodName,
Class>[] parameterTypes,
Object[] arguments) throws Throwable {
return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
}
};
}
能看到,它就实现了两个方法,恰好对应消费者端与生产端的代理实现,下面分析getProxy(),即消费者端。getProxy需要两个参数,一个是需要被代理的类invoker,另一个是需要实现的接口,这接口中定义的方法,需要通过代理达到远程调用生产端的作用。首先调用getProxy,并传入接口数组(java允许实现多接口)。
public static Proxy getProxy(Class>... ics)
{
return getProxy(ClassHelper.getCallerClassLoader(Proxy.class), ics);
}
在生成字节码之前,需要得到当前Proxy的类加载器,防止类加载器之间的隔离。我们来看下getProxy具体生成字节码的逻辑,代码很长我们一点一点来看。
if( ics.length > 65535 )
throw new IllegalArgumentException("interface limit exceeded");
StringBuilder sb = new StringBuilder();
for(int i=0;i tmp = null;
try
{
tmp = Class.forName(itf, false, cl);
}
catch(ClassNotFoundException e)
{}
if( tmp != ics[i] )
throw new IllegalArgumentException(ics[i] + " is not visible from class loader");
sb.append(itf).append(';');
}
先是对接口个数做了个限制,不能大于65535。然后逐个判断传入的接口组成的数组是否每个元素都是接口,并通过当前的类加载器去判断是否能加载,然后将所有接口名拼接,用来获得所有接口名所组成的字符串。
String key = sb.toString();
// get cache by class loader.
Map cache;
synchronized( ProxyCacheMap )
{
cache = ProxyCacheMap.get(cl);
if( cache == null )
{
cache = new HashMap();
ProxyCacheMap.put(cl, cache);
}
}
Proxy proxy = null;
synchronized( cache )
{
do
{
Object value = cache.get(key);
if( value instanceof Reference> )
{
proxy = (Proxy)((Reference>)value).get();
if( proxy != null )
return proxy;
}
if( value == PendingGenerationMarker )
{
try{ cache.wait(); }catch(InterruptedException e){}
}
else
{
cache.put(key, PendingGenerationMarker);
break;
}
}
while( true );
}
ProxyCacheMap缓存着每个类加载器加载过了的类,如果接口组成的字符串为key能在缓存中找到已经生成了的代理类,那么就可以直接返回。否则,创建PedingGenerationMaker,往下。
ccp = ClassGenerator.newInstance(cl);
Set worked = new HashSet();
List methods = new ArrayList();
通过类加载器获得ClassGernerator,实际上是获得类加载器的对象池。
public static ClassGenerator newInstance()
{
return new ClassGenerator(getClassPool(Thread.currentThread().getContextClassLoader()));
}
worked用于存储方法的描述符,methods来保存需要被代理的方法。 往下。
for(int i=0;i rt = method.getReturnType();
Class>[] pts = method.getParameterTypes();
StringBuilder code = new StringBuilder("Object[] args = new Object[").append(pts.length).append("];");
for(int j=0;j
先对每一个接口进行判断。如果接口不是public,判断该接口是否与至少一个public接口在同一包下,否则报错。然后遍历接口的每一个方法取得方法描述符,加到worked中,再取得方法的参数,返回值动态生成被代理的字节码,这里只生成方法体的字节码。
假设生成的返回值为int类型,形参个数一个,在java方法调用中,会给第一个参数位置放上this,所以参数配置从第二个开始:
Object[]args = new Object[1];
args[0]= 下标为1的参数;
Objectret = handler.invoke(this, methods[该方法在代理类中的下标], args);
returnret == null ? (int) 0 :((Integert)ref).intValue();
最后调用ClassGenerator中的addMethod()方法加入代理的字节码。
public ClassGenerator addMethod(String name, int mod, Class> rt, Class>[] pts, Class>[] ets, String body)
{
StringBuilder sb = new StringBuilder();
sb.append(modifier(mod)).append(' ').append(ReflectUtils.getName(rt)).append(' ').append(name);
sb.append('(');
for(int i=0;i 0 )
sb.append(',');
sb.append(ReflectUtils.getName(pts[i]));
sb.append(" arg").append(i);
}
sb.append(')');
if( ets != null && ets.length > 0 )
{
sb.append(" throws ");
for(int i=0;i 0 )
sb.append(',');
sb.append(ReflectUtils.getName(ets[i]));
}
}
sb.append('{').append(body).append('}');
return addMethod(sb.toString());
}
这无非给方法加了申明跟结尾,把方法的修饰符,方法名,以及参数类型,参数名(argi),以及异常的抛出都在这拼接。拼接完后,将拼接好的字符串加入到mCopyMethods map中。
if( pkg == null )
pkg = PACKAGE_NAME;
// create ProxyInstance class.
String pcn = pkg + ".proxy" + id;
ccp.setClassName(pcn);
ccp.addField("public static java.lang.reflect.Method[] methods;");
ccp.addField("private " + InvocationHandler.class.getName() + " handler;");
ccp.addConstructor(Modifier.PUBLIC, new Class>[]{ InvocationHandler.class }, new Class>[0], "handler=$1;");
ccp.addDefaultConstructor();
Class> clazz = ccp.toClass();
clazz.getField("methods").set(null, methods.toArray(new Method[0]));
// create Proxy class.
String fcn = Proxy.class.getName() + id;
ccm = ClassGenerator.newInstance(cl);
ccm.setClassName(fcn);
ccm.addDefaultConstructor();
ccm.setSuperClass(Proxy.class);
ccm.addMethod("public Object newInstance(" + InvocationHandler.class.getName() + " h){ return new " + pcn + "($1); }");
Class> pc = ccm.toClass();
proxy = (Proxy)pc.newInstance();
然后给该类设置类名为包名.proxy+id(生成代理的数量),这个类就是具体的代理的Instance类。然后为其添加字段。
存放被调用的方法的methods:public static java.lang.reflect.Method[] methods;存放被代理的invoker的handler:private InvocationHandler handler;只有handler的构造方法;无参构造方法。
之后通过toClass()动态得到字节码编译后的类,最后将所有的实际调用方法通过set注入。
之前得到的是Instance类,接下来生成的是Proxy类,它内继承了Proxy.class,实现了newInstance方法:
public Object newInstance(" + InvocationHandler.class.getName() + " h){ return new " + pcn + "($1); }"); 其中pcn为对应的Instance类,即上文生成的。然后toClass得到proxy类,再得到实例,返回,在JavassistProxyFactory中得到proxy实例后马上调用newInstance方法,这个方法会根据传入的参数,通过新的类名的构造方法得到新的proxyInstance实例。ccp.addConstructor(Modifier.PUBLIC, new Class>[]{ InvocationHandler.class }, new Class>[0], "handler=$1;");读到这里$1很突兀,猜测是形参中下标为1的参数,非静态方法下标为0是this
到这里基本结束了,我们来看下toClass()的逻辑。
CtClass ctcs = mSuperClass == null ? null : mPool.get(mSuperClass);
if( mClassName == null )
mClassName = ( mSuperClass == null || javassist.Modifier.isPublic(ctcs.getModifiers())
? ClassGenerator.class.getName() : mSuperClass + "$sc" ) + id;
mCtc = mPool.makeClass(mClassName);
if( mSuperClass != null )
mCtc.setSuperclass(ctcs);
mCtc.addInterface(mPool.get(DC.class.getName())); // add dynamic class tag.
if( mInterfaces != null )
for( String cl : mInterfaces ) mCtc.addInterface(mPool.get(cl));
if( mFields != null )
for( String code : mFields ) mCtc.addField(CtField.make(code, mCtc));
if( mMethods != null )
{
for( String code : mMethods )
{
if( code.charAt(0) == ':' )
mCtc.addMethod(CtNewMethod.copy(getCtMethod(mCopyMethods.get(code.substring(1))), code.substring(1, code.indexOf('(')), mCtc, null));
else
mCtc.addMethod(CtNewMethod.make(code, mCtc));
}
}
if( mDefaultConstructor )
mCtc.addConstructor(CtNewConstructor.defaultConstructor(mCtc));
if( mConstructors != null )
{
for( String code : mConstructors )
{
if( code.charAt(0) == ':' )
{
mCtc.addConstructor(CtNewConstructor.copy(getCtConstructor(mCopyConstructors.get(code.substring(1))), mCtc, null));
}
else
{
String[] sn = mCtc.getSimpleName().split("\\$+"); // inner class name include $.
mCtc.addConstructor(CtNewConstructor.make(code.replaceFirst(SIMPLE_NAME_TAG, sn[sn.length-1]), mCtc));
}
}
}
return mCtc.toClass(loader, pd);
依次从之前的处理类的配置(mpool)中得到相应实例,并按照javassist的规范在ctclass中依次配置超类,接口,字段,方法,构造方法,最后再调用toClass方法获得通过字节码生成的代理类。
我们再回头来看下javassistProxyFactory的getProxy方法。
public T getProxy(Invoker invoker, Class>[] interfaces) {
return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}
getProxy得到新的(字节码动态生成的)Proxy类的实例后,通过生成的Proxy覆盖的newInstance方法,得到新的proxyInstance(也是字节码动态生成的)实例。我们也可以从字节码角度看到,生成的代理,在消费者角度,看起来调用了消费者端的方法,实际上调用了代理的相应方法,最后直接调用handler的方法,达到代理目的。