启动dubbo的demo服务,我们会看到一行日志
The service ready on spring started. service: org.apache.dubbo.demo.DemoService
复制代码
我们利用idea的搜索功能,找到它的出处。
public void onApplicationEvent(ContextRefreshedEvent event) {
if (isDelay() && !isExported() && !isUnexported()) {
if (logger.isInfoEnabled()) {
logger.info("The service ready on spring started. service: " + getInterface());
}
export();
}
}
复制代码
看到onApplicationEvent
方法,我们要非常敏感的想到Spring
。那好,我们看下该类的继承关系。
可以看到
ServiceBean
扩展来一系列
Spring
的扩展点。这里在容器刷新的时候就会开始暴露服务,这是我们的入口。
经过一些的检查,判断到了
首先拿到
registryURLs
,然后分不同的协议暴露服务。
loadRegistries
方法根据xml的配置拼装成一个URL。过程如下
protected List<URL> loadRegistries(boolean provider) {
List<URL> registryList = new ArrayList<URL>();
if (registries != null && !registries.isEmpty()) {
for (RegistryConfig config : registries) {
String address = config.getAddress();
if (address == null || address.length() == 0) {
address = Constants.ANYHOST_VALUE;
}
String sysaddress = System.getProperty("dubbo.registry.address");
if (sysaddress != null && sysaddress.length() > 0) {
address = sysaddress;
}
if (address.length() > 0 && !RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(address)) {
Map<String, String> map = new HashMap<String, String>();
appendParameters(map, application);
appendParameters(map, config);
map.put("path", RegistryService.class.getName());
map.put("dubbo", Version.getProtocolVersion());
map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
if (ConfigUtils.getPid() > 0) {
map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));
}
if (!map.containsKey("protocol")) {
if (ExtensionLoader.getExtensionLoader(RegistryFactory.class).hasExtension("remote")) {
map.put("protocol", "remote");
} else {
map.put("protocol", "dubbo");
}
}
List<URL> urls = UrlUtils.parseURLs(address, map);
for (URL url : urls) {
url = url.addParameter(Constants.REGISTRY_KEY, url.getProtocol());
url = url.setProtocol(Constants.REGISTRY_PROTOCOL);
if ((provider && url.getParameter(Constants.REGISTER_KEY, true))
|| (!provider && url.getParameter(Constants.SUBSCRIBE_KEY, true))) {
registryList.add(url);
}
}
}
}
}
return registryList;
}
复制代码
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
//..根据protocolConfig,registryURLs.组装url对象
protocolConfig:
<dubbo:protocol name="dubbo" port="20880" id="dubbo" />
registryURLs:
registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&pid=72954&qos.port=22222®istry=zookeeper×tamp=1537430745031
url对象:
dubbo://0.0.0.1:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=0.0.0.1&bind.port=20880&dubbo=2.0.2&generic=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello&pid=72954&qos.port=22222&side=provider×tamp=1537432276167
if (!Constants.SCOPE_NONE.equalsIgnoreCase(scope)) {
if (!Constants.SCOPE_REMOTE.equalsIgnoreCase(scope)) {
exportLocal(url);
}
if (!Constants.SCOPE_LOCAL.equalsIgnoreCase(scope)) {
//...远程暴露
}
}
this.urls.add(url);
}
复制代码
我们进入exportLocal
方法。
private void exportLocal(URL url) {
//url
dubbo://0.0.0.1:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=0.0.0.1&bind.port=20880&dubbo=2.0.2&generic=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello&pid=72954&qos.port=22222&side=provider×tamp=1537432276167
if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
URL local = URL.valueOf(url.toFullString())
.setProtocol(Constants.LOCAL_PROTOCOL)
.setHost(LOCALHOST)
.setPort(0);
// local:
injvm://127.0.0.1/org.apache.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=192.168.21.178&bind.port=20880&dubbo=2.0.2&generic=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello&pid=72954&qos.port=22222&side=provider×tamp=1537432276167
ServiceClassHolder.getInstance().pushServiceClass(getServiceClass(ref));
//proxyFactory是动态生成的. ProxyFactory$Adaptive
Exporter<?> exporter = protocol.export(
proxyFactory.getInvoker(ref, (Class) interfaceClass, local));
exporters.add(exporter);
logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry");
}
}
复制代码
我们进入getInvoker
(ProxyFactory$Adaptive)方法
这里使用的是javassist的实现方式。
这里重点来了。
首先对实现类进行包装,返回一个wrapper类。(思想类似Spring的BeanWrapper)getWrapper
中调用makeWrapper
。利用javasssit重新生成一个.class文件。 我们看下它的生成过程。
private static Wrapper makeWrapper(Class<?> c) {
StringBuilder c1 = new StringBuilder("public void setPropertyValue(Object o, String n, Object v){ ");
StringBuilder c2 = new StringBuilder("public Object getPropertyValue(Object o, String n){ ");
StringBuilder c3 = new StringBuilder("public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws " + InvocationTargetException.class.getName() + "{ ");
c1.append(name).append(" w; try{ w = ((").append(name).append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");
c2.append(name).append(" w; try{ w = ((").append(name).append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");
c3.append(name).append(" w; try{ w = ((").append(name).append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");
c1.append(" throw new " + NoSuchPropertyException.class.getName() + "(\"Not found property \\\"\"+$2+\"\\\" filed or setter method in class " + c.getName() + ".\"); }");
c2.append(" throw new " + NoSuchPropertyException.class.getName() + "(\"Not found property \\\"\"+$2+\"\\\" filed or setter method in class " + c.getName() + ".\"); }");
// make class
long id = WRAPPER_CLASS_COUNTER.getAndIncrement();
ClassGenerator cc = ClassGenerator.newInstance(cl);
cc.setClassName((Modifier.isPublic(c.getModifiers()) ? Wrapper.class.getName() : c.getName() + "$sw") + id);
cc.setSuperClass(Wrapper.class);
cc.addDefaultConstructor();
cc.addField("public static String[] pns;"); // property name array.
cc.addField("public static " + Map.class.getName() + " pts;"); // property type map.
cc.addField("public static String[] mns;"); // all method name array.
cc.addField("public static String[] dmns;"); // declared method name array.
for (int i = 0, len = ms.size(); i < len; i++)
cc.addField("public static Class[] mts" + i + ";");
cc.addMethod("public String[] getPropertyNames(){ return pns; }");
cc.addMethod("public boolean hasProperty(String n){ return pts.containsKey($1); }");
cc.addMethod("public Class getPropertyType(String n){ return (Class)pts.get($1); }");
cc.addMethod("public String[] getMethodNames(){ return mns; }");
cc.addMethod("public String[] getDeclaredMethodNames(){ return dmns; }");
//省略代码
}
复制代码
我们看下最终生成的类。
public class Wrapper1 extends Wrapper {
//省略代码
public Object invokeMethod(Object o, String n, Class[] p, Object[] v) {
com.alibaba.dubbo.demo.provider.DemoServiceImpl w;
try{
w = ((com.alibaba.dubbo.demo.provider.DemoServiceImpl)o);
}catch(Throwable e){
}
try{
if( "sayHello".equals( n ) && p.length == 1 ) {
return w.sayHello((java.lang.String)v[0]);
}
} catch(Throwable e) {
}
}
}
复制代码
可以看到这里是通过直接调用的方式来调用目标方法,相比于JdkProxyFactory的反射调用,直接调用能带来更大的性能提升,因此JavassistProxyFactory也是dubbo的默认配置。
我们看下
public AbstractProxyInvoker(T proxy, Class<T> type, URL url) {
this.proxy = proxy;//DemoServiceImpl
this.type = type;//DemoService
this.url = url;
}
复制代码
这里会返回一个invoke对象。