Dubbo中@Service工作过程解析

Spring中的BeanPostProcessor

首先我们应当了解到在spring体系中BeanPostProcessor是什么、加载流程

它是什么

BeanPostProcessor也也称为后置处理器。在spring容加载流程。

spring容器bean加载流程
// Prepare this context for refreshing.
prepareRefresh(); // 获取beanFactory并加载容器中定义的bean信息
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory); try {
// 归类bean工厂的后置处理器
postProcessBeanFactory(beanFactory); // 处理这些bean工厂的后置处理器
invokeBeanFactoryPostProcessors(beanFactory); // 调用所有实现了beanpostprocessor接口的类。(先加载实现了priority接口的,然后加载order的,最后加载剩余的)
registerBeanPostProcessors(beanFactory); // Initialize message source for this context.
initMessageSource(); // Initialize event multicaster for this context.
initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses.
onRefresh(); // Check for listener beans and register them.
registerListeners(); // Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event.
finishRefresh();
}

后置处理器的加载和工作

所以由上可以看出,在registerBeanPostProcessors这一步的时候会划分容器中各种后置处理器,首先归类有@Priority注解,其次归类有@Order注解。最后划分其它的。

// 1
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors); // 2
sortPostProcessors(orderedPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, orderedPostProcessors); // 3
registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors); // Finally, re-register all internal BeanPostProcessors.
sortPostProcessors(internalPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, internalPostProcessors);

这样,就将所有的后置处理器注册到容器中。后续在容器启动的过程中,会通过反射的方式调用各个实现了BeanPostProcessor的实现类的beforexxxxafterxxx的方法来做进一步的处理。

有了以上基础,就可以去dubbo包中找到各自的xxxBeanPostProcessor了。

dubbo中的ServiceAnnotationBeanPostProcessor

类继承关系

作用

BeanDefinitionRegistryPostProcessor继承自BeanFactoryPostProcessor,是一种比较特殊的BeanFactoryPostProcessor。BeanDefinitionRegistryPostProcessor中定义的postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)方法 可以让我们实现自定义的注册bean定义的逻辑。

从以上论述可以看出实现BeanDefinitionRegistryPostProcessor的作用就是向spring容器中注册响应的bean实例。

具体分析

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// 读取配置中声明的dubbo扫描类
Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan); if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
// 将加有@Service注解的类注册到spring容器中
registerServiceBeans(resolvedPackagesToScan, registry);
} else {
if (logger.isWarnEnabled()) {
logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
}
} }

下面就分析registerServiceBeans的处理过程。

  1. 扫描系统配置的 basePackages,将@Service注解的类放到一个set集合中。
  2. 找到所有标注@Service的类是否被扫面到。
  3. registerServiceBean循环遍历这个集合,并将它们注入到spring容器中。

ServiceBean的作用

类图继承关系



主要看它实现了InitializingBean。通过实现它来对bean初始化之后做一定操作(调用afterPropertiesSet())。

代码实现

  1. ServiceBean初始化过程。如下所示:
@SuppressWarnings({"unchecked", "deprecation"})
public void afterPropertiesSet() throws Exception {
// 进行大量的操作,来读取对应的@Service组成相应的对象信息.....
.....
// 最后一步,导出服务。
export()
}
  1. export()导出服务

    最后得到一系列的URL信息(形如注册在zk上的provider节点信息)
export() -> doExportUrlsFor1Protocol()
  1. 获取暴露的host和端口
// 获取部署主机信息
String host = this.findConfigedHosts(protocolConfig, registryURLs, map);
// 获取配置的端口信息。(默认20880,)
Integer port = this.findConfigedPorts(protocolConfig, name, map);
  1. 导出有两步

    1. exportLocal
    2. exportJVM
  2. 根据第3步启动相关的服务

    @Override
    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    //
    URL url = invoker.getUrl(); // export service.
    String key = serviceKey(url);
    DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
    exporterMap.put(key, exporter); //export an stub service for dispatching event
    Boolean isStubSupportEvent = url.getParameter(STUB_EVENT_KEY, DEFAULT_STUB_EVENT);
    Boolean isCallbackservice = url.getParameter(IS_CALLBACK_SERVICE, false);
    if (isStubSupportEvent && !isCallbackservice) {
    String stubServiceMethods = url.getParameter(STUB_EVENT_METHODS_KEY);
    if (stubServiceMethods == null || stubServiceMethods.length() == 0) {
    if (logger.isWarnEnabled()) {
    logger.warn(new IllegalStateException("consumer [" + url.getParameter(INTERFACE_KEY) +
    "], has set stubproxy support event ,but no stub methods founded."));
    } } else {
    stubServiceMethodsMap.put(url.getServiceKey(), stubServiceMethods);
    }
    }
    // 根据url配置host、port信息启动服务(默认使用netty)
    openServer(url);
    optimizeSerialization(url); return exporter;
    }

    netty服务启动

     private ExchangeServer createServer(URL url) {
    url = URLBuilder.from(url)
    // send readonly event when server closes, it's enabled by default
    .addParameterIfAbsent(CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString())
    // enable heartbeat by default
    .addParameterIfAbsent(HEARTBEAT_KEY, String.valueOf(DEFAULT_HEARTBEAT))
    .addParameter(CODEC_KEY, DubboCodec.NAME)
    .build();
    String str = url.getParameter(SERVER_KEY, DEFAULT_REMOTING_SERVER); if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
    throw new RpcException("Unsupported server type: " + str + ", url: " + url);
    } ExchangeServer server;
    try {
    server = Exchangers.bind(url, requestHandler);
    } catch (RemotingException e) {
    throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
    } str = url.getParameter(CLIENT_KEY);
    if (str != null && str.length() > 0) {
    Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();
    if (!supportedTypes.contains(str)) {
    throw new RpcException("Unsupported client type: " + str);
    }
    } return server;
    }

Dubbo中@Service工作过程解析的更多相关文章

  1. Service工作过程

    Service两种工作状态的作用 1)启动状态:用于执行后台计算 2)绑定状态:用于其他组件和Service的交互 注意:Service这两种状态可以共存,即Service既可以处于启动状态也可以同时 ...

  2. Dubbo中SPI扩展机制解析

    dubbo的SPI机制类似与Java的SPI,Java的SPI会一次性的实例化所有扩展点的实现,有点显得浪费资源. dubbo的扩展机制可以方便的获取某一个想要的扩展实现,每个实现都有自己的name, ...

  3. Dubbo中订阅和通知解析

    Dubbo中关于服务的订阅和通知主要发生在服务提供方暴露服务的过程和服务消费方初始化时候引用服务的过程中. 2345678910111213141516171819 public <T> ...

  4. Dubbo中Directory解析

    Directory代表多个Invoker,可以把它看成List Directory接口 Directory接口继承了Node接口: 1234567 public interface Directory ...

  5. Dubbo中暴露服务的过程解析

    dubbo暴露服务有两种情况,一种是设置了延迟暴露(比如delay="5000"),另外一种是没有设置延迟暴露或者延迟设置为-1(delay="-1"): 设置 ...

  6. Dubbo中消费者初始化的过程解析

    首先还是Spring碰到dubbo的标签之后,会使用parseCustomElement解析dubbo标签,使用的解析器是dubbo的DubboBeanDefinitionParser,解析完成之后返 ...

  7. Dubbo中集群Cluster,负载均衡,容错,路由解析

    Dubbo中的Cluster可以将多个服务提供方伪装成一个提供方,具体也就是将Directory中的多个Invoker伪装成一个Invoker,在伪装的过程中包含了容错的处理,负载均衡的处理和路由的处 ...

  8. Dubbo中服务消费者和服务提供者之间的请求和响应过程

    服务提供者初始化完成之后,对外暴露Exporter.服务消费者初始化完成之后,得到的是Proxy代理,方法调用的时候就是调用代理. 服务消费者经过初始化之后,得到的是一个动态代理类,InvokerIn ...

  9. Dubbo中编码和解码的解析

    (这里做的解析不是很详细,等到走完整个流程再来解析)Dubbo中编解码的工作由Codec2接口的实现来处理,回想一下第一次接触到Codec2相关的内容是在服务端暴露服务的时候,根据具体的协议去暴露服务 ...

随机推荐

  1. springboot集成aop日志

    日常开发中假如是前后端完全分离,我们会习惯用浏览器去调用controller的接口来测试.这一个过程普通的日志功能会记录sql参数等一些基本信息.但是假如项目越来越庞大,我们的包越来越多,在维护项目和 ...

  2. PAT甲级——1011 World Cup Betting

    PATA1011 World Cup Betting With the 2010 FIFA World Cup running, football fans the world over were b ...

  3. markdown 的一些字体

    <font face='Comic Sans MS', size=5> 看看字体 1 one Hello 2 two Hello <font face='Kristen ITC', ...

  4. rest framework-视图和路由-长期维护

    ###############   三种视图    ############### # 第一种方法:使用mixins # class AuthorView(mixins.ListModelMixin, ...

  5. vs编译完提示不支持尝试的执行操作

    最近公司在用MFC做项目,编译完就弹窗提示,不支持尝试的执行操作,开始没注意,但是点击弹窗提示无反应: 问题原因:dialog里的控件有的已经删除了,但是DoDataExchange函数还存在控件的关 ...

  6. 【转】HeadFirst 组合模式+迭代器错误原因以及解决代码

    http://blog.csdn.net/sugar_girl/article/details/53400267     <HeadFirst JAVA设计模式>中用迭代器迭代组合模式是存 ...

  7. JavaScript函数创建方式

    1.工厂模式 function createPerson(name, job) { var o = new Object() o.name = name o.job = job o.sayName = ...

  8. freeRadius日志关闭

    vim /etc/raddb/radiusd.conf #file = ${logdir}/radius.log file = /dev/null vim /etc/raddb/modules/det ...

  9. 吴裕雄--天生自然python学习笔记:Python MySQL - mysql-connector 驱动

    本章节我们为大家介绍使用 mysql-connector 来连接使用 MySQL, mysql-connector 是 MySQL 官方提供的驱动器. 我们可以使用 pip 命令来安装 mysql-c ...

  10. 关于前端使用JavaScript获取base64图片大小的方法

    base64原理 Base64编码要求把3个8位字节(38=24)转化为4个6位的字节(46=24),之后在6位的前面补两个0,形成8位一个字节的形式. 如果剩下的字符不足3个字节,则用0填充,输出字 ...