一, 如何自定义标签
可参考以前本人针对spring自定义标签写过的一篇博客, Spring自定义标签, 其实原理就是利用spring在初始化的过程中会加载META-INF/spring.handlers, META-INF/spring.schemes这两项文件, 再通过继承NamespaceHandlerSupport类, 并且自定义一个实现类实现BeanDefinitionParser接口就完成了自定的spring标签.
二, motan解析自定义标签
上一篇我们使用xml配置文件的方式进行了服务的暴露与引用, 如下:
motan针对服务暴露, server端xml文件的配置
<bean id="motanDemoServiceImpl" class="com.weibo.motan.demo.server.MotanDemoServiceImpl"/>
<motan:registry regProtocol="zookeeper" name="registry" address="127.0.0.1:2181"/>
<motan:protocol id="demoMotan" default="true" name="motan"
requestTimeout="220" maxServerConnection="80000" maxContentLength="1048576"
maxWorkerThread="800" minWorkerThread="20"/>
<motan:basicService requestTimeout="220" export="demoMotan:8002"
group="motan-demo-rpc" accessLog="false" shareChannel="true" module="motan-demo-rpc"
application="myMotanDemo" registry="registry" id="serviceBasicConfig"/>
<motan:service interface="com.weibo.motan.demo.service.MotanDemoService"
ref="motanDemoServiceImpl" export="demoMotan:8001" basicService="serviceBasicConfig"
requestTimeout="200">
</motan:service>
<motan:service interface="com.weibo.motan.demo.service.MotanDemoService"
ref="motanDemoServiceImpl" export="demoMotan:8002" basicService="serviceBasicConfig"
requestTimeout="200">
</motan:service>
motan针对服务引用, client端xml文件配置
<motan:registry regProtocol="zookeeper" name="registry" address="127.0.0.1:2181" requestTimeout="5000"/>
<motan:protocol default="true" name="motan" haStrategy="failover"
loadbalance="roundrobin" maxClientConnection="10" minClientConnection="2"/>
<motan:basicReferer requestTimeout="200" accessLog="false"
retries="2" group="motan-demo-rpc" module="motan-demo-rpc"
application="myMotanDemo" protocol="motan" registry="registry"
id="motantestClientBasicConfig" throwException="false" check="true" />
<motan:referer id="motanDemoReferer"
interface="com.weibo.motan.demo.service.MotanDemoService"
connectTimeout="300" requestTimeout="300" basicReferer="motantestClientBasicConfig"/>
motan框架对以上配置的解析源码: 通过继承BeanDefinitionPaser接口, 并实现pase()方法.
public class MotanBeanDefinitionParser implements 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);
}
}
paser解析方法:该方法就是将上面server端和client端的xml配置文件中配置项进行逐行解析, 并将解析的内容放入spring容器.
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");
// 如果xml配置文件中该标签没有id属性, 则使用name属性
if ((id == null || id.length() == 0) && required) {
String generatedBeanName = element.getAttribute("name");
if (generatedBeanName == null || generatedBeanName.length() == 0) {
generatedBeanName = element.getAttribute("class");
}
// 如果解析的标签既没有配置id或者name, 那就使用该属性包装类的全限定名称com.weibo.api.xxx.xxz
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 {
// <motan:service>标签的interface属性, 也是在这时候被add到spring容器
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;
}
三, 总结
motan中自定义spring标签, 其原理就是利用了spring在初始化过程中会去加载META-INF/spring.handlers, META-INF/spring.schemes这两个文件, 其中spring.schemes文件中定义了xsd文件的具体路径, 如:http\://api.weibo.com/schema/motan.xsd=META-INF/motan.xsd, 而spring.handlers中则是定义了具体的解析类, 如:http\://api.weibo.com/schema/motan=com.weibo.api.motan.config.springsupport.MotanNamespaceHandler,该MotanNamespaceHandler类则继承自NamespaceHandlerSupport抽象类, 实现其init()方法, 完成具体的解析过程.