(个人理解,如果有误,望请指正,谢谢)
看过一些分析dubbo源码的文章,一般都是按模块进行分析的,分析common下面的包,然后是注册中心,过滤器等等,或许我能力有限,感觉零零碎碎的,最后也没有串联起来。所以用我自己的方式去分析代码。我这里看的代码是2.5.x分支的,2.7.x的代码已经略有不同。
其实看代码就像去发现了一个溶洞,需要探险一样,探险出发前需要确定从哪里开始,需要带上装备。一般,先带点基本工具,初步探查一些这个洞,然后再二次,三次,装备更加齐全,更深入的去探查。这探查dubbo,了解自定义标签就像确定从哪里开始,SPI就像带了个基本的装备(照明灯),准备好了后,我们要开始初探dubbo源码。
ServiceBean是在dubbo-Spring-config下的,是解析了dubbo:service这个标签的配置类,其实它也是服务提供者初始类,通过它来发起服务的暴露,服务的注册等。接下来,我们就来看下这个类
这个是ServiceBean的类继承关系图,不难看出他实现了initializingBean,ApplicationContextAware,BeanNameAware,ApplicationListener,DisposableBean这个几个都是spring里的接口,那实现这些接口有什么用呢?我们逐个来看一下:
1.InitializingBean方法中只有一个方法afterPropertiesSet,当bean初始化完成后,会执行这个方法
2.ApplicationContextAware 通过它Spring容器会自动把上下文环境对象调用ApplicationContextAware接口中的setApplicationContext方法。
3.BeanNameAware接口需要实现setBeanName()方法,这个方法只是简单的返回我们当前的beanName
4.DisposableBean就是在一个bean被销毁的时候,spring容器会帮你自动执行这个方法
5.ApplicationListener是一个监听接口,可以监听对应的事件
从这里看出,这几个接收实现了以后,serviceBean被Spring实例化了以后,会自动执行这些接口的方法,所以service的一些初始化操作,一定是在这几个接口方法中,接下来,我们来看下这几个接口方法的实现。
按触发的顺序,我们依次来看,首先触发的应该是BeanNamAware,这个接口比较简单只是返回备案name,所以不进行展开。直接从ApplicationContextAware的setApplicationContext开始
1.在类实例化的时候,就会把applicationContext设置进去,所以它会先执行,来看下setApplicationContext方法:
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
SpringExtensionFactory.addApplicationContext(applicationContext);
supportedApplicationListener = addApplicationListener(applicationContext, this);
}
可以看到除了设置spring的容器上下文之外,调用SpringExtensionFacotry进行容器上下文的添加,然后将当前类添加到事件监听当中,supportedApplicationListener是boolean指,返回值,来判断当前是否支持事件监听。
我们来看下addApplicationListener方法
public static boolean addApplicationListener(ApplicationContext applicationContext, ApplicationListener listener) {
try {
// backward compatibility to spring 2.0.1
Method method = applicationContext.getClass().getMethod("addApplicationListener", ApplicationListener.class);
method.invoke(applicationContext, listener);
return true;
} catch (Throwable t) {
if (applicationContext instanceof AbstractApplicationContext) {
try {
// backward compatibility to spring 2.0.1
Method method = AbstractApplicationContext.class.getDeclaredMethod("addListener", ApplicationListener.class);
if (!method.isAccessible()) {
method.setAccessible(true);
}
method.invoke(applicationContext, listener);
return true;
} catch (Throwable t2) {
// ignore
}
}
}
return false;
}
刚开始看到这个方法内容还是有点费解的(抱歉英文不是太好),spring的监听类只要继承ApplicationContextListener,然后在spring上下文中注册实例,spring会自动添加到监听队列里的,但是这里为什么要用反射来设置呢?直到看到:backward compatibility to spring 2.0.1,是为了兼容spring的2.0.1版本。这样就不难理解了。到这里,可以知道,setApplicationContext的功能设置了上下文到ServiceBean中,然后兼容了spring老版本的事件监听功能。返回值,确定系统是否支持事件监听机制(这里和后面的延迟暴露相关)
2.看看InitializingBean的afterPropertiesSet,这个方法是在Bean所有属性都被注入后触发的。
public void afterPropertiesSet() throws Exception {
if (getProvider() == null) {
Map<String, ProviderConfig> providerConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProviderConfig.class, false, false);
if (providerConfigMap != null && providerConfigMap.size() > 0) {
Map<String, ProtocolConfig> protocolConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProtocolConfig.class, false, false);
if ((protocolConfigMap == null || protocolConfigMap.size() == 0)
&& providerConfigMap.size() > 1) { // backward compatibility
List<ProviderConfig> providerConfigs = new ArrayList<ProviderConfig>();
for (ProviderConfig config : providerConfigMap.values()) {
if (config.isDefault() != null && config.isDefault().booleanValue()) {
providerConfigs.add(config);
}
}
if (providerConfigs.size() > 0) {
setProviders(providerConfigs);
}
} else {
ProviderConfig providerConfig = null;
for (ProviderConfig config : providerConfigMap.values()) {
if (config.isDefault() == null || config.isDefault().booleanValue()) {
if (providerConfig != null) {
throw new IllegalStateException("Duplicate provider configs: " + providerConfig + " and " + config);
}
providerConfig = config;
}
}
if (providerConfig != null) {
setProvider(providerConfig);
}
}
}
}
if (getApplication() == null
&& (getProvider() == null || getProvider().getApplication() == null)) {
Map<String, ApplicationConfig> applicationConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ApplicationConfig.class, false, false);
if (applicationConfigMap != null && applicationConfigMap.size() > 0) {
ApplicationConfig applicationConfig = null;
for (ApplicationConfig config : applicationConfigMap.values()) {
if (config.isDefault() == null || config.isDefault().booleanValue()) {
if (applicationConfig != null) {
throw new IllegalStateException("Duplicate application configs: " + applicationConfig + " and " + config);
}
applicationConfig = config;
}
}
if (applicationConfig != null) {
setApplication(applicationConfig);
}
}
}
if (getModule() == null
&& (getProvider() == null || getProvider().getModule() == null)) {
Map<String, ModuleConfig> moduleConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ModuleConfig.class, false, false);
if (moduleConfigMap != null && moduleConfigMap.size() > 0) {
ModuleConfig moduleConfig = null;
for (ModuleConfig config : moduleConfigMap.values()) {
if (config.isDefault() == null || config.isDefault().booleanValue()) {
if (moduleConfig != null) {
throw new IllegalStateException("Duplicate module configs: " + moduleConfig + " and " + config);
}
moduleConfig = config;
}
}
if (moduleConfig != null) {
setModule(moduleConfig);
}
}
}
if ((getRegistries() == null || getRegistries().size() == 0)
&& (getProvider() == null || getProvider().getRegistries() == null || getProvider().getRegistries().size() == 0)
&& (getApplication() == null || getApplication().getRegistries() == null || getApplication().getRegistries().size() == 0)) {
Map<String, RegistryConfig> registryConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, RegistryConfig.class, false, false);
if (registryConfigMap != null && registryConfigMap.size() > 0) {
List<RegistryConfig> registryConfigs = new ArrayList<RegistryConfig>();
for (RegistryConfig config : registryConfigMap.values()) {
if (config.isDefault() == null || config.isDefault().booleanValue()) {
registryConfigs.add(config);
}
}
if (registryConfigs != null && registryConfigs.size() > 0) {
super.setRegistries(registryConfigs);
}
}
}
if (getMonitor() == null
&& (getProvider() == null || getProvider().getMonitor() == null)
&& (getApplication() == null || getApplication().getMonitor() == null)) {
Map<String, MonitorConfig> monitorConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, MonitorConfig.class, false, false);
if (monitorConfigMap != null && monitorConfigMap.size() > 0) {
MonitorConfig monitorConfig = null;
for (MonitorConfig config : monitorConfigMap.values()) {
if (config.isDefault() == null || config.isDefault().booleanValue()) {
if (monitorConfig != null) {
throw new IllegalStateException("Duplicate monitor configs: " + monitorConfig + " and " + config);
}
monitorConfig = config;
}
}
if (monitorConfig != null) {
setMonitor(monitorConfig);
}
}
}
if ((getProtocols() == null || getProtocols().size() == 0)
&& (getProvider() == null || getProvider().getProtocols() == null || getProvider().getProtocols().size() == 0)) {
Map<String, ProtocolConfig> protocolConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProtocolConfig.class, false, false);
if (protocolConfigMap != null && protocolConfigMap.size() > 0) {
List<ProtocolConfig> protocolConfigs = new ArrayList<ProtocolConfig>();
for (ProtocolConfig config : protocolConfigMap.values()) {
if (config.isDefault() == null || config.isDefault().booleanValue()) {
protocolConfigs.add(config);
}
}
if (protocolConfigs != null && protocolConfigs.size() > 0) {
super.setProtocols(protocolConfigs);
}
}
}
if (getPath() == null || getPath().length() == 0) {
if (beanName != null && beanName.length() > 0
&& getInterface() != null && getInterface().length() > 0
&& beanName.startsWith(getInterface())) {
setPath(beanName);
}
}
if (!isDelay()) {
export();
}
}
这个方法有点长,我们一步步来看下这个方法的实现。
首先,Service去获取ProviderConfig,(这个有在自定义标签里例子里有说明过serviceBean会设置一个Provider的,如果dubbo:service标签是设置在dubbo:provider下的,那ServiceBeangetProvider的话就会是父元素生成的ProviderConfig.如果不是,就会调用全局的Provider)。如果getProvider中没有值,就从spring上下文中获取全局的ProviderConfig.Map<String, ProviderConfig> providerConfigMap,获取到的是一个Map值,当ProviderConfigMap不为空的情况下,获取ProtocolConfig从Spring上下文中,如果protocolMap为空,providerConfigMap 多于1个,就会将多个默认的provider放到providers中,针对这一点有点不理解,看到有备注是 backward compatibility,所以这里应该是兼容老版本的,(老版本不是太了解,这里就不展开讲了)。接下来,剩下情况,是获取providermap中的默认Provider设置到当前的ServiceBean中。
接下来判断Application是否为空,以及provider中的application是否为空,如果为空,从spring上下文中获取默认Application进行设置。后面的module也是类似处理。
判断当前的注册中心的Ids,registryIds是否为空,是,取application里的,或provider里的registryIds,默认是用provider里的registryIds不为空的情况,进行覆盖。
再接下来是判断注册中了registrys(可以多个注册中心),首先在当前类里查看,如果为空依次从provider和Applicatoin中查看(由这里可以看出,同一个值,在不同配置里的优先级,ServiceBean > ProviderConfig > ApplicationConfig).如果都为空,那从spring中全局的registry,得到的是一个map,如果registryIds,不为空,则遍历map,如果key刚好在registryIds中,则添加到当前注册中心。如果registryIds为空,则将所有的注册中心都添加到ServiceBean的registrys属性中。
接下获取报表元数据配置,如果当前属性获取不到,直接从spring中获取MetadataReportConfig的map,然后赋值给对应属性,configCenter的处理方式相似。
获取监控中心,monitor,serviceBean -->provider --> application中逐级获取,如果还是为空,获取spring中全局的map,从中获取默认的monitor,设置到当前属性中
获取metrics,标准度量库,类似
再就是获取protocol,获取方法和获取registry基本一致,不展开说明。
最后是设置Path,如果为空设置成BeanName.
设置完配置以后,执行的代码就是:
if (!isDelay()) {
export();
}
这里先去判断是不是延迟暴露,如果是,则先不进行服务暴露,否,则直接在这里暴露。来看下isDelay是怎么判断是不是延迟暴露服务的:
private boolean isDelay() {
Integer delay = getDelay();
ProviderConfig provider = getProvider();
if (delay == null && provider != null) {
delay = provider.getDelay();
}
return supportedApplicationListener && (delay == null || delay == -1);
}
首先获取delay属性,如果没有,再从provider中获取。一般我都不会去设置delay,所以这个值是null,那再看下supportedApplicationListener,由于setApplicationContext先于afterPropertiesSet执行,而一般使用的spring版本都支持监听机制,所以supportedApplicationListener为ture,返回值一般为true,所以这里export一般不会被执行。
那什么时候会去执行export呢?
3.接着来看下事件的监听applicatonContextListener,可以看到它监听的事件是ContextRefreshedEvent,这是个什么事件呢?我们都知道,spring实现IOC是从一个refresh()方法开始的,所以这个事件就是refresh()执行完的时候,会触发,就是spring完成所有类的注入,容器完成初始化的时候。看下代码:
if (isDelay() && !isExported() && !isUnexported()) {
if (logger.isInfoEnabled()) {
logger.info("The service ready on spring started. service: " + getInterface());
}
export();
}
由于isDelay不做设置的情况下都是true(既延迟加载)而第一次服务暴露的话,后面两个值一般也都未false,所以export()将会被执行。(这里为什么要等到applicationContext初始化完成在执行呢?我想防止如果在applicationContext初始化中执行,有可能依赖的类没有初始化,会报错,export()执行失败)。
接下来就是重头戏了,服务暴露的所有逻辑都在export这一个类中:
public synchronized void export() {
if (provider != null) {
if (export == null) {
export = provider.getExport();
}
if (delay == null) {
delay = provider.getDelay();
}
}
if (export != null && !export) {
return;
}
if (delay != null && delay > 0) {
delayExportExecutor.schedule(new Runnable() {
public void run() {
doExport();
}
}, delay, TimeUnit.MILLISECONDS);
} else {
doExport();
}
}
有没有发现这个方法上加了synchronized,对方法加了锁的,为什么?难道容器会有多次初始化?其实不然,是有多个容器的缘故,多个容器,初始化结束都会发出ContextRefreshEvent,同时集成了spring和springMVC的话,上下文中会存在父、子容器,
(spring配置文件的contextConfigLocation的父容器和springMVC配置文件的contextConfigLocation的子容器), 在通过applicationContext发送通知的时候,事件可能会被两个容器同时发布。所以这里是防止多次暴露。接下来,我们来分析下代码吧,
首先如果provider不为空的情况下,判断provider中的标记,是否已暴露过(export)和是否延迟暴露(delay)。如果export为true,表示已经暴露过服务了,直接返回,不在暴露服务。如果未暴露服务,判断是否延迟暴露,delay是具体一个数值,如果delay>0,会调用一个定时线程ScheduledExecutorService,在delay毫秒后开始暴露服务。否则直接暴露服务。doExport():
代码如下:
protected synchronized void doExport() {
if (unexported) {
throw new IllegalStateException("Already unexported!");
}
if (exported) {
return;
}
exported = true;
if (interfaceName == null || interfaceName.length() == 0) {
throw new IllegalStateException("<dubbo:service interface=\"\" /> interface not allow null!");
}
checkDefault();
if (provider != null) {
if (application == null) {
application = provider.getApplication();
}
if (module == null) {
module = provider.getModule();
}
if (registries == null) {
registries = provider.getRegistries();
}
if (monitor == null) {
monitor = provider.getMonitor();
}
if (protocols == null) {
protocols = provider.getProtocols();
}
}
if (module != null) {
if (registries == null) {
registries = module.getRegistries();
}
if (monitor == null) {
monitor = module.getMonitor();
}
}
if (application != null) {
if (registries == null) {
registries = application.getRegistries();
}
if (monitor == null) {
monitor = application.getMonitor();
}
}
if (ref instanceof GenericService) {
interfaceClass = GenericService.class;
if (StringUtils.isEmpty(generic)) {
generic = Boolean.TRUE.toString();
}
} else {
try {
interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
.getContextClassLoader());
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
checkInterfaceAndMethods(interfaceClass, methods);
checkRef();
generic = Boolean.FALSE.toString();
}
if (local != null) {
if ("true".equals(local)) {
local = interfaceName + "Local";
}
Class<?> localClass;
try {
localClass = ClassHelper.forNameWithThreadContextClassLoader(local);
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
if (!interfaceClass.isAssignableFrom(localClass)) {
throw new IllegalStateException("The local implementation class " + localClass.getName() + " not implement interface " + interfaceName);
}
}
if (stub != null) {
if ("true".equals(stub)) {
stub = interfaceName + "Stub";
}
Class<?> stubClass;
try {
stubClass = ClassHelper.forNameWithThreadContextClassLoader(stub);
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
if (!interfaceClass.isAssignableFrom(stubClass)) {
throw new IllegalStateException("The stub implementation class " + stubClass.getName() + " not implement interface " + interfaceName);
}
}
checkApplication();
checkRegistry();
checkProtocol();
appendProperties(this);
checkStubAndMock(interfaceClass);
if (path == null || path.length() == 0) {
path = interfaceName;
}
doExportUrls();
ProviderModel providerModel = new ProviderModel(getUniqueServiceName(), this, ref);
ApplicationModel.initProviderModel(getUniqueServiceName(), providerModel);
}
接下来分析下这个代码。
同样的这个方法上也加了锁,防止重复暴露服务,我们接下来一步步分析里面的代码:
首先判断是否已经暴露过,或者被设定为不暴露。如果验证通过,将export设置为true,这里export是voliate变量。接着校验interfaceName,接口名有没有,这个是必填的,没有就直接抛出异常。然后调用checkDefualt()校验默认值,跟到代码里面,可以看到如果当前的provider为空,就新建一个providerConfig,并调用appendProperties方法进行添加属性。appendProperties其实是从系统中获取属性值,放到对应config里的属性上。其会先拼接出前缀,这里获取provider,就会先拼出dubbo.provider. (如果有id,会在后面加上id值)然后根据providerConfig里的set方法,获取到属性名拼接上去,例如:dubbo.provider.xxxx,在用这个在System.getProperty()去获取,通过反射将值注入到Config中。
接下来已经有providerConfig了,然后校验各个参数,如果为空,就一级级往下寻找。再校验接口是不是泛型,如果是,就将泛型标记设置成true,如果否,先要获取interface的Class,然后校验。首先判断interface的Class是不是接口,在通过methodsConfig来判断interface的Class中,是否有methodConfig里的方法,没有就抛出异常。再校验Ref,(接口的实现类),判断ref是不是为空,为空,则没有实例,抛出异常,判断ref是不是实现了interface,没有则抛出异常。校验通过后,将泛型标记为false。
继续往下,判断是不是本地暴露,是interface后加上Local,(stub也是类似)。然后获取新interface+"Local"的Class,判断interface是不是其子类。
再往下,就开始校验各个组件,首先是Application,和provider类似,也是不为空就新建一个,拼接dubbo.application. 根据ApplicationConfi里的声明变量,从System.getProperty()来获取系统的属性值,再注入。校验registry的时候,略有不同,为了兼容早期版本,会直接从系统中获取dubbo.registry.address 的值,然后切分后生成多个registry(如果有多个地址的话)。再判断registry是否为空,如果为空,则没有找到注册中心,直接报错。不然的话,遍历每个registry,然后调用appendProperties 赋值。checkProtocol也是类似处理。最后调用appendProperties对当前的ServiceBean获取系统值进行覆盖。校验stub和mock.然后判断path是否为空,是,则设置成interfaceName.到这里,就将所以的准备工作做完了,接下来调用doExportUrls(),来看下实现的代码:
private void doExportUrls() {
List<URL> registryURLs = loadRegistries(true);
for (ProtocolConfig protocolConfig : protocols) {
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
}
}
这里是显示调用loadRegistries,来生成注册Urls,
protected List<URL> loadRegistries(boolean provider) {
checkRegistry();
List<URL> registryList = new ArrayList<URL>();
if (registries != null && registries.size() > 0) {
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 != null && 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.getVersion());
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;
}
因为URL在整个Dubbo服务暴露过程中很重要,所以,来仔细分析下这里的代码。同样的和前面一样,再次校验registry,然后判断当前ServiceBean里的registryConfig是否为空,不为空继续生成URL。遍历registries,判断每个address是否为空,并用System.getProperty(“dubbo.registry.address”);来覆盖address,这个时候如果address可用,就会来组装用于生成URL的参数map,调用appendParameters,将applicationConfig里的属性生成放到map里,看下map里都有哪些值:application,application.version,dump.directory,qos.enable,qos.port,owner,organization,architecture,environment,并且会去除parameters中的值放到map里,registryConfig,也是这样获取的,将path = com.alibaba.dubbo.registry.RegistryService放到map中,dubbo=版本号,timestamp=当前时间戳,pid=当前pid,如果当前map中没有protocol,则判断扩展类型中有没有remote,有的话将protocol设置成remote,没有的话就设置成dubbo。生成URL,根据address,map,生成dubbo://ip:port?key=val&key=val的这种形式。
回到doExportUrl中,遍历protocols,以protocol和registries,调用doExportUrlsFor1Protocol(protocolConfig, registryURLs);来暴露服务:
首先判断protocol.name是否为空,如果为空使用默认的dubbo。接下来从各个配置中获取属性值,放到map中。这里代码比较简单,不在展开。执行到String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();,这个语句,这里是用Wrapper生成包装类。看下代码:
public static Wrapper getWrapper(Class<?> c) {
while (ClassGenerator.isDynamicClass(c)) // can not wrapper on dynamic class.
c = c.getSuperclass();
if (c == Object.class)
return OBJECT_WRAPPER;
Wrapper ret = WRAPPER_MAP.get(c);
if (ret == null) {
ret = makeWrapper(c);
WRAPPER_MAP.put(c, ret);
}
return ret;
}
这里看到,不能包装动态类,接着从已包装好的Map中获取当前类,如果没有,则调用makeWrapper©类生成包装类,进入到里面,会发现,其实他也是用javassist进行字节码增强的。举个例子,我有一个类WrapperTest:
package com.tom;
public class WrapperTest{
private String f1;
public String f2;
public static String f3;
private Integer f4;
public String test1(String t1,String t2){
......
};
private String test2(String t3){
......
}
public Integer test3(String t1,Integer t2){
}
public void test3(Integer t1){
.....
}
get/set方法
}
根据makeWrapper的方法,看先生成的字符串应该是什么样子的:
public com.tom.WrapperTest$sw0 extend Wrapper{ //如果为私有类,Wrapper$sw0
public com.tom.WrapperTest$sw0(){}
public void setPropertyValue(Object o, String n, Object v){
w; try{ w = ((com.tom.WrapperTest)$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }
if( $2.equals(f1)) ){ w.f1 = (String)$3 return;}
if( $2.equals(f2)) ){ w.f1 = (String)$3 return;}
if( $2.equals(f4)) ){ w.f1 = (Integer)$3 return;}
throw new com.alibaba.dubbo.common.bytecode.NoSuchPropertyException("Not found property "+$2+" filed or setter method in class com.tom.WrapperTest.\");)
}
public Object getPropertyValue(Object o, String n){
w; try{ w = ((com.tom.WrapperTest)$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }
if( $2.equals(f1)) ){ return ($w)w.f1;}
if( $2.equals(f2)) ){ return ($w)w.f2;}
if( $2.equals(f4)) ){ return ($w)w.f4;}
if( $2.equals(getF1){ return ($w)w.getF1();}
if( $2.equals(setF1){ w.setF1((String)$3); return;}
if( $2.equals(getF2){ return ($w)w.getF2();}
if( $2.equals(setF2){ w.setF2((String)$3); return ;}
if( $2.equals(getF4){ return ($w)w.getF4();}
if( $2.equals(setF4){ w.setF4((Integer)$3); return;}
throw new com.alibaba.dubbo.common.bytecode.NoSuchPropertyException("Not found property "+$2+" filed or setter method in class com.tom.WrapperTest.\");)
}
public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws java.lang.reflect.InvocationTargetException{
w; try{ w = ((com.tom.WrapperTest)$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }
try{
//如果不是重载方法
if("test1".equals( $Misplaced &2 ) && $3.length == 2){
return w.test1((String)$4[0],(String)$4[1])
}
if("test2".equals( $Misplaced &2 ) && $3.length == 1){
return w.test2((String)$4[0]);
}
//如果是重载的方法
if("test3".equals( $Misplaced &2 ) && $3.length == 2 && $Misplaced &3[0].getName().equals("String") && $3[1].getName().equals("Integer")){
return w.test3(((Integer)$4[0]));
}
//返回为空
if("test3".equals( $Misplaced &2 ) && $3.length == 1 && $3[0].getName().equals("Integer")){
w.test3(((Integer)$4[0]));
return null;
}
} catch(Throwable e) {
throw new java.lang.reflect.InvocationTargetException(e);
}
throw new com.alibaba.dubbo.common.bytecode.NoSuchMethodException ("Not found method"\"+$2+\"\ in class com.tom.WrapperTest.\"); }"
}
public static String[] pns;
public static Map pts;
public static String[] mns;
public static String[] dmns;
public static Class[] mts0;
public static Class[] mts1;
public static Class[] mts2;
public static Class[] mts3;
public String[] getPropertyNames(){ return pns; }
public boolean hasProperty(String n){ return pts.containsKey($1); }
public Class getPropertyType(String n){ return (Class)pts.get($1); }
public String[] getMethodNames(){ return mns; }
public String[] getDeclaredMethodNames(){ return dmns; }
}
javassist中特殊 字符的含义:
$0, $1, $2, ... this and 方法的参数; $args 方法参数数组.它的类型为 Object[]; $$ 所有实参。例如, m($$) 等价于 m($1,$2,...); $cflow(...) cflow 变量; $r 返回结果的类型,用于强制类型转换; $w 包装器类型,用于强制类型转换 ;$sig 类型为 java.lang.Class 的参数类型数组; $type 一个 java.lang.Class 对象,表示返回值类型; $class 一个 java.lang.Class 对象,表示当前正在修改的类
到这里已经生成了当前需要暴露接口的包装类,它将会放到Wrapper的全局变量里,后面不需要重新构建。继续往下分析代码,通过包装类,已经可以获取所有的方法名了,如果方法名不为空,则map里key为methods,值为方法名,以“,”分割。然后是对token判断,这个比较简单。再看protocol,如果为:injvm表示本地暴露,将注册服务的标记为false,添加notify为false。接着从protocol中获取contextPath,如果为空,则从provider中获取。然后,继续是获取地址IP,和端口Port。配置的优先级:environment variables -> java system properties -> host property in config file ->etc/hosts -> default network address -> first available network address。端口的配置优先级是: environment variable -> java system properties -> port property in protocol config file -> protocol default port;获取到本地ip和端口后,用已经获取到的参数,就可以构建一个URL。然后用ExtendLoard可以来获取对应protocol下的配置类从而来重新设置URL,这个来看下代码:
if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
.hasExtension(url.getProtocol())) {
url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
.getExtension(url.getProtocol()).getConfigurator(url).configure(url);
}
我们前面已经看过ExtensionLoader了,实际当中,来找下这里的怎么获取的。getExtensionLoader(ConfigratorFactory.class)这个会查看有没有加载
@SPI
public interface ConfiguratorFactory {
@Adaptive("protocol")
Configurator getConfigurator(URL url);
}
从这可以看到,会生成动态代理,adptive是protocol的。接着我们来看下com.alibaba.dubbo.rpc.cluster.ConfiguratorFactory的文件有哪些类加载了,只找到
override=com.alibaba.dubbo.rpc.cluster.configurator.override.OverrideConfiguratorFactory
absent=com.alibaba.dubbo.rpc.cluster.configurator.absent.AbsentConfiguratorFactory
所以hasExtension(“dubbo”)的时候不成立,不执行了其中的代码。继续往下就要正在开始暴露服务了。下篇将会详细再分析export部分。