Motan系列-Motan如何完成與Spring的集成

🌹🌹若是您以爲個人文章對您有幫助的話,記得在GitHub上star一波哈🌹🌹php

🌹🌹GitHub_awesome-it-blog 🌹🌹java


Motan是新浪微博研發並開源的一個RPC框架,與Dubbo相比,他更輕量級一些,代碼也更少一些,但也五臟俱全。git

Motan在GitHub上的項目地址:github.com/weibocom/mo…github

關於Motan的使用,能夠看官方Wiki:github.com/weibocom/mo…spring

基於Xml和基於Annotation的使用方式這裏再也不贅述,下面主要關注他是如何解析的。api

0 如何掃描解析Xml文件

首先咱們要知道的是,Spring是如何識別、解析Xml文件中那一大堆標籤的。微信

這裏給出一個Xml的示例:網絡

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:motan="http://api.weibo.com/schema/motan" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://api.weibo.com/schema/motan http://api.weibo.com/schema/motan.xsd">

    <!-- reference to the remote service -->
    <motan:referer id="remoteService" interface="quickstart.FooService" directUrl="localhost:8002"/>
</beans>
複製代碼

Xml文件經過xmlns定義命名空間,好比這裏的 <motan:referer ... > 標籤就是經過 xmlns:motan="http://api.weibo.com/schema/motan" 來定義的。框架

那麼,寫了這些東西后,Spring如何識別motan標籤呢?ide

在Spring啓動時會自動掃描 classpath 下的 META-INF/spring.handlers 文件,在motan中,此文件在motan-core下:

motan-core/src/main/resources/META-INF/spring.handlers
複製代碼

此文件中定義了motan標籤的解析器:

http\://api.weibo.com/schema/motan=com.weibo.api.motan.config.springsupport.MotanNamespaceHandler
複製代碼

到這裏能夠發現,其實 xmlns:motan 的值,就是 spring.handlers 中配置的key,Spring就能夠經過這個能夠找到解析器 MotanNamespaceHandler

0.1 MotanNamespaceHandler

MotanNamespaceHandler在 motan-springsupport 工程下,它的實現以下:

public class MotanNamespaceHandler extends NamespaceHandlerSupport {
    public final static Set<String> protocolDefineNames = new ConcurrentHashSet<String>();
    public final static Set<String> registryDefineNames = new ConcurrentHashSet<String>();
    public final static Set<String> basicServiceConfigDefineNames = new ConcurrentHashSet<String>();
    public final static Set<String> basicRefererConfigDefineNames = new ConcurrentHashSet<String>();

    @Override
    public void init() {
        registerBeanDefinitionParser("referer", new MotanBeanDefinitionParser(RefererConfigBean.class, false));
        registerBeanDefinitionParser("service", new MotanBeanDefinitionParser(ServiceConfigBean.class, true));
        registerBeanDefinitionParser("protocol", new MotanBeanDefinitionParser(ProtocolConfig.class, true));
        registerBeanDefinitionParser("registry", new MotanBeanDefinitionParser(RegistryConfig.class, true));
        registerBeanDefinitionParser("basicService", new MotanBeanDefinitionParser(BasicServiceInterfaceConfig.class, true));
        registerBeanDefinitionParser("basicReferer", new MotanBeanDefinitionParser(BasicRefererInterfaceConfig.class, true));
        registerBeanDefinitionParser("spi", new MotanBeanDefinitionParser(SpiConfigBean.class, true));
        registerBeanDefinitionParser("annotation", new MotanBeanDefinitionParser(AnnotationBean.class, true));
        Initializable initialization = InitializationFactory.getInitialization();
        // 這裏用於初始化SPI擴展點,本文暫不介紹。
        initialization.init();
    }
}
複製代碼

它繼承於Spring中的 NamespaceHandlerSupport,並實現 init() 了方法,此方法用於初始化標籤解析器。

調用 NamespaceHandlerSupportregisterBeanDefinitionParser 方法,可完成解析器的註冊,motan統一用 MotanBeanDefinitionParser 做爲解析器,經過不一樣的參數來處理不一樣的標籤。 MotanBeanDefinitionParser 繼承於Spring的 BeanDefinitionParser,並實現了其 parse() 方法,這個方法就會在解析Xml文件時,將遇到的element解析並生成BeanDefinition,並將其註冊到上下文的 BeanDefinitionRegistry 中,最終完成Xml的解析。

1 如何掃描解析Annotation

有兩種方式開啓Annotation模式:

  • Xml中配置
  • @Bean配置

使用這兩種方式都會生成 AnnotationBean,並將其注入到Spring容器中。

1.1 Xml配置形式

此方式依然是經過 META-INF/spring.handlers 完成的。

因爲容器啓動須要掃描此文件,在上文描述中,指定的 MotanNamespaceHandler 中的 init() 方法,經過如下代碼提供了annotation這個element的解析:

@Override
public void init() {
    // ...
    registerBeanDefinitionParser("annotation", new MotanBeanDefinitionParser(AnnotationBean.class, true));
    // ... 
}
複製代碼

而後在Xml配置中配置:

<motan:annotation package="xxx.xxx.xx"/>
複製代碼

就能夠掃描指定package下面的類了。

1.2 @Bean形式

經過如下方式掃描package指定的類:

@Bean
public AnnotationBean motanAnnotationBean() {
    AnnotationBean motanAnnotationBean = new AnnotationBean();
    motanAnnotationBean.setPackage("xxx.xxx.xx");
    return motanAnnotationBean;
}
複製代碼

1.3 motan核心註解

motan有兩個核心註解:

  • com.weibo.api.motan.config.springsupport.annotation.MotanReferer
  • com.weibo.api.motan.config.springsupport.annotation.MotanService

其中,@MotanService 用於標識某個類是一個motan服務實現類,MotanReferer 用於標識服務調用方。

典型的使用方式以下:

  • 定義一個接口
public interface MotanDemoService {
    String hello(name);
}
複製代碼
  • 提供其實現類,並暴露服務
@MotanService(/* protocol、registry 等配置 */)
public class MotanDemoServiceImpl implements MotanDemoService {

    public String hello(String name) {
        System.out.println(name);
        return "Hello " + name + "!";
    }
}
複製代碼
  • 服務調用
@MotanReferer(/* 配置 */)
private MotanDemoService service;
複製代碼

1.4 核心註解的解析

AnnotationBean 注入到容器時,motan經過Spring提供的功能介入了Bean的生命週期,來完成註解的掃描。

來看一下 AnnotationBean 的定義:

public class AnnotationBean implements DisposableBean, BeanFactoryPostProcessor, BeanPostProcessor, BeanFactoryAware, Ordered {
    // ...
}
複製代碼

最關鍵的在於 BeanFactoryPostProcessorBeanPostProcessor。對他倆作個簡單介紹:

  • BeanFactoryPostProcessor:容許咱們在容器實例化相應對象以前,對註冊到容器的BeanDefinition所保存的信息作響應的修改。
  • BeanPostProcessor:容許在Spring Bean對象的初始化方法(init-method)的調用先後來接入初始化階段。

引用一個網絡上的圖來描述一下這兩個接口介入的時機:

Spring-Bean生命週期

按照順序,首先看一下 BeanFactoryPostProcessorpostProcessBeanFactory 都幹了啥。

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    if (annotationPackage == null || annotationPackage.length() == 0) {
        return;
    }
    if (beanFactory instanceof BeanDefinitionRegistry) {
        try {
            // 初始化類掃描器
            Class<?> scannerClass = ClassUtils.forName("org.springframework.context.annotation.ClassPathBeanDefinitionScanner",
                    AnnotationBean.class.getClassLoader());
            // 實例化類掃描器
            Object scanner = scannerClass.getConstructor(new Class<?>[]{BeanDefinitionRegistry.class, boolean.class})
                    .newInstance(new Object[]{(BeanDefinitionRegistry) beanFactory, true});
            // 初始化註解類型過濾器,在包掃描的過程當中,知足指定條件註解的class會被加載
            Class<?> filterClass = ClassUtils.forName("org.springframework.core.type.filter.AnnotationTypeFilter",
                    AnnotationBean.class.getClassLoader());
            // 設置要匹配的註解:@MotanService,即暴露motan服務的類
            Object filter = filterClass.getConstructor(Class.class).newInstance(MotanService.class);
            Method addIncludeFilter = scannerClass.getMethod("addIncludeFilter",
                    ClassUtils.forName("org.springframework.core.type.filter.TypeFilter", AnnotationBean.class.getClassLoader()));
            // 設置"包含"過濾器,只有包含上面指定的@MotanService註解時,才加載
            addIncludeFilter.invoke(scanner, filter);
            // 執行掃描
            Method scan = scannerClass.getMethod("scan", new Class<?>[]{String[].class});
            scan.invoke(scanner, new Object[]{annotationPackages});
        } catch (Throwable e) {
            // spring 2.0
        }
    }
}
複製代碼

變量 annotationPackage 即上面設置的要掃描的包。代碼大意已在註釋中給出。這段代碼的做用,就是掃描指定的包,並將符合條件的class做爲Spring候選Bean,並將其BeanDefinition註冊到給定的BeanFactory或ApplicationContext中。

而後來看下 BeanPostProcessor 的兩個方法幹了啥。

BeanPostProcessor 有兩個方法:postProcessBeforeInitializationpostProcessAfterInitialization

postProcessBeforeInitialization 用於 init-mothod 方法執行前介入,postProcessAfterInitialization 在其後介入。兩個方法的實現以下:

  • postProcessBeforeInitialization
/** * init reference field */
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    if (!isMatchPackage(bean)) {
        return bean;
    }
    Class<?> clazz = bean.getClass();
    if (isProxyBean(bean)) {
        clazz = AopUtils.getTargetClass(bean);
    }
    // ... 解析setter method上的@MotanReferer註解,省略 ...

    Field[] fields = clazz.getDeclaredFields();
    for (Field field : fields) {
        try {
            if (!field.isAccessible()) {
                field.setAccessible(true);
            }
            // 獲取field上的MotanReferer註解,若是獲取到了,經過refer方法解析他
            MotanReferer reference = field.getAnnotation(MotanReferer.class);
            if (reference != null) {
                Object value = refer(reference, field.getType());
                if (value != null) {
                    field.set(bean, value);
                }
            }
        } catch (Throwable t) {
            throw new BeanInitializationException("Failed to init remote service reference at filed " + field.getName()
                    + " in class " + bean.getClass().getName(), t);
        }
    }
    return bean;
}
複製代碼
  • postProcessAfterInitialization
/** * init service config and export servcice */
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    if (!isMatchPackage(bean)) {
        return bean;
    }
    Class<?> clazz = bean.getClass();
    if (isProxyBean(bean)) {
        clazz = AopUtils.getTargetClass(bean);
    }
    // 獲取class上的@MotanService註解,並解析
    MotanService service = clazz.getAnnotation(MotanService.class);
    if (service != null) {
        ServiceConfigBean<Object> serviceConfig = new ServiceConfigBean<Object>();
        // ... 初始化ServiceConfigBean,省略 ...
    }
    return bean;
}
複製代碼

postProcessBeforeInitialization 方法用於解析Bean中帶有@MotanReferer註解的setter方法或field,並完成調用方的初始化。

postProcessAfterInitialization 方法用於解析帶有@MotanService註解的class,並將這個class做爲motan服務註冊到註冊中心,暴露爲服務。

2 總結

motan也有原生的服務暴露形式,本文沒有介紹,具體能夠參考官方wiki。

motan主要利用Spring啓動時加載並初始化 META-INF/spring.handlers 來完成與Spring的集成。

在xml配置形式下,motan用 MotanNamespaceHandler 完成標籤解析器的註冊。

在Annotation配置形式下,motan主要利用 BeanFactoryPostProcessorBeanPostProcessor 介入Bean生命週期,用 BeanFactoryPostProcessor 實現了class的掃描,用 BeanPostProcessor 實現了兩個核心註解 @MotanReferer@MotanService 的解析。


歡迎關注個人微信公衆號

公衆號
相關文章
相關標籤/搜索