Javassist代理 Dubbo源码

代理在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的方法,达到代理目的。

你可能感兴趣的:(dubbo,源码学习)