或许我们已悉知Java的动态代理的方式:jdk——通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;cglib——通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理。dubbo在沿用java的jdk方式外,还采取了javassist方式——通过字节码生成代替反射。
从类图我们可以看出Dubbo代理工厂主要实现了JavassistProxyFactory和JdkProxyFactory。
我们先来看看ProxyFactory接口
@SPI("javassist")
public interface ProxyFactory {
/**
* create proxy.
*
* @param invoker
* @return proxy
*/
@Adaptive({Constants.PROXY_KEY})
T getProxy(Invoker invoker) throws RpcException;
/**
* create proxy.
*
* @param invoker
* @return proxy
*/
@Adaptive({Constants.PROXY_KEY})
T getProxy(Invoker invoker, boolean generic) throws RpcException;
/**
* create invoker.
*
* @param
* @param proxy
* @param type
* @param url
* @return invoker
*/
@Adaptive({Constants.PROXY_KEY})
Invoker getInvoker(T proxy, Class type, URL url) throws RpcException;
}
@SPI(“javassist”)注解表示Dubbo默认使用的代理模式,对应后面将会讲到的JavassistProxyFactory。
@Adaptive({Constants.PROXY_KEY})配合@SPI一起使用,其中{Constants.PROXY_KEY}匹配请求url中参数key值,在ExtensionLoader动态加载扩展进行相应匹配。其详细讲解可参考博文https://blog.csdn.net/matthew_zhang/article/details/68966051
ProxyFactory接口有三个方法声明:
GenericService demoService = (GenericService) context.getBean("demoService");
Object result = demoService.$invoke("getPermissions", new String[] { "java.lang.Long" }, new Object[]{ 1L });
继续看AbstractProxyFactory抽象类
public abstract class AbstractProxyFactory implements ProxyFactory {
@Override
public T getProxy(Invoker invoker) throws RpcException {
return getProxy(invoker, false);
}
@Override
public T getProxy(Invoker invoker, boolean generic) throws RpcException {
Class>[] interfaces = null;
String config = invoker.getUrl().getParameter(Constants.INTERFACES);
if (config != null && config.length() > 0) {
String[] types = Constants.COMMA_SPLIT_PATTERN.split(config);
if (types != null && types.length > 0) {
interfaces = new Class>[types.length + 2];
interfaces[0] = invoker.getInterface();
interfaces[1] = EchoService.class;
for (int i = 0; i < types.length; i++) {
// TODO can we load successfully for a different classloader?.
interfaces[i + 2] = ReflectUtils.forName(types[i]);
}
}
}
if (interfaces == null) {
interfaces = new Class>[]{invoker.getInterface(), EchoService.class};
}
if (!GenericService.class.isAssignableFrom(invoker.getInterface()) && generic) {
int len = interfaces.length;
Class>[] temp = interfaces;
interfaces = new Class>[len + 1];
System.arraycopy(temp, 0, interfaces, 0, len);
interfaces[len] = com.alibaba.dubbo.rpc.service.GenericService.class;
}
return getProxy(invoker, interfaces);
}
public abstract T getProxy(Invoker invoker, Class>[] types);
}
AbstractProxyFactory初步实现了getProxy(Invoker invoker)和getProxy(Invoker invoker, boolean generic)接口(实际相当于一个接口),主要获取了调用请求中的接口信息interfaces,然后定义了一个抽象方法getProxy(Invoker invoker, Class>[] types),让具体的类去实现代理对象的获取。
下面就是具体的代理实现类JdkProxyFactory
public class JdkProxyFactory extends AbstractProxyFactory {
@Override
@SuppressWarnings("unchecked")
public T getProxy(Invoker invoker, Class>[] interfaces) {
return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), interfaces, new InvokerInvocationHandler(invoker));
}
@Override
public Invoker getInvoker(T proxy, Class type, URL url) {
return new AbstractProxyInvoker(proxy, type, url) {
@Override
protected Object doInvoke(T proxy, String methodName,
Class>[] parameterTypes,
Object[] arguments) throws Throwable {
Method method = proxy.getClass().getMethod(methodName, parameterTypes);
return method.invoke(proxy, arguments);
}
};
}
}
方式和jdk动态代理方式一致,这里不再赘述(可参考博文)。
接下来就是本文的重点了,看看JavassistProxyFactory如何实现的。
public class JavassistProxyFactory extends AbstractProxyFactory {
@Override
@SuppressWarnings("unchecked")
public T getProxy(Invoker invoker, Class>[] interfaces) {
return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}
@Override
public Invoker getInvoker(T proxy, Class type, URL url) {
// TODO Wrapper cannot handle this scenario correctly: the classname contains '$'
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时,它采用了com.alibaba.dubbo.common.bytecode.Proxy 生成代理对象的工具类,步骤如下:
for (int i = 0; i < ics.length; i++) {
String itf = ics[i].getName();
if (!ics[i].isInterface())
throw new RuntimeException(itf + " is not a interface.");
Class> 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(';');
}
// 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);
}
long id = PROXY_CLASS_COUNTER.getAndIncrement();
String pkg = null;
ClassGenerator ccp = null, ccm = null;
try {
ccp = ClassGenerator.newInstance(cl);
Set worked = new HashSet();
List methods = new ArrayList();
for (int i = 0; i < ics.length; i++) {
if (!Modifier.isPublic(ics[i].getModifiers())) {
String npkg = ics[i].getPackage().getName();
if (pkg == null) {
pkg = npkg;
} else {
if (!pkg.equals(npkg))
throw new IllegalArgumentException("non-public interfaces from different packages");
}
}
ccp.addInterface(ics[i]);
for (Method method : ics[i].getMethods()) {
String desc = ReflectUtils.getDesc(method);
if (worked.contains(desc))
continue;
worked.add(desc);
int ix = methods.size();
Class> rt = method.getReturnType();
Class>[] pts = method.getParameterTypes();
StringBuilder code = new StringBuilder("Object[] args = new Object[").append(pts.length).append("];");
for (int j = 0; j < pts.length; j++)
code.append(" args[").append(j).append("] = ($w)$").append(j + 1).append(";");
code.append(" Object ret = handler.invoke(this, methods[" + ix + "], args);");
if (!Void.TYPE.equals(rt))
code.append(" return ").append(asArgument(rt, "ret")).append(";");
methods.add(method);
ccp.addMethod(method.getName(), method.getModifiers(), rt, pts, method.getExceptionTypes(), code.toString());
}
}
// 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代理类的构造已完成。接下来我们在看看getInvoker(T proxy, Class type, URL url) 方法。
public Invoker getInvoker(T proxy, Class type, URL url) {
// TODO Wrapper cannot handle this scenario correctly: the classname contains '$'
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);
}
};
}
此方法采用匿名内部类的模式创建调用者类new AbstractProxyInvoker(proxy, type, url),并在其内部doInvoke方法中实现代理类的实际调用。
这篇文章是博主对dubbo动态代理的源码的初步解读,有不当之处还望各位大佬多多指正。
关于dubbo动态代理选型方面的问题,可参考这一篇博文动态代理方案性能对比,评论区不容错过。。。