dubbo中有许多和SpringMVC相关的操作,这篇文章来探讨一下dubbo是如何利用SpringMVC的依赖注入机制完成bean的注入的。
首先我们知道,依赖注入是通过在配置文件中指定component-scan来确定的:
<context:component-scan base-package="com.xxxx.xxxx"/>
而SpringMVC里最终通过ContextNamespaceHandler这个类去注册了一个parse用来扫描“component-scan”。
public class ContextNamespaceHandler extends NamespaceHandlerSupport { public ContextNamespaceHandler() { } public void init() { this.registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser()); this.registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser()); this.registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser()); this.registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser()); this.registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser()); this.registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser()); this.registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser()); this.registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser()); } }
可以看到,通过ComponentScanBeanDefinitionParser这个类去解析。
public BeanDefinition parse(Element element, ParserContext parserContext) { String[] basePackages = StringUtils.tokenizeToStringArray(element.getAttribute("base-package"), ",; /t/n"); ClassPathBeanDefinitionScanner scanner = this.configureScanner(parserContext, element); Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages); this.registerComponents(parserContext.getReaderContext(), beanDefinitions, element); return null; }
可以看到它的parse方法,通过定义一个scanner去完成。这个scanner是ClassPathBeanDefinitionScanner。
protected Set<BeanDefinitionHolder> doScan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet(); String[] var3 = basePackages; int var4 = basePackages.length; for(int var5 = 0; var5 < var4; ++var5) { String basePackage = var3[var5]; Set<BeanDefinition> candidates = this.findCandidateComponents(basePackage); Iterator var8 = candidates.iterator(); while(var8.hasNext()) { BeanDefinition candidate = (BeanDefinition)var8.next(); ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); if(candidate instanceof AbstractBeanDefinition) { this.postProcessBeanDefinition((AbstractBeanDefinition)candidate, beanName); } if(candidate instanceof AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition)candidate); } if(this.checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); this.registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; }
我们看它的doScan方法。通过调用findCandidateComponents方法去扫描basePackage下的所有需要注入的类,转换成BeanDefinition。
public Set<BeanDefinition> findCandidateComponents(String basePackage) { LinkedHashSet candidates = new LinkedHashSet(); try { String packageSearchPath = "classpath*:" + this.resolveBasePackage(basePackage) + "/" + this.resourcePattern; Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath); boolean traceEnabled = this.logger.isTraceEnabled(); boolean debugEnabled = this.logger.isDebugEnabled(); Resource[] var7 = resources; int var8 = resources.length; for(int var9 = 0; var9 < var8; ++var9) { Resource resource = var7[var9]; if(traceEnabled) { this.logger.trace("Scanning " + resource); } if(resource.isReadable()) { try { MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource); if(this.isCandidateComponent(metadataReader)) { ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setResource(resource); sbd.setSource(resource); if(this.isCandidateComponent((AnnotatedBeanDefinition)sbd)) { if(debugEnabled) { this.logger.debug("Identified candidate component class: " + resource); } candidates.add(sbd); } else if(debugEnabled) { this.logger.debug("Ignored because not a concrete top-level class: " + resource); } } else if(traceEnabled) { this.logger.trace("Ignored because not matching any filter: " + resource); } } catch (Throwable var13) { throw new BeanDefinitionStoreException("Failed to read candidate component class: " + resource, var13); } } else if(traceEnabled) { this.logger.trace("Ignored because not readable: " + resource); } } return candidates; } catch (IOException var14) { throw new BeanDefinitionStoreException("I/O failure during classpath scanning", var14); } }
findCandidateComponents方法比较长。
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
其中这一句,就将basePackage下的类转换成了BeanDefinition。
至此,我们知道了SpringMVC是如何去扫描配置文件中的注入类的——通过定义对应的parser,将element转换成BeanDefinition。
dubbo中当然也有自己的parser:
public class DubboNamespaceHandler extends NamespaceHandlerSupport { public DubboNamespaceHandler() { } public void init() { this.registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true)); this.registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true)); this.registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true)); this.registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true)); this.registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true)); this.registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true)); this.registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true)); this.registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true)); this.registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false)); this.registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true)); } static { Version.checkDuplicate(DubboNamespaceHandler.class); } }
可以看到,所有的节点都有自己的parser,将其进行转换。
上面的流程是将配置文件进行解析并转换保存,那么具体的注入是什么时候呢?
在SpringMVC的AbstractApplicationContext中的refresh方法里,SpringMVC会遍历之前注册的所有的BeanDefinition,并判断是否是懒加载的,如果不是,则调用getBean方法去初始化。最终会调用到AbstractAutowireCapableBeanFactory的doCreateBean方法。
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, Object[] args) { BeanWrapper instanceWrapper = null; if(mbd.isSingleton()) { instanceWrapper = (BeanWrapper)this.factoryBeanInstanceCache.remove(beanName); } if(instanceWrapper == null) { instanceWrapper = this.createBeanInstance(beanName, mbd, args); } final Object bean = instanceWrapper != null?instanceWrapper.getWrappedInstance():null; Class<?> beanType = instanceWrapper != null?instanceWrapper.getWrappedClass():null; Object var7 = mbd.postProcessingLock; synchronized(mbd.postProcessingLock) { if(!mbd.postProcessed) { this.applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); mbd.postProcessed = true; } } boolean earlySingletonExposure = mbd.isSingleton() && this.allowCircularReferences && this.isSingletonCurrentlyInCreation(beanName); if(earlySingletonExposure) { if(this.logger.isDebugEnabled()) { this.logger.debug("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } this.addSingletonFactory(beanName, new ObjectFactory<Object>() { public Object getObject() throws BeansException { return AbstractAutowireCapableBeanFactory.this.getEarlyBeanReference(beanName, mbd, bean); } }); } Object exposedObject = bean; try { this.populateBean(beanName, mbd, instanceWrapper); if(exposedObject != null) { exposedObject = this.initializeBean(beanName, exposedObject, mbd); } } catch (Throwable var17) { if(var17 instanceof BeanCreationException && beanName.equals(((BeanCreationException)var17).getBeanName())) { throw (BeanCreationException)var17; } throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", var17); } if(earlySingletonExposure) { Object earlySingletonReference = this.getSingleton(beanName, false); if(earlySingletonReference != null) { if(exposedObject == bean) { exposedObject = earlySingletonReference; } else if(!this.allowRawInjectionDespiteWrapping && this.hasDependentBean(beanName)) { String[] dependentBeans = this.getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet(dependentBeans.length); String[] var12 = dependentBeans; int var13 = dependentBeans.length; for(int var14 = 0; var14 < var13; ++var14) { String dependentBean = var12[var14]; if(!this.removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } if(!actualDependentBeans.isEmpty()) { throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been " + "wrapped. This means that said other beans do not use the final version of the " + "bean. This is often the result of over-eager type matching - consider using " + "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example."); } } } } try { this.registerDisposableBeanIfNecessary(beanName, bean, mbd); return exposedObject; } catch (BeanDefinitionValidationException var16) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", var16); } }
方法很长,我们先看这一句:
instanceWrapper = this.createBeanInstance(beanName, mbd, args);
创建一个wrapper,这里如果我们打断点就会发现,对于dubbo的bean来说,如果是引用的服务,最终创建出来的就是一个ReferenceBean了。
下面再来看:
this.populateBean(beanName, mbd, instanceWrapper);
在这个方法中,SpringMVC遍历注册的BeanPostProcessor,并调用其postProcessPropertyValues方法将已经生成的wrapper注入其中。
public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException { InjectionMetadata metadata = this.findAutowiringMetadata(beanName, bean.getClass()); try { metadata.inject(bean, beanName, pvs); return pvs; } catch (Throwable var7) { throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", var7); } }
调用inject进行真正的注入。
前面我们说过,对于dubbo来说,每一个服务都会变成一个ReferenceBean,而ReferenceBean是实现了InitializingBean接口的,会在对应的afterPropertiesSet方法里进行初始化。
以上就是dubbo中的SpringMVC依赖注入,整套流程比较规范,通过定义parse解析自己的node,然后在对应的bean中通过实现InitializingBean接口在afterPropertiesSet进行初始化操作,一般的框架想要整合SpringMVC,都会这么做。