天天看點

淘寶HSF通信架構,內建spring 啟動流程分析

     剛入職新公司,公司内部通信架構用的是淘寶的HSF,用法大概是在xml配置好一個接口,以及其他等待資訊之類的。。注入即可,我就沒看明白他這是咋回事。我猜應該是有注冊中心,從注冊中心拉取消費者執行個體然後去調用。

配置大概就是這麼用的:

<hsf:consumer id="accountCompanyService" inter
                  group="${spring.hsf.account.group}" version="${spring.hsf.version}" clientTimeout="5000000"/>
           

但是具體怎麼做的?怎麼加載到spring的bean工廠?怎麼就知道要調用那個服務?怎麼找到對方的IP:PORT的?

且由源碼分析一下:

首先這是個spring boot項目:把啟動類的代碼拷過來記錄一下,就明白了、

@MapperScan(basePackages="com.dajia.estatepayment.mapper")
@EnableScheduling
public class ProviderApplicationMain extends ApplicationMainParent {
    public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication(ProviderApplicationMain.class);
        springApplication.addListeners( new ApplicationReadyListener() );
        springApplication.addListeners( new ApplicationFailedListener() );
        springApplication.run(args);
    }
           

然後他繼承了一個父類:

@Configuration
@SpringBootApplication
@EnableAutoConfiguration(
    exclude = {MongoAutoConfiguration.class, MongoDataAutoConfiguration.class, DataSourceAutoConfiguration.class}
)
@ImportResource({"classpath*:spring/spring-*.xml"})
public abstract class ApplicationMainParent {
    public ApplicationMainParent() {
    }
}
           

差不多意思就是把spring-*.xml 外部配置都加載進來,

以及聲明了基礎掃描包,排除一些自動配置類,排除的這幾個我懷疑是重寫了,是以排除spirng預設的自動配置類

因為spring啟動所有bean的裝填都要經過

那我改從哪下手?

如果需要和Spring整合,肯定會重寫自己的初始化類,那麼去hsf提供的源碼中找找有關于spring的東西 , 我發現hsf和spring整合依賴幾個bean,在package com.taobao.hsf.app.spring.util包下有三各類

HSFSpringConsumerBean
HSFSpringProviderBean
HSFSpringRegistryBean      

看名字也知道了,那麼先從consumer,全局搜尋一下,看哪裡使用到了?

發現有兩個地方用到了

1.

public class HSFBeanDefinitionParser implements BeanDefinitionParser {
    private final Class clazz;

    public HSFBeanDefinitionParser(Class clazz) {
        this.clazz = clazz;
    }

    public BeanDefinition parse(Element element, ParserContext parserContext) {
        if (clazz == HSFSpringProviderBean.class) {
            return parseProvider(element, parserContext);
        } else if (clazz == HSFSpringConsumerBean.class) {
            return parseConsumer(element, parserContext);
        }  else if (clazz == HSFSpringRegistryBean.class) {
            return parseRegistry(element, parserContext);
        }else {
            throw new BeanDefinitionValidationException("Unknown class to definition " + clazz.getName());
        }
    }
           

2.

package com.taobao.hsf.app.spring.schema;

import com.taobao.hsf.app.spring.util.HSFSpringConsumerBean;
import com.taobao.hsf.app.spring.util.HSFSpringProviderBean;
import com.taobao.hsf.app.spring.util.HSFSpringRegistryBean;

import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

/**
 * @author xiaofei.wxf
 * @since 2015-02-03
 */
public class HSFNameSpaceHandler extends NamespaceHandlerSupport {
    public void init() {
    	registerBeanDefinitionParser("registry",new HSFBeanDefinitionParser(HSFSpringRegistryBean.class));
        registerBeanDefinitionParser("provider",new HSFBeanDefinitionParser(HSFSpringProviderBean.class));
        registerBeanDefinitionParser("consumer",new HSFBeanDefinitionParser(HSFSpringConsumerBean.class));
    }
}
           

好,那就打個斷點,啟動項目,注意hsf需要淘寶封裝的tomcat 啟動

2.1項目啟動,斷點走到了public class HSFNameSpaceHandler extends NamespaceHandlerSupport {

這個類繼承了NamespaceHandlerSupport 走入這個底層我猜就是解析XML并将其轉化為java的一個過程,不是我研究的重點,詳細我沒看

然後進去三個registerBeanDefinitionParser方法看看

static final Map<String, FieldDefenition> providerFieldDefMap = new HashMap<String, FieldDefenition>();

    static {
        providerFieldDefMap.put("interface", new FieldDefenition("serviceInterface", true, false));
        providerFieldDefMap.put("timeout", new FieldDefenition("clientTimeout", false, false));
        providerFieldDefMap.put("version", new FieldDefenition("serviceVersion", false, false));
        providerFieldDefMap.put("group", new FieldDefenition("serviceGroup", false, false));
        providerFieldDefMap.put("tenantID", new FieldDefenition("tenantID", false, false));
        providerFieldDefMap.put("envID", new FieldDefenition("envID", false, false));
        providerFieldDefMap.put("ref", new FieldDefenition(true));
        providerFieldDefMap.put("id", new FieldDefenition(true));
    }
           

他初始了一個map,fieldDef這個對象,他初始了定了provider的所有的屬性,以及是否必須填寫

一路走,發現到最後,這個方式的實作是這樣:

registerBeanDefinitionParser("registry",new HSFBeanDefinitionParser(HSFSpringRegistryBean.class));
實作:
protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
   this.parsers.put(elementName, parser);
}      

parsers 是一個map。。。

讓他全走完,這是啥事也沒做啊

然後就是provider,consumer,registry 的定義對象放入了parsers中.... 隻不過定義對象中多了幾個屬性map。。。

2.2

然後繼續下去就是還讀XML》。。

大概是讀到一個指定的xml就調用我們自己寫的parse方法。

重點來了,重點是來看我們的解析方法的實作

包名:package com.taobao.hsf.app.spring.schema

類名: HSFBeanDefinitionParser implements BeanDefinitionParser

先把解析實作貼上:

public BeanDefinition parse(Element element, ParserContext parserContext) {
        if (clazz == HSFSpringProviderBean.class) {
            return parseProvider(element, parserContext);
        } else if (clazz == HSFSpringConsumerBean.class) {
            return parseConsumer(element, parserContext);
        }  else if (clazz == HSFSpringRegistryBean.class) {
            return parseRegistry(element, parserContext);
        }else {
            throw new BeanDefinitionValidationException("Unknown class to definition " + clazz.getName());
        }
    }
           

三個if,判斷你屬于provider,consumer。,還是register。不同的bean傳回不同的實作體

我們是consumer,走進去看看

代碼先貼上,這一段看起來挺吓人,不過很簡單,都是if

public BeanDefinition parseConsumer(Element element, ParserContext parserContext) {
        RootBeanDefinition beanDef = new RootBeanDefinition();
        beanDef.setBeanClass(clazz);
        beanDef.setLazyInit(false);

        //props
        parseAttr(element, beanDef, consumerFieldDefMap);

        String callbackInvokerRef = element.getAttribute("callbackInvoker");
        if (!callbackInvokerRef.isEmpty()) {
            beanDef.getPropertyValues().addPropertyValue("callbackInvoker", new RuntimeBeanReference(callbackInvokerRef));
        }

        List<MethodSpecial> list = new ArrayList<MethodSpecial>();
        List<String> asyncallMethods = new ArrayList<String>();
        NodeList methodSpecials = element.getChildNodes();
        for (int i = 0; i < methodSpecials.getLength(); i++) {
            Node item = methodSpecials.item(i);
            if (item instanceof Element) {
                String localName = item.getLocalName();
                if (localName.equals("asyncallMethods")) {
                    //asyncallMethods
                    NodeList asyncallMethodsList = item.getChildNodes();
                    for (int j = 0; j < asyncallMethodsList.getLength(); j++) {
                        Node asyncallMethod = asyncallMethodsList.item(j);
                        if (!(asyncallMethod instanceof Element)) {
                            continue;
                        }
                        String name = ((Element) asyncallMethod).getAttribute("name");
                        String type = ((Element) asyncallMethod).getAttribute("type");
                        if (name.isEmpty() || type.isEmpty()) {
                            continue;
                        }
                        String listener = ((Element) asyncallMethod).getAttribute("listener");
                        StringBuilder sb = new StringBuilder();
                        sb.append("name:").append(name).append(";type:").append(type);
                        if (type.equals("callback")) {
                            sb.append(";listener:").append(listener);
                        }
                        asyncallMethods.add(sb.toString());
                    }
                } else if (localName.equals("methodSpecials")) {
                    //methodSpecials
                    NodeList methodSpecialList = item.getChildNodes();
                    for (int j = 0; j < methodSpecialList.getLength(); j++) {
                        Node methodSpecial = methodSpecialList.item(j);
                        if (methodSpecial instanceof Element) {
                            String timeout = ((Element) methodSpecial).getAttribute("timeout");
                            String name = ((Element) methodSpecial).getAttribute("name");
                            String retries = ((Element) methodSpecial).getAttribute("retries");
                            MethodSpecial ms = new MethodSpecial();
                            ms.setClientTimeout(Long.valueOf(timeout));
                            ms.setMethodName(name);
                            if (!retries.isEmpty()) {
                                ms.setRetries(Integer.valueOf(retries));
                            }
                            list.add(ms);
                        }
                    }
                }
            }
        }
        if (list.size() > 0) {
            beanDef.getPropertyValues().addPropertyValue("methodSpecials", list.toArray());
        }
        if (asyncallMethods.size() > 0) {
            beanDef.getPropertyValues().addPropertyValue("asyncallMethods", asyncallMethods);
        }

        String id = getId(parserContext, element.getAttribute("id"));
        BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDef, id);
        BeanDefinitionReaderUtils.registerBeanDefinition(holder, parserContext.getRegistry());
        return beanDef;
    }
           

分析一下這一段代碼:

RootBeanDefinition beanDef = new RootBeanDefinition();      

1.先建立一個bean定義對象

2.設定是否懶加載,bean的class

3.然後解析bean的屬性(最重要)

4.擷取回調的執行方法,如果不為空,就設定上

5.建立一個方法屬性的list

6.建立一個異步方法的集合

7.擷取子節點

8.若子節點不為空,進行子節點的一些操作,,,因為我們這子節點都是空的,是以沒看,這方法實作最大的一段跳過了

9.擷取bean的id

10.根據ID,定義對象 建立一個BeanDefinitionHolder

11.BeanDefinitionReaderUtils.registerBeanDefinition(holder, parserContext.getRegistry());

注冊bean進spring容器

傳回bean定義對象

結束...

細看3.和11.

3.解析屬性:

private void parseAttr(Element element, RootBeanDefinition beanDef, Map<String, FieldDefenition> fieldDefMap) {
        //props
        NamedNodeMap attrMap = element.getAttributes();
        for (int i = 0; i < attrMap.getLength(); i++) {
            Attr attr = (Attr) attrMap.item(i);
            String name = attr.getName();
            if(null == name || name.isEmpty()) {
                continue;
            }
            FieldDefenition fieldDefenition = fieldDefMap.get(name);
            if(null == fieldDefenition) {
                setProperty(beanDef, element, name, name, false);
            } else {
                if(fieldDefenition.isDiscard()) {
                    continue;
                }
                setProperty(beanDef, element, name, fieldDefenition.getPropName(), fieldDefenition.isRequred());
            }
        }
    }
           

其實很簡單,擷取該元素的所i有NameNode

namenode:是你在xml中配置的那些id啊,time啊  之類的屬性

如果屬性值的NameNode不是空,擷取到該屬性的值,如果為空,那麼從第一步覺得沒啥用的那個類的屬性定義Map中擷取到這個屬性定義對象,并且給他指派

指派的實作也比較簡單:

private void setProperty(RootBeanDefinition beanDef, Element element, String attrName, String propertyName, boolean required) {
        String attr = element.getAttribute(attrName);
        if (required) {
            checkAttr(attrName, attr);
        }
        if (attr != null && !attr.isEmpty()) {
            beanDef.getPropertyValues().addPropertyValue(propertyName, attr);
        }
    }
           

擷取到這個nameNode的值,檢查是否是必須的 如果是必須的那麼就檢查屬性,如果不是必須的那麼就直接設定,結束。

子節點之類的都是空,異步方法什麼的都是空的,如果不是  直接給這個bean定義對象指派相應屬性即可

然後就是建立BeanDefinitionHolder 也比較簡單,就是校驗一下你傳的ID,bean定義對象是否為空。

然後重點

11.将bean放入spring的容器中

該方法兩個參數,剛剛這個holder對象,還一個就是解析容器中擷取BeanDefinitionRegistry

到這裡就是spring的工作流程了,而不是我們自己定義的流程了;

注冊的源碼看一下:

public static void registerBeanDefinition(
			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException {

		// Register bean definition under primary name.
		String beanName = definitionHolder.getBeanName();
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

		// Register aliases for bean name, if any.
		String[] aliases = definitionHolder.getAliases();
		if (aliases != null) {
			for (String alias : aliases) {
				registry.registerAlias(beanName, alias);
			}
		}
	}
           

也很簡單:

注冊主名稱

如果有别名注冊别名

好,看下怎麼注冊的:”

registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

      

兩個參數:

beanName,bean定義對象

看源碼,挺長

@Override
	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {

		Assert.hasText(beanName, "Bean name must not be empty");
		Assert.notNull(beanDefinition, "BeanDefinition must not be null");

		if (beanDefinition instanceof AbstractBeanDefinition) {
			try {
				((AbstractBeanDefinition) beanDefinition).validate();
			}
			catch (BeanDefinitionValidationException ex) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Validation of bean definition failed", ex);
			}
		}

		BeanDefinition oldBeanDefinition;

		oldBeanDefinition = this.beanDefinitionMap.get(beanName);
		if (oldBeanDefinition != null) {
			if (!isAllowBeanDefinitionOverriding()) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
						"': There is already [" + oldBeanDefinition + "] bound.");
			}
			else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
				// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
				if (this.logger.isWarnEnabled()) {
					this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
							"' with a framework-generated bean definition: replacing [" +
							oldBeanDefinition + "] with [" + beanDefinition + "]");
				}
			}
			else if (!beanDefinition.equals(oldBeanDefinition)) {
				if (this.logger.isInfoEnabled()) {
					this.logger.info("Overriding bean definition for bean '" + beanName +
							"' with a different definition: replacing [" + oldBeanDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
			else {
				if (this.logger.isDebugEnabled()) {
					this.logger.debug("Overriding bean definition for bean '" + beanName +
							"' with an equivalent definition: replacing [" + oldBeanDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}
		else {
			if (hasBeanCreationStarted()) {
				// Cannot modify startup-time collection elements anymore (for stable iteration)
				synchronized (this.beanDefinitionMap) {
					this.beanDefinitionMap.put(beanName, beanDefinition);
					List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
					updatedDefinitions.addAll(this.beanDefinitionNames);
					updatedDefinitions.add(beanName);
					this.beanDefinitionNames = updatedDefinitions;
					if (this.manualSingletonNames.contains(beanName)) {
						Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
						updatedSingletons.remove(beanName);
						this.manualSingletonNames = updatedSingletons;
					}
				}
			}
			else {
				// Still in startup registration phase
				this.beanDefinitionMap.put(beanName, beanDefinition);
				this.beanDefinitionNames.add(beanName);
				this.manualSingletonNames.remove(beanName);
			}
			this.frozenBeanDefinitionNames = null;
		}

		if (oldBeanDefinition != null || containsSingleton(beanName)) {
			resetBeanDefinition(beanName);
		}
	}
           

1.顯示檢查參數是否合法

2.Validate this bean definition.

-----進入spring建立bean的流程

3.擷取舊的bean定義對象-不存在,如果存在,

   3.1bean是否可以被覆寫?抛異常

   3.2舊的bean和新的bean權重比較,列印日志。。

   3.3舊bean和新bean是否相等。列印日志

   3.4 放入bean的定義map

4.bean是否在建立?

    4.1建立bean

    4.2先把bean定義對象放入這個beanDefinitionMap

  5.後續讓spring執行個體化這些bean去吧......

  第一次寫,有點亂,并且也沒有下載下傳spring源碼到本地。注釋都沒法寫 還在下面一行一行的寫。