1、spring自定义标签的步骤(参考https://my.oschina.net/lenglingx/blog/889662)
a、编写xsd文件描述自定义的元素。看下面的代码实例,这里定义了application。其中上面的代码是具体的元素定义,下面的是自定义元素名。
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns="http://ttdubbo.oschina.net/schema/ttdubbo"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:tool="http://www.springframework.org/schema/tool"
targetNamespace="http://ttdubbo.oschina.net/schema/ttdubbo">
<xsd:complexType name="applicationType">
<xsd:attribute name="id" type="xsd:ID">
<xsd:annotation>
<xsd:documentation><![CDATA[ The unique identifier for a bean. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="name" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[ The application name. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="version" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[ The application version. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:element name="application" type="applicationType">
<xsd:annotation>
<xsd:documentation><![CDATA[ The application config ]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:schema>
b、编写标签对应的实体类:
public class Application {
private String id;
private String name;
private String version;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
@Override
public String toString() {
return "Application [id=" + id + ", name=" + name + ", version=" + version + "]";
}
}
c、编写标签的解析类
/**
* All rights Reserved, Designed By www.tydic.com
* @Title: ApplicationBeanDefinitionParser.java
* @Package ApplicationBeanDefinitionParser
* @Description: TODO(用一句话描述该文件做什么)
* @author: andyzhu
* @date: 2018年10月8日 上午11:14:30
* @version V1.0
* @Copyright: 2018 www.acc.com Inc. All rights reserved.
* 注意:禁止外泄以及用于其他的商业目
*/
package cn.andy.testdubbo;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* @author sks
*
*/
public class ApplicationBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
private final Class<?> beanClass;
private final boolean required;
public ApplicationBeanDefinitionParser(Class<?> beanClass, boolean required) {
this.beanClass = beanClass;
this.required = required;
}
protected Class getBeanClass(Element element) {
return Application.class;
}
protected void doParse(Element element, BeanDefinitionBuilder bean) {
String id = element.getAttribute("id");
String name = element.getAttribute("name");
String version = element.getAttribute("version");
if (StringUtils.hasText(id)) {
bean.addPropertyValue("id", id);
}
if (StringUtils.hasText(name)) {
bean.addPropertyValue("name", name);
}
if (StringUtils.hasText(version)) {
bean.addPropertyValue("version", version);
}
}
}
d 、编写命令空间处理类,指定自定义的标签的解析类。
/**
* All rights Reserved, Designed By www.tydic.com
* @Title: TtdubboNamespaceHandler.java
* @Package cn.andy.testdubbo
* @Description: TODO(用一句话描述该文件做什么)
* @author: andyzhu
* @date: 2018年10月8日 下午1:58:31
* @version V1.0
* @Copyright: 2018 www.acc.com Inc. All rights reserved.
* 注意:禁止外泄以及用于其他的商业目
*/
package cn.andy.testdubbo;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
/**
* @author sks
*
*/
public class TtdubboNamespaceHandler extends NamespaceHandlerSupport {
/* (non-Javadoc)
* @see org.springframework.beans.factory.xml.NamespaceHandler#init()
*/
@Override
public void init() {
// TODO Auto-generated method stub
registerBeanDefinitionParser("application", new ApplicationBeanDefinitionParser(Application.class, true));
}
}
e、编写spring.handlers和spring.schemas,这两个文件都在resources目录下的META-INF文件夹中
spring.handlers文件内容,http后面的\是转义字符,注意中间的等号
http\://ttdubbo.oschina.net/schema/ttdubbo=cn.andy.testdubbo.TtdubboNamespaceHandler
spring.schemas文件内容:
http\://ttdubbo.oschina.net/schema/ttdubbo.xsd=META-INF/ttdubbo.xsd
f、测试
在spring的xml文件中定义:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ttdubbo="http://ttdubbo.oschina.net/schema/ttdubbo"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
http://ttdubbo.oschina.net/schema/ttdubbo http://ttdubbo.oschina.net/schema/ttdubbo.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<!-- 导入properity文件-->
<context:property-placeholder location="classpath*:conf/*.properties"/>
<context:component-scan base-package="cn.andy.dubbo"></context:component-scan>
<import resource="classpath*:spring/applicationContext-dubbo.xml"/>
<ttdubbo:application id="app1" name="ttdubboTest" version="0.0.1" />
<ttdubbo:registry id="reg1" address="192.168.1.101" port="80" />
</beans>
然后,在测试文件中
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-service.xml");
Application application1 = (Application) context.getBean("app1");
System.out.println(application1.toString());
System.out.println("Hello World!");
}
}
例子下载地址https://download.csdn.net/download/andyzhu_2005/10814375
2、 dubbo的标签解析
通过上面的流程,来看看dubbo的标签解析是否是这样子的:
a、在dubbo的源码目录下的META-INF文件夹中,找到的dubbo.xsd文件。里面有各个标签的元素定义:我们关注两个,service和reference标签
<xsd:complexType name="abstractServiceType">
<xsd:complexContent>
<xsd:extension base="abstractInterfaceType">
<xsd:attribute name="register" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[ The service can be register to registry. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="version" type="xsd:string" use="optional" default="0.0.0">
<xsd:annotation>
<xsd:documentation><![CDATA[ The service version. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="group" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[ The service group. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="deprecated" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[ whether the service is deprecated. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="delay" type="xsd:string" use="optional" default="0">
<xsd:annotation>
<xsd:documentation>
<![CDATA[ The service export delay millisecond. ]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="export" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation>
<![CDATA[ The service is export. ]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="weight" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation>
<![CDATA[ The service weight. ]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="document" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation>
<![CDATA[ The service document. ]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="dynamic" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[ the service registered to the registry is dynamic(true) or static(false). ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="token" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[ The service use token. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="accesslog" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[ The service use accesslog. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="executes" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[ The service allow execute requests. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="protocol" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[ The service protocol. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:anyAttribute namespace="##other" processContents="lax" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="referenceType">
<xsd:complexContent>
<xsd:extension base="abstractReferenceType">
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element ref="method" minOccurs="0" maxOccurs="unbounded" />
<xsd:element ref="parameter" minOccurs="0" maxOccurs="unbounded" />
</xsd:choice>
<xsd:attribute name="interface" type="xsd:token" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[ The service interface class name. ]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:expected-type type="java.lang.Class"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="url" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[ Provider list url. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="client" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[ Protocol transport client type. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="consumer" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[ Deprecated. Replace to reference-default. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="protocol" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[ The service protocol. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:anyAttribute namespace="##other" processContents="lax" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:element name="service" type="serviceType">
<xsd:annotation>
<xsd:documentation><![CDATA[ Export service config ]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="reference" type="referenceType">
<xsd:annotation>
<xsd:documentation><![CDATA[ Reference service config ]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
b、在spring.handlers和spring.schemas写入DubboNamespaceHandler和dubbo.xsd。
其中,DubboNamespaceHandler的内容是:
/*
* Copyright 1999-2011 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.dubbo.config.spring.schema;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
import com.alibaba.dubbo.common.Version;
import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.ConsumerConfig;
import com.alibaba.dubbo.config.ModuleConfig;
import com.alibaba.dubbo.config.MonitorConfig;
import com.alibaba.dubbo.config.ProtocolConfig;
import com.alibaba.dubbo.config.ProviderConfig;
import com.alibaba.dubbo.config.RegistryConfig;
import com.alibaba.dubbo.config.spring.AnnotationBean;
import com.alibaba.dubbo.config.spring.ReferenceBean;
import com.alibaba.dubbo.config.spring.ServiceBean;
/**
* DubboNamespaceHandler
*
* @author william.liangf
* @export
*/
public class DubboNamespaceHandler extends NamespaceHandlerSupport {
static {
Version.checkDuplicate(DubboNamespaceHandler.class);
}
public void init() {
registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true));
}
}
这里,对dubbo自定的标签分别进行了解析。其中,service标签就生成了ServiceBean类,而reference就生成了ReferenceBean类。
3、看ReferenceBean类
public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean, ApplicationContextAware, InitializingBean, DisposableBean
ReferenceBean是实现了FactoryBean接口的类,我们知道,Spring中有两种类型的Bean,一种是普通Bean,另一种是工厂Bean,即FactoryBean,这两种Bean都被容器管理,但工厂Bean跟普通Bean不同,其返回的对象不是指定类的一个实例,其返回的是该FactoryBean的getObject方法所返回的对象。
所以,查看ReferenceBean的getObject()方法,
public Object getObject() throws Exception {
return get();
}
public synchronized T get() {
if (destroyed){
throw new IllegalStateException("Already destroyed!");
}
if (ref == null) {
init();
}
return ref;
}
这里的 return get();就是我们的消费端初始化的开始。
4、看ServiceBean类
public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener, BeanNameAware{}
ServiceBean实现了ApplicationListener接口,其在初始化完成后,会回调onApplicationEvent方法:
public void onApplicationEvent(ApplicationEvent event) {
if (ContextRefreshedEvent.class.getName().equals(event.getClass().getName())) {
if (isDelay() && ! isExported() && ! isUnexported()) {
if (logger.isInfoEnabled()) {
logger.info("The service ready on spring started. service: " + getInterface());
}
export();
}
}
}
这个方法就是服务端发布服务的开始。