这篇文章主要介绍“怎么用Dubbo与Spring整合解析配置文件”,在日常操作中,相信很多人在怎么用Dubbo与Spring整合解析配置文件问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么用Dubbo与Spring整合解析配置文件”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
本专栏分析的Dubbo源码是基于2.6.x版本
public class Consumer { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-consumer.xml"}); context.start(); DemoService demoService = (DemoService) context.getBean("demoService"); while (true) { try { Thread.sleep(1000); String hello = demoService.sayHello("world"); System.out.println(hello); } catch (Throwable throwable) { throwable.printStackTrace(); } } } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> <dubbo:application name="demo-consumer"/> <dubbo:registry address="zookeeper://127.0.0.1:2181"/> <dubbo:reference id="demoService" check="false" interface="com.alibaba.dubbo.demo.DemoService"/> </beans>
public class Provider { public static void main(String[] args) throws Exception { System.setProperty("java.net.preferIPv4Stack", "true"); ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-provider.xml"}); context.start(); System.in.read(); } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> <dubbo:application name="demo-provider"/> <dubbo:registry address="zookeeper://127.0.0.1:2181"/> <dubbo:protocol name="dubbo" port="20880"/> <bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl"/> <dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService"/> </beans>
先启动服务提供者,再启动消费者,发现控制台可以正常输出。下面分析一下Spring是如何解析dubbo的消费者和服务提供者的配置文件。Spring容器提供了IOC功能,可以替我们生成bean。通常,我们将bean的定义放在xml文件中,我们来分析一下Spring加载xml配置文件并生成bean过程。Spring提供的容器分为两种:BeanFactory和ApplicationContext
。其中BeanFactory是懒加载,也就是延迟初始化,它在你调用getBean时才会初始化这个bean,而ApplicationContext是初始化容器时就会加载非延迟初始化的bean
。先简单概况下Spring容器生成bean的过程,首先通过loadBeanDefinition过程将bean的信息封装成一个个BeanDefinition,然后再根据这些BeanDefinition创建bean。下面看Spring解析Dubbo的配置文件并生成bean的过程。
// 1、new ClassPathXmlApplicationContext时Spring容器初始化,此时会先调用loadBeanDefinition方法去加载解析xml配置文件 context = new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-provider.xml"});
// 2、加载配置文件最终会走到这里 protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); // 3、这里其实已经通过dom4j将xml文件解析成了Document,将xml中的一项一项配置解析成了一个个Node去读取处理. for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; // 4、判断是否是Spring默认可以处理的Node.这里看下面截图,由于dubbo:application, // 是dubbo中定义的,不属于Spring的命名空间管理 if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } } } }
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { // http://dubbo.apache.org/schema/dubbo String namespaceUri = getNamespaceURI(ele); // DubboNameSpaceHandler NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler == null) { return null; } return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); }
private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) { RootBeanDefinition beanDefinition = new RootBeanDefinition(); // class com.alibaba.dubbo.config.ApplicationConfig beanDefinition.setBeanClass(beanClass); beanDefinition.setLazyInit(false); // 解析id属性 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++); } } if (id != null && id.length() > 0) { if (parserContext.getRegistry().containsBeanDefinition(id)) { throw new IllegalStateException("Duplicate spring bean id " + id); } // 注册BeanDefinition parserContext.getRegistry().registerBeanDefinition(id, beanDefinition); // 将id属性放入beanDefinition中,后续getBean创建bean时就是根据这些属性来创建bean, // 这里创建的bean是ApplicationConfig beanDefinition.getPropertyValues().addPropertyValue("id", id); } // 删去一些代码,reference是解析得到的value值,可见这里将属性和属性值都放入了BeanDefinition beanDefinition.getPropertyValues().addPropertyValue(property, reference); return beanDefinition; }
到这里就解析完了,Spring将xml中的application节点解析成一个BeanDefinition,并注册到Registry中,Registry就是一个Map。下面分析Spring创建这个ApplicationConfig的过程。
context = new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-provider.xml"});
// Spring容器的初始化过程,new ClassPathXmlApplicationContext后会走到这里 public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { prepareRefresh(); // 这里面就会执行上面的分析过程,调用loadBeanDefinition解析BeanDefinition ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); prepareBeanFactory(beanFactory); try { postProcessBeanFactory(beanFactory); invokeBeanFactoryPostProcessors(beanFactory); registerBeanPostProcessors(beanFactory); initMessageSource(); initApplicationEventMulticaster(); onRefresh(); registerListeners(); // Instantiate all remaining (non-lazy-init) singletons.可以看到Spring容器初始化 // 的后面会初始化非延迟加载的bean,这里会走到下图的preInstantiasteSingletons方法 finishBeanFactoryInitialization(beanFactory); finishRefresh(); } } }
// Spring创建bean最终会走到这里 protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) throws BeanCreationException { BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { // 删除一些无用代码,这里会调用反射创建bean,创建完仅是一个空的bean,属性还没有赋值 instanceWrapper = createBeanInstance(beanName, mbd, args); } final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null); Object exposedObject = bean; try { // 属性赋值,最终也是调用反射进行赋值 populateBean(beanName, mbd, instanceWrapper); if (exposedObject != null) { exposedObject = initializeBean(beanName, exposedObject, mbd); } } return exposedObject; }
protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) { // 这里的pvs就是之前解析配置文件得到BeanDefinition时,给BeanDefinition注入进去的 PropertyValues pvs = mbd.getPropertyValues(); // 删除一些代码,最终这里会调用反射赋值,跳来跳去有点复杂 applyPropertyValues(beanName, mbd, bw, pvs); }
protected void addSingleton(String beanName, Object singletonObject) { // 最终创建完bean以后会将它保存起来(猜测,Spring容器初始化以后,非懒加载的bean已经以如下方式 // 保存到Spring容器中了,后续通过@Autowired注解)来获取时就是从这里面获取,只是分析,还没有看源码) synchronized (this.singletonObjects) { this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT)); this.singletonFactories.remove(beanName); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } }
到此,关于“怎么用Dubbo与Spring整合解析配置文件”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。