在本文第一章,分析的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>(); @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));
}
}

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;
} @Override
public 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 init
bd.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();
// 必须是setXXX
if (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类来实现的:

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方法来发布服务。

motan源码分析三:与spring框架的结合的更多相关文章

  1. spring源码分析系列 (3) spring拓展接口InstantiationAwareBeanPostProcessor

    更多文章点击--spring源码分析系列 主要分析内容: 一.InstantiationAwareBeanPostProcessor简述与demo示例 二.InstantiationAwareBean ...

  2. 使用react全家桶制作博客后台管理系统 网站PWA升级 移动端常见问题处理 循序渐进学.Net Core Web Api开发系列【4】:前端访问WebApi [Abp 源码分析]四、模块配置 [Abp 源码分析]三、依赖注入

    使用react全家桶制作博客后台管理系统   前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基 ...

  3. tomcat源码分析(三)一次http请求的旅行-从Socket说起

    p { margin-bottom: 0.25cm; line-height: 120% } tomcat源码分析(三)一次http请求的旅行 在http请求旅行之前,我们先来准备下我们所需要的工具. ...

  4. [Abp 源码分析]三、依赖注入

    0.简要介绍 在 Abp 框架里面,无时无刻不存在依赖注入,关于依赖注入的作用与好处我就不在这里多加赘述了,网上有很多解释的教程.在 [Abp 源码分析]一.Abp 框架启动流程分析 里面已经说过,A ...

  5. 5.2 spring5源码--spring AOP源码分析三---切面源码分析

    一. AOP切面源码分析 源码分析分为三部分 1. 解析切面 2. 创建动态代理 3. 调用 源码的入口 源码分析的入口, 从注解开始: 组件的入口是一个注解, 比如启用AOP的注解@EnableAs ...

  6. Spring5深度源码分析(三)之AnnotationConfigApplicationContext启动原理分析

    代码地址:https://github.com/showkawa/spring-annotation/tree/master/src/main/java/com/brian AnnotationCon ...

  7. ABP源码分析三:ABP Module

    Abp是一种基于模块化设计的思想构建的.开发人员可以将自定义的功能以模块(module)的形式集成到ABP中.具体的功能都可以设计成一个单独的Module.Abp底层框架提供便捷的方法集成每个Modu ...

  8. ABP源码分析三十三:ABP.Web

    ABP.Web模块并不复杂,主要完成ABP系统的初始化和一些基础功能的实现. AbpWebApplication : 继承自ASP.Net的HttpApplication类,主要完成下面三件事一,在A ...

  9. ABP源码分析三十五:ABP中动态WebAPI原理解析

    动态WebAPI应该算是ABP中最Magic的功能之一了吧.开发人员无须定义继承自ApiController的类,只须重用Application Service中的类就可以对外提供WebAPI的功能, ...

随机推荐

  1. 解决Windows8前面板耳机无声的问题

    Windows8已经到来不久了,相信很多朋友已经在使用,在使用时也许会遇到前面板耳机无声的问题,网上的其他办法很麻烦还不一定能解决,在这里我会给大家提供最简单的办法解决这个问题. 百度经验:jingy ...

  2. (转)dedecms入门

    学dedecms一段时间了,把我的入门体会和大家分享一下. 什么是dedecm cms(内容管理系统):现在有各种内容模型,如书评(包括书名,出版社,评论等字段).cms一般有用户后台,网页的用户可以 ...

  3. (转)Android Touch事件传递机制

    -----来源:http://www.trinea.cn/android/touch-event-delivery-mechanism/ 介绍Android Touch事件的传递机制. 不少朋友私信问 ...

  4. birt报表中使用多个数据集。

    这个问题困扰了几天,也没搜到答案,由于工作需要,创建了两个数据集和两个表格,第一个数据集和表格之间没有任何问题.但是第二个数据集拖过去就显示不可用,除非拖到表格外面,当然也就没用了.一朋友说拖一个网格 ...

  5. iOS程序的完整启动过程(有storyboard)

    1.先执行main函数,main内部会调用UIApplicationMain函数 2.UIApplicationMain函数里面做了什么事情:1> 创建UIApplication对象 2> ...

  6. 使用C#连接ORACLE数据库

    一.使用OracleClient组件连接Oracle   .Net框架的System.Data.OracleClient.dll组件(ADO.Net组件),为连接和使用Oracle数据库提供了很大的方 ...

  7. (转载)最实用的清除浮动代码 css的文字过长裁剪后面跟着省略号

    css: .clearfloat:after{display:block;clear:both;content:"";visibility:hidden;} .clearfloat ...

  8. struts1、ajax、jquery、json简单实例

    1.页面ajax代码,使用$.ajax,获得json对象后each $.ajax({ type:"GET", url:ctx + "/uploadImg.do" ...

  9. 织梦dede标签tags的美化教程

    我们在行dede仿站的时候,经常会遇到tags标签的调用,调用非常简单,官方有专门的调用标签,但是官方的调用是一成不变的模式,dede5.6以前的版本,基本上都是黑色相同大小的表示,而在dede5.7 ...

  10. python函数参数的pack与unpack

    python函数参数的pack与unpack 上周在使用django做开发的时候用到了mixin(关于mixin我还要写一个博客专门讨论一下,现在请参见这里),其中又涉及到了一个关于函数参数打包(pa ...