Dubbo如何与Spring IOC 配合工作 源码分析
思考????:dubbo是如何做到与spring集成的?是dubbo的扩展机制?还是spring的扩展机制?依赖于Spring提供的XML Schema可扩展机制,用户可以自定义XML Schema文件,并自定义XML Bean解析器,并集成到SpringIOC容器中。创建自定义扩展,主要有以下步骤:[1] 设计配置属性和JavaBean[2]创建XML Schema 文件(编写XSD文件)描述自
思考🤔:dubbo是如何做到与spring集成的?是dubbo的扩展机制?还是spring的扩展机制?
依赖于Spring提供的XML Schema可扩展机制,用户可以自定义XML Schema文件,并自定义XML Bean解析器,并集成到SpringIOC容器中。
创建自定义扩展,主要有以下步骤:
[1] 设计配置属性和JavaBean
[2]创建XML Schema 文件( 编写XSD文件)描述自定义的合法构建模块,主要用于定义数据约束;
[3] 自定义个处理器类,编写NamespaceHandler和BeanDefinitionParser完成解析工作
[4] 编写spring.handlers和spring.schemas串联起所有部件
[5] 在Bean文件中应用
简单了解下spring的扩展机制
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
// 默认解析
parseDefaultElement(ele, delegate);
}else {
// 自定义解析
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
在此之前,先看看Spring中是怎么将各个XML Schema文件和NamespaceHandler对应起来的。
DefaultNamespaceHandlerResolver
public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver {
// 映射文件路径默认在 META-INF/spring.handlers,可以存在于多个jar文件中
public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";
// 资源路径
private final String handlerMappingsLocation;
// 缓存 namespace URI:NamespaceHandler 映射关系
private volatile Map<String, Object> handlerMappings;
public DefaultNamespaceHandlerResolver() {
this(null, DEFAULT_HANDLER_MAPPINGS_LOCATION);
}
public DefaultNamespaceHandlerResolver(ClassLoader classLoader) {
this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION);
}
public DefaultNamespaceHandlerResolver(ClassLoader classLoader, String handlerMappingsLocation) {
Assert.notNull(handlerMappingsLocation, "Handler mappings location must not be null");
this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
this.handlerMappingsLocation = handlerMappingsLocation;
}
/**
* 根据 namespace URI 加载 NamespaceHandler
* @param namespaceUri the relevant namespace URI
* @return the located {@link NamespaceHandler}, or {@code null} if none found
*/
@Override
public NamespaceHandler resolve(String namespaceUri) {
Map<String, Object> handlerMappings = getHandlerMappings();
Object handlerOrClassName = handlerMappings.get(namespaceUri);
if (handlerOrClassName == null) {
return null;
}else if (handlerOrClassName instanceof NamespaceHandler) {
return (NamespaceHandler) handlerOrClassName;
}
else {
String className = (String) handlerOrClassName;
try {
Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
}
NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
// 实例化之后会调用init方法
namespaceHandler.init();
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;
}
catch (ClassNotFoundException ex) {
throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
namespaceUri + "] not found", ex);
}
catch (LinkageError err) {
throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
namespaceUri + "]: problem with handler class file or dependent class", err);
}
}
}
private Map<String, Object> getHandlerMappings() {
// 只会在第一次调用的时候执行以下逻辑,即只会加载一次,只会就从缓存中获取了
if (this.handlerMappings == null) {
synchronized (this) {
if (this.handlerMappings == null) {
try {
// 加载所有配置文件:META-INF/spring.handlers
Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
if (logger.isDebugEnabled()) {
logger.debug("Loaded NamespaceHandler mappings: " + mappings);
}
Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(mappings.size());
// 将 Properties解析成 Map
CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
this.handlerMappings = handlerMappings;
}
catch (IOException ex) {
throw new IllegalStateException(
"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
}
}
}
}
return this.handlerMappings;
}
@Override
public String toString() {
return "NamespaceHandlerResolver using mappings " + getHandlerMappings();
}
}
那么的DefaultNamespaceHandlerResolver的resolve方法是在什么时候被调用呢?前面提到,在Spring启动解析Bean时有可能涉及到自定义解析,即:delegate.parseCustomElement(root),看看它的代码
parseCustomElement
public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele);
// 这里就调用了resolve方法
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
// 调用NamespaceHandler的parse方法,其内部委托各种BeanDefinitionParser进行真正的解析
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
简单看下dubbo的XML文件配置
<!-- 应用信息 -->
<dubbo:application name="demo"/>
<!-- 用dubbo协议在20880端口暴露服务 -->
<dubbo:protocol name="dubbo" port="20880"/>
<!-- 使用zookeeper注册中心暴露服务地址 -->
<dubbo:registry address="zookeeper://localhost:2181" id="registry"/>
<!-- 默认的服务端配置 -->
<dubbo:provider registry="registry" retries="0" timeout="5000"/>
<!-- 声明需要暴露的服务接口 -->
<dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService"/>
<!-- 引用服务 -->
<dubbo:reference id="demoService" check="false" interface="org.apache.dubbo.demo.DemoService"/>
dubbo 标签解析大体流程如下:
1)入口是ClassPathXmlApplicationContext,这个我们一般会讲dubbo的提供者和消费者统一跟spring普通注入xml文件放在一块交由spring容器去解析,至于dubbo自定义标签如何解析往下看。
2)Spring会寻找spring.handlers和spring.schemas串联起所有部件,这里也就是dubbo.xsd已经dubbo自定义注解解析器DubboNamespaceHandler,dubbo.xsd负责解析spring-provider.xml配置的属性是否合法。DubboNamespaceHandler就能解析ClassPathXmlApplicationContext读入的spring-provider.xml读入的带有dubbo自定义标签的注入内容,然后通过DubboBeanDefinitionParser的parse方法进行解析并注入到Spring容器。
3)最后在Service或其他层面用@Resource 和 @Autowired等注解从Spring容器中取出使用。
首先Spring要注入自己的bean需要在Spring-provider.xml(提供者spring注入文件,名字可能不一样)添加bean注入,其中有dubbo的自定义标签,xml如何识别这些标签?拿到标签如何注入到Spring Container?一般需要如下几个步骤:
设计配置属性和JavaBean
设计属性即dubbo.xsd中的attribute属性,如下
<dubbo:reference timeout="40000" check="false" id="order" interface="com.xx.order"
group="test" version="0.1" />
JavaBean对象
xsd定义这些属性,javaBean匹配的属性,如下(代码很长,大致知道了解下,想看的话,建议去看下源码里面):
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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;
import ...
import static com.alibaba.dubbo.common.utils.NetUtils.isInvalidLocalHost;
/**
* ReferenceConfig
*
* @export
*/
public class ReferenceConfig<T> extends AbstractReferenceConfig {
private static final long serialVersionUID = -5864351140409987595L;
private static final Protocol refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
private static final Cluster cluster = ExtensionLoader.getExtensionLoader(Cluster.class).getAdaptiveExtension();
private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
private final List<URL> urls = new ArrayList<URL>();
// interface name
private String interfaceName;
private Class<?> interfaceClass;
// client type
private String client;
// url for peer-to-peer invocation
private String url;
// method configs
private List<MethodConfig> methods;
// default config
private ConsumerConfig consumer;
private String protocol;
// interface proxy reference
private transient volatile T ref;
private transient volatile Invoker<?> invoker;
private transient volatile boolean initialized;
private transient volatile boolean destroyed;
@SuppressWarnings("unused")
private final Object finalizerGuardian = new Object() {
@Override
protected void finalize() throws Throwable {
super.finalize();
if (!ReferenceConfig.this.destroyed) {
logger.warn("ReferenceConfig(" + url + ") is not DESTROYED when FINALIZE");
/* don't destroy for now
try {
ReferenceConfig.this.destroy();
} catch (Throwable t) {
logger.warn("Unexpected err when destroy invoker of ReferenceConfig(" + url + ") in finalize method!", t);
}
*/
}
}
};
public ReferenceConfig() {
}
public ReferenceConfig(Reference reference) {
appendAnnotation(Reference.class, reference);
}
....此处省略很多代码
}
XML Schema文件
编写XSD文件,全称就是XML Schema
------用来校验XML,定义一系列的语法来规范XML
对应的XSD文件如下:
这个文件在dubbo.xsd中
dubbo的XML Schema文件位于dubbo-config模块下的dubbo-config-spring模块中:
dubbo.xsd文件的内容比较多,就不过多介绍了,主要就是定义了一些约束,重点看看spring.handlers的文件内容:
其实就是定义了namespace url 和 NamespaceHandler 的映射关系,从这里可以知道,dubbo相关命名空间的解析主要就是借助于DubboNamespaceHandler
DubboNamespaceHandler
编写继承自Spring的NamespaceHandlerSupport类,重写init()方法,解析属性文件( dubbo bean注入文件)
// NamespaceHandlerSupport是一个抽象类,实现了NamespaceHandler接口
public class DubboNamespaceHandler extends NamespaceHandlerSupport {
static {
Version.checkDuplicate(DubboNamespaceHandler.class);
}
@Override
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 AnnotationBeanDefinitionParser());
}
}
NamespaceHandlerSupport是一个抽象类,实现了NamespaceHandler接口,里面提供了一些默认实现,所以在自定义NamespaceHandler的时候,只要继承NamespaceHandlerSupport即可。
DubboNamespaceHandler中的init方法在上面已经介绍过,在调用DefaultNamespaceHandlerResolver的resolve方法的时候,实例化NamespaceHandler会被执行。
DubboBeanDefinitionParser
registerBeanDefinitionParser主要就是将标签与对应的BeanDefinitionParser缓存到一个Map中。这里涉及到一个DubboBeanDefinitionParser,它实现了BeanDefinitionParser接口,调用NamespaceHandler的parse方法的时,其内部委托DubboBeanDefinitionParser执行真正的Bean解析逻辑
private final Map<String, BeanDefinitionParser> parsers =new HashMap<String, BeanDefinitionParser>();
protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
this.parsers.put(elementName, parser);
}
NamespaceHandler的parse方法在NamespaceHandlerSupport中有默认实现,如下:
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 委托委托DubboBeanDefinitionParser执行真正的Bean解析逻辑
return findParserForElement(element, parserContext).parse(element, parserContext);
}
// 根据标签找到对应的DubboBeanDefinitionParser
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
String localName = parserContext.getDelegate().getLocalName(element);
BeanDefinitionParser parser = this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal(
"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
}
return parser;
}
DubboBeanDefinitionParser中的代码比较多,如果你不是很熟悉dubbo中xml配置方式,估计很难看懂。项目中一般都是通过注解的使用引用服务,xml太过繁琐,简单看下代码
public DubboBeanDefinitionParser(Class<?> beanClass, boolean required) {
this.beanClass = beanClass;
this.required = required;
}
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
return parse(element, parserContext, beanClass, required);
}
private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) {
RootBeanDefinition beanDefinition = new RootBeanDefinition();
// 这里的beanClass主要有:ApplicationConfig.class、ModuleConfig.class、RegistryConfig.class、MonitorConfig.class、ProviderConfig.class、
// ConsumerConfig.class、ServiceBean.class、ReferenceBean.class
beanDefinition.setBeanClass(beanClass);
beanDefinition.setLazyInit(false);
// 处理标签中的id,如果id设置为必输但又没有给值,则取name属性的值,如果name属性也没有值,则不同beanClass有不同的取值逻辑
String id = element.getAttribute("id");
if ((id == null || id.length() == 0) && required) {
String generatedBeanName = element.getAttribute("name");
if (generatedBeanName == null || generatedBeanName.length() == 0) {
if (ProtocolConfig.class.equals(beanClass)) {
generatedBeanName = "dubbo";
} else {
generatedBeanName = element.getAttribute("interface");
}
}
if (generatedBeanName == null || generatedBeanName.length() == 0) {
generatedBeanName = beanClass.getName();
}
id = generatedBeanName;
int counter = 2;
while (parserContext.getRegistry().containsBeanDefinition(id)) {
id = generatedBeanName + (counter++);
}
}
// 如果有多个相同的id,抛IllegalStateException异常
if (id != null && id.length() > 0) {
if (parserContext.getRegistry().containsBeanDefinition(id)) {
throw new IllegalStateException("Duplicate spring bean id " + id);
}
parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
beanDefinition.getPropertyValues().addPropertyValue("id", id);
}
// 当beanClass为ProtocolConfig.class
if (ProtocolConfig.class.equals(beanClass)) {
for (String name : parserContext.getRegistry().getBeanDefinitionNames()) {
BeanDefinition definition = parserContext.getRegistry().getBeanDefinition(name);
PropertyValue property = definition.getPropertyValues().getPropertyValue("protocol");
if (property != null) {
Object value = property.getValue();
if (value instanceof ProtocolConfig && id.equals(((ProtocolConfig) value).getName())) {
definition.getPropertyValues().addPropertyValue("protocol", new RuntimeBeanReference(id));
}
}
}
}
// 当beanClass为ServiceBean.class
else if (ServiceBean.class.equals(beanClass)) {
String className = element.getAttribute("class");
if (className != null && className.length() > 0) {
RootBeanDefinition classDefinition = new RootBeanDefinition();
// 设置beanClass为dubbo标签中真正配置的接口所对应的Class
classDefinition.setBeanClass(ReflectUtils.forName(className));
classDefinition.setLazyInit(false);
parseProperties(element.getChildNodes(), classDefinition);
beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl"));
}
}
// 当beanClass为ProviderConfig.class
else if (ProviderConfig.class.equals(beanClass)) {
parseNested(element, parserContext, ServiceBean.class, true, "service", "provider", id, beanDefinition);
}
// 当beanClass为ConsumerConfig.class
else if (ConsumerConfig.class.equals(beanClass)) {
parseNested(element, parserContext, ReferenceBean.class, false, "reference", "consumer", id, beanDefinition);
}
// 以下的逻辑主要用于获取一些属性的值,然后存到MutablePropertyValues中的 List<PropertyValue> propertyValueList。一个属性对应一个
Set<String> props = new HashSet<String>();
ManagedMap parameters = null;
for (Method setter : beanClass.getMethods()) {
String name = setter.getName();
// 获取beanClass中的所有 public、只有一个参数 并且以set开头的方法
if (name.length() > 3 && name.startsWith("set")
&& Modifier.isPublic(setter.getModifiers())
&& setter.getParameterTypes().length == 1) {
Class<?> type = setter.getParameterTypes()[0];
// 这里就是为了得到要注入对象的属性名,但是有一点要注意:属性 name => name;属性 userName => user-name
String property = StringUtils.camelToSplitName(name.substring(3, 4).toLowerCase() + name.substring(4), "-");
props.add(property);
// 得到get方法
Method getter = null;
try {
getter = beanClass.getMethod("get" + name.substring(3), new Class<?>[0]);
} catch (NoSuchMethodException e) {
try {
getter = beanClass.getMethod("is" + name.substring(3), new Class<?>[0]);
} catch (NoSuchMethodException e2) {
}
}
if (getter == null
|| !Modifier.isPublic(getter.getModifiers())
|| !type.equals(getter.getReturnType())) {
continue;
}
// 和dubbo中的一一些标签属性相关
if ("parameters".equals(property)) {
parameters = parseParameters(element.getChildNodes(), beanDefinition);
} else if ("methods".equals(property)) {
parseMethods(id, element.getChildNodes(), beanDefinition, parserContext);
} else if ("arguments".equals(property)) {
parseArguments(id, element.getChildNodes(), beanDefinition, parserContext);
} else {
// 获取标签中所配置的属性值
String value = element.getAttribute(property);
if (value != null) {
value = value.trim();
if (value.length() > 0) {
if ("registry".equals(property) && RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(value)) {
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setAddress(RegistryConfig.NO_AVAILABLE);
beanDefinition.getPropertyValues().addPropertyValue(property, registryConfig);
} else if ("registry".equals(property) && value.indexOf(',') != -1) {
parseMultiRef("registries", value, beanDefinition, parserContext);
} else if ("provider".equals(property) && value.indexOf(',') != -1) {
parseMultiRef("providers", value, beanDefinition, parserContext);
} else if ("protocol".equals(property) && value.indexOf(',') != -1) {
parseMultiRef("protocols", value, beanDefinition, parserContext);
} else {
Object reference;
if (isPrimitive(type)) {
if ("async".equals(property) && "false".equals(value)
|| "timeout".equals(property) && "0".equals(value)
|| "delay".equals(property) && "0".equals(value)
|| "version".equals(property) && "0.0.0".equals(value)
|| "stat".equals(property) && "-1".equals(value)
|| "reliable".equals(property) && "false".equals(value)) {
// backward compatibility for the default value in old version's xsd
value = null;
}
reference = value;
} else if ("protocol".equals(property)
&& ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(value)
&& (!parserContext.getRegistry().containsBeanDefinition(value)
|| !ProtocolConfig.class.getName().equals(parserContext.getRegistry().getBeanDefinition(value).getBeanClassName()))) {
if ("dubbo:provider".equals(element.getTagName())) {
logger.warn("Recommended replace <dubbo:provider protocol=\"" + value + "\" ... /> to <dubbo:protocol name=\"" + value + "\" ... />");
}
// backward compatibility
ProtocolConfig protocol = new ProtocolConfig();
protocol.setName(value);
reference = protocol;
} else if ("onreturn".equals(property)) {
int index = value.lastIndexOf(".");
String returnRef = value.substring(0, index);
String returnMethod = value.substring(index + 1);
reference = new RuntimeBeanReference(returnRef);
beanDefinition.getPropertyValues().addPropertyValue("onreturnMethod", returnMethod);
} else if ("onthrow".equals(property)) {
int index = value.lastIndexOf(".");
String throwRef = value.substring(0, index);
String throwMethod = value.substring(index + 1);
reference = new RuntimeBeanReference(throwRef);
beanDefinition.getPropertyValues().addPropertyValue("onthrowMethod", throwMethod);
} else if ("oninvoke".equals(property)) {
int index = value.lastIndexOf(".");
String invokeRef = value.substring(0, index);
String invokeRefMethod = value.substring(index + 1);
reference = new RuntimeBeanReference(invokeRef);
beanDefinition.getPropertyValues().addPropertyValue("oninvokeMethod", invokeRefMethod);
} else {
if ("ref".equals(property) && 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);
}
beanDefinition.getPropertyValues().addPropertyValue(property, reference);
}
}
}
}
}
}
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));
}
}
if (parameters != null) {
beanDefinition.getPropertyValues().addPropertyValue("parameters", parameters);
}
return beanDefinition;
}
有关于各个标签和具体抽象类的对应关系,其实在DubboNamespaceHandler已经简单的写出来的
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 AnnotationBeanDefinitionParser());
有关于各个标签的简单用法,如下图:
编写spring.handlers和spring.schemas串联起所有部件
上面几个步骤走下来会发现开发好的handler与xsd还没法让应用感知到,就这样放上去是没法把前面做的工作纳入体系中的,spring提供了 spring.handlers和spring.schemas这两个配置文件来完成这项工作,这两个文件需要我们自己编写并放入META-INF文件夹 中,这两个文件的地址必须是META-INF/spring.handlers和META-INF/spring.schemas,spring会默认去 载入它们
在Bean文件中应用
这里就是用ClassPathXmlApplicationContext读取Spring-provider.xml注入bean到Spring容器,然后在Service中通过@Resource或@Autowired注解拿到Bean使用。
更多推荐
所有评论(0)