motan源码分析三:与spring框架的结合

article/2025/9/11 4:07:49

在本文第一章,分析的demo中使用了代码加载的方式加载了相关的类,但在我们的实际工作中,使用spring来加载相关的类的情况会更多,本文将分析一下motan是如何与spring一起协同工作的,主要的原理就是利用了spring支持的自定义标签的实现,这也是需要和spring结合的框架的实现方式。

1.首先实现motan.xsd文件,具体可以参见:http://api.weibo.com/schema/motan.xsd

2.对于标签的相应解析类进行注册:

复制代码
public class MotanNamespaceHandler extends NamespaceHandlerSupport {//集成NamespaceHandlerSupport类,spring会自动调用init方法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>();@Overridepublic 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));}
}
复制代码

3.motan的标签解析类MotanBeanDefinitionParser:

复制代码
public class MotanBeanDefinitionParser implements BeanDefinitionParser {//实现BeanDefinitionParser接口private final Class<?> beanClass;private final boolean required;public MotanBeanDefinitionParser(Class<?> beanClass, boolean required) {this.beanClass = beanClass;this.required = required;}@Overridepublic BeanDefinition parse(Element element, ParserContext parserContext) {try {return parse(element, parserContext, beanClass, required);} catch (ClassNotFoundException e) {e.printStackTrace();throw new RuntimeException(e);}}@SuppressWarnings({"rawtypes", "unchecked"})private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required)throws ClassNotFoundException {RootBeanDefinition bd = new RootBeanDefinition();bd.setBeanClass(beanClass);// 不允许lazy initbd.setLazyInit(false);// 如果没有id则按照规则生成一个id,注册id到context中String id = element.getAttribute("id");if ((id == null || id.length() == 0) && required) {String generatedBeanName = element.getAttribute("name");if (generatedBeanName == null || generatedBeanName.length() == 0) {generatedBeanName = element.getAttribute("class");}if (generatedBeanName == null || generatedBeanName.length() == 0) {generatedBeanName = beanClass.getName();}id = generatedBeanName;int counter = 2;while (parserContext.getRegistry().containsBeanDefinition(id)) {id = generatedBeanName + (counter++);}}if (id != null && id.length() > 0) {if (parserContext.getRegistry().containsBeanDefinition(id)) {throw new IllegalStateException("Duplicate spring bean id " + id);}parserContext.getRegistry().registerBeanDefinition(id, bd);}bd.getPropertyValues().addPropertyValue("id", id);if (ProtocolConfig.class.equals(beanClass)) {MotanNamespaceHandler.protocolDefineNames.add(id);} else if (RegistryConfig.class.equals(beanClass)) {MotanNamespaceHandler.registryDefineNames.add(id);} else if (BasicServiceInterfaceConfig.class.equals(beanClass)) {MotanNamespaceHandler.basicServiceConfigDefineNames.add(id);} else if (BasicRefererInterfaceConfig.class.equals(beanClass)) {MotanNamespaceHandler.basicRefererConfigDefineNames.add(id);} else if (ServiceConfigBean.class.equals(beanClass)) {String className = element.getAttribute("class");if (className != null && className.length() > 0) {RootBeanDefinition classDefinition = new RootBeanDefinition();classDefinition.setBeanClass(Class.forName(className, true, Thread.currentThread().getContextClassLoader()));classDefinition.setLazyInit(false);parseProperties(element.getChildNodes(), classDefinition);bd.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl"));}}Set<String> props = new HashSet<String>();ManagedMap parameters = null;// 把配置文件中的可以set的属性放到bd中for (Method setter : beanClass.getMethods()) {String name = setter.getName();// 必须是setXXXif (name.length() <= 3 || !name.startsWith("set") || !Modifier.isPublic(setter.getModifiers())|| setter.getParameterTypes().length != 1) {continue;}String property = (name.substring(3, 4).toLowerCase() + name.substring(4)).replaceAll("_", "-");props.add(property);if ("id".equals(property)) {bd.getPropertyValues().addPropertyValue("id", id);continue;}String value = element.getAttribute(property);if (StringUtils.isBlank(value) && "protocol".equals(property)) {// srevice中的protocol信息是隐含在export中,所以需要从export中获取protocol来配置String exportValue = element.getAttribute(URLParamType.export.getName());if (!StringUtils.isBlank(exportValue)) {value = ConfigUtil.extractProtocols(exportValue);}}if ("methods".equals(property)) {parseMethods(id, element.getChildNodes(), bd, parserContext);}if (StringUtils.isBlank(value)) {continue;}value = value.trim();if (value.length() == 0) {continue;}Object reference;if ("ref".equals(property)) {if (parserContext.getRegistry().containsBeanDefinition(value)) {BeanDefinition refBean = parserContext.getRegistry().getBeanDefinition(value);if (!refBean.isSingleton()) {throw new IllegalStateException("The exported service ref " + value + " must be singleton! Please set the " + value+ " bean scope to singleton, eg: <bean id=\"" + value + "\" scope=\"singleton\" ...>");}}reference = new RuntimeBeanReference(value);} else if ("protocol".equals(property)) {if (!value.contains(",")) {reference = new RuntimeBeanReference(value);} else {parseMultiRef("protocols", value, bd, parserContext);reference = null;}} else if ("registry".equals(property)) {parseMultiRef("registries", value, bd, parserContext);reference = null;} else if ("basicService".equals(property)) {reference = new RuntimeBeanReference(value);} else if ("basicReferer".equals(property)) {reference = new RuntimeBeanReference(value);} else if ("extConfig".equals(property)) {reference = new RuntimeBeanReference(value);} else {reference = new TypedStringValue(value);}if (reference != null) {bd.getPropertyValues().addPropertyValue(property, reference);}}if (ProtocolConfig.class.equals(beanClass)) {// 把剩余的属性放到protocol的parameters里面NamedNodeMap attributes = element.getAttributes();int len = attributes.getLength();for (int i = 0; i < len; i++) {Node node = attributes.item(i);String name = node.getLocalName();if (!props.contains(name)) {if (parameters == null) {parameters = new ManagedMap();}String value = node.getNodeValue();parameters.put(name, new TypedStringValue(value, String.class));}}bd.getPropertyValues().addPropertyValue("parameters", parameters);}return bd;}@SuppressWarnings({"unchecked", "rawtypes"})private static void parseMultiRef(String property, String value, RootBeanDefinition beanDefinition, ParserContext parserContext) {String[] values = value.split("\\s*[,]+\\s*");ManagedList list = null;for (int i = 0; i < values.length; i++) {String v = values[i];if (v != null && v.length() > 0) {if (list == null) {list = new ManagedList();}list.add(new RuntimeBeanReference(v));}}beanDefinition.getPropertyValues().addPropertyValue(property, list);}private static void parseProperties(NodeList nodeList, RootBeanDefinition beanDefinition) {if (nodeList != null && nodeList.getLength() > 0) {for (int i = 0; i < nodeList.getLength(); i++) {Node node = nodeList.item(i);if (node instanceof Element) {if ("property".equals(node.getNodeName()) || "property".equals(node.getLocalName())) {String name = ((Element) node).getAttribute("name");if (name != null && name.length() > 0) {String value = ((Element) node).getAttribute("value");String ref = ((Element) node).getAttribute("ref");if (value != null && value.length() > 0) {beanDefinition.getPropertyValues().addPropertyValue(name, value);} else if (ref != null && ref.length() > 0) {beanDefinition.getPropertyValues().addPropertyValue(name, new RuntimeBeanReference(ref));} else {throw new UnsupportedOperationException("Unsupported <property name=\"" + name+ "\"> sub tag, Only supported <property name=\"" + name + "\" ref=\"...\" /> or <property name=\""+ name + "\" value=\"...\" />");}}}}}}}@SuppressWarnings({"unchecked", "rawtypes"})private static void parseMethods(String id, NodeList nodeList, RootBeanDefinition beanDefinition, ParserContext parserContext)throws ClassNotFoundException {if (nodeList != null && nodeList.getLength() > 0) {ManagedList methods = null;for (int i = 0; i < nodeList.getLength(); i++) {Node node = nodeList.item(i);if (node instanceof Element) {Element element = (Element) node;if ("method".equals(node.getNodeName()) || "method".equals(node.getLocalName())) {String methodName = element.getAttribute("name");if (methodName == null || methodName.length() == 0) {throw new IllegalStateException("<motan:method> name attribute == null");}if (methods == null) {methods = new ManagedList();}BeanDefinition methodBeanDefinition = parse((Element) node, parserContext, MethodConfig.class, false);String name = id + "." + methodName;BeanDefinitionHolder methodBeanDefinitionHolder = new BeanDefinitionHolder(methodBeanDefinition, name);methods.add(methodBeanDefinitionHolder);}}}if (methods != null) {beanDefinition.getPropertyValues().addPropertyValue("methods", methods);}}}}
复制代码

4.在第一章节中,有一个export的动作在各项配置加载完成后,需要被调用,motan是通过下面的ServiceConfigBean类来实现的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public  class  ServiceConfigBean<T>  extends  ServiceConfig<T>
         implements
             BeanPostProcessor,
             BeanFactoryAware,
             InitializingBean,
             DisposableBean,
             ApplicationListener<ContextRefreshedEvent> {
     @Override
     public  void  onApplicationEvent(ContextRefreshedEvent event) {
         if  (!getExported().get()) {
             export(); //当配置信息加载完后,调用export方法
         }
     }
}

总结一下本章的知识点:

1.使用常规的spring扩展自定义标签的方式来实现motan对于spring的支持;

2.集成NamespaceHandlerSupport来实现标签解析类的注册;

3.实现BeanDefinitionParser的接口来解析具体的标签;

4.在配置信息加载完后,利用onApplicationEvent事件来调用export方法来发布服务。


http://chatgpt.dhexx.cn/article/IArGtQl5.shtml

相关文章

motan源码分析一:服务发布及注册

motan是新浪微博开源的服务治理框架&#xff0c;具体介绍请看&#xff1a;http://tech.sina.com.cn/i/2016-05-10/doc-ifxryhhh1869879.shtml. 本系列的文章将分析它的底层源码&#xff0c;分析的源码版本为&#xff1a;0.1.2。第一篇文章将以服务的发布和注册开始&#xff0c;…

轻量级Rpc框架设计--motan源码解析六:client端服务发现

一, Client端初始化工作 client端通过RefererConfigBean类实现InitializingBean接口的afterPropertiesSet方法, 进行下面三项检查配置工作: ①checkAndConfigBasicConfig(); // 检查并配置basicConfig ②checkAndConfigProtocols(); //检查并配置protocols ③checkAndConfi…

java rpc motan_【RPC 专栏】Motan 中使用异步 RPC 接口

为什么慢&#xff1f; 多线程加速 异步调用 RPC 异步调用 总结 这周六参加了一个美团点评的技术沙龙&#xff0c;其中一位老师在介绍他们自研的 RPC 框架时提到一点&#xff1a;RPC 请求分为 sync&#xff0c;future&#xff0c;callback&#xff0c;oneway&#xff0c;并且需要…

Motan-远程调用的rpc框架的负载均衡策略

虽然我不会&#xff0c;但是偶然之间看到了Motan远程调用框架的一些内容&#xff0c;然后直接copy过来了&#xff0c;想着以后自己可能看看 集群中的loadbalance负载均衡策略 ActiveWeightLoadBalance"低并发优化" 负载均衡 /** Copyright 2009-2016 Weibo, Inc.** …

motan源码分析五:cluster相关

上一章我们分析了客户端调用服务端相关的源码&#xff0c;但是到了cluster里面的部分我们就没有分析了&#xff0c;本章将深入分析cluster和它的相关支持类。 1.clustersupport的创建过程&#xff0c;上一章的ReferConfig的initRef()方法中调用了相关的创建代码&#xff1a; fo…

java 微博 开源_微博开源框架Motan初体验

前两天&#xff0c;我在开源中国的微信公众号看到新浪微博的轻量Rpc框架——Motan开源了。上网查了下&#xff0c;才得知这个Motan来头不小&#xff0c;支撑着新浪微博的千亿调用&#xff0c;曾经在2014年的春晚中有着千亿次的调用&#xff0c;对抗了春晚的最高峰值。 什么是Mo…

搭建新浪RPC框架motan Demo

motan是新浪微博开源的RPC框架&#xff0c;github官网是&#xff1a;https://github.com/weibocom/motan 今天就先搭建一个Hello world demo&#xff0c;本demo基于motan 0.2.1版本 首先先去github下载源代码&#xff08;motan-manager报错请忽略&#xff0c;eclipse的web Mod…

微博RPC框架Motan

原文来自&#xff1a;http://blog.csdn.net/autfish/article/details/51374798 从14年开始就陆续看到新浪微博RPC框架Motan的介绍&#xff0c;时隔两年后&#xff0c;微博团队终于宣布开源轻量级RPC框架Motan&#xff0c;项目地址&#xff1a; https://github.com/weibocom/mot…

motan rpc 接口统一异常处理

1.hello word 一个Motan扩展 大概需要下面的三点&#xff1a; 实现SPI扩展点接口 package com.weibo.api.motan.filter; Spi public interface Filter {Response filter(Caller<?> caller, Request request); }业务代码实现Filter public class PlsProviderExceptionF…

motan用户开发指南

目录 基本介绍 架构概述 模块概述 配置概述 使用Motan 工程依赖 处理调用异常 配置说明 协议与连接&#xff08;motan:protocol) 介绍 Motan协议 本地调用 注册中心与服务发现(motan:registry) 介绍 使用Consul作为注册中心 使用Zookeeper作为注册中心 不使用…

从motan看RPC框架设计

kris的文章开始 计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决 从零开发一款RPC框架&#xff0c;说难也难说简单也简单。难的是你的设计将如何面对实际中的复杂应用场景&#xff1b;简单的是其思想可以仅仅浓缩成一行方法调用。motan是今年(2016年)新浪微博…

motan与zookeeper框架

新浪科技讯 2016年5月10日&#xff0c;微博方面宣布&#xff0c;支撑微博千亿调用的轻量级 RPC 框架 Motan 正式开源了。微博技术团队希望未来能有更多优秀的开源人入驻&#xff0c;并进一步完善优化。 搭建新浪RPC框架motan Demo&#xff1a;http://blog.csdn.net/linuu/arti…

java rpc motan_RPC框架motan使用

简介 motan是新浪微博开源的一套轻量级、方便使用的RPC框架 Hello World 使用的过程分为Server端和Client端&#xff0c;Server提供RCP的服务接口&#xff0c;Client端发起调用获取结果。 maven的pom文件配置 0.2.1 com.weibo motan-core ${motan.version} com.weibo motan-tra…

轻量级Rpc框架设计--motan源码解析一:框架介绍及框架使用初体验

一, 框架介绍 1.1 概况 motan是新浪微博开源出来的一套高性能、易于使用的分布式远程服务调用(RPC)框架。 1.2 功能 可以spring的配置方式与项目集成. 支持zookeeper服务发现组件, 实现集群环境下服务注册与发现. 保证高并发, 高负载场景下的稳定高性能, , 实现生产环境…

Motan原理、使用、JavaAPI简化、为什么使用Motan

前言&#xff0c;本文包括&#xff0c;rpc解释与为什么使用rpc、rpc性能对比、Motan依赖问题、Motan源码梳理、Motan功能、特点、使用。 主要中心&#xff1a;为什么使用Motan? 一、什么是RPC 官方解释&#xff1a;RPC&#xff08;Remote Procedure Call&#xff09;—远程…

jplayer自动播放

音乐网站的播放器一直都没有解决自动播放的问题&#xff0c;小哲说这样不行的&#xff0c;我也知道不可以这样&#xff0c;毕竟是自己提出要做的&#xff0c;所以要尽自己最大的能力去做好它&#xff01;本周末我一直都在围绕这个问题而研究。 我曾经想过在播放器初始化的时候…

JWPlayer

原文&#xff1a; http://www.cnblogs.com/yukui/archive/2009/03/12/1409469.html The JW MP3 Player (built with Adobes Flash) is the easiest way to add live music or podcasts to your website. It supports playback of a single MP3 file or an RSS, XSPF or ASX pla…

今天开始写些随笔,就从Jplayer开始吧

今天才开始用Jplayer&#xff0c;可能有点落伍了&#xff0c;但是看到网上千篇一律的使用说明&#xff0c;开始决定把自己的使用心得分享一下&#xff0c;废话不多说&#xff0c;开始吧。 Step1&#xff1a; 官网上有具体的搭建顺序&#xff0c;URL&#xff1a;http://www.jp…

关于播放器JPlayer的使用及遇到的问题

jPlayer是一个用于控制和播放mp3文件的jQuery插件。它在后台使用Flash来播放mp3文件&#xff0c;前台播放器外观完全可以使用XHML/CSS自定义。支持&#xff1a; 有一点比较好的是&#xff0c;在支持html5的浏览器上会使用html5的标签audio或者video&#xff0c;而不支持的浏览…

ijkplayer支持播放rtsp、jpeg、gif

ijkplayer版本&#xff1a;k.0.8.8 编译环境&#xff1a;Ubuntu 18.04.6 LTS 使用平台&#xff1a;android 支持rtsp播放 默认的ijkplayer并不支持rtsp流的播放&#xff0c;因为在编译ffmpeg的时候并没有开启rtsp的demuxer&#xff0c;所以在编译ffmpeg的时候需要开启rtsp的d…