剛入職新公司,公司内部通信架構用的是淘寶的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源碼到本地。注釋都沒法寫 還在下面一行一行的寫。