一步步解析Dubbo之ServiceBean(一)

(个人理解,如果有误,望请指正,谢谢)

看过一些分析dubbo源码的文章,一般都是按模块进行分析的,分析common下面的包,然后是注册中心,过滤器等等,或许我能力有限,感觉零零碎碎的,最后也没有串联起来。所以用我自己的方式去分析代码。我这里看的代码是2.5.x分支的,2.7.x的代码已经略有不同。

其实看代码就像去发现了一个溶洞,需要探险一样,探险出发前需要确定从哪里开始,需要带上装备。一般,先带点基本工具,初步探查一些这个洞,然后再二次,三次,装备更加齐全,更深入的去探查。这探查dubbo,了解自定义标签就像确定从哪里开始,SPI就像带了个基本的装备(照明灯),准备好了后,我们要开始初探dubbo源码。

ServiceBean是在dubbo-Spring-config下的,是解析了dubbo:service这个标签的配置类,其实它也是服务提供者初始类,通过它来发起服务的暴露,服务的注册等。接下来,我们就来看下这个类

在这里插入图片描述img

这个是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部分。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值