转载

dubbo中的SpringMVC依赖注入

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,都会这么做。

原文  http://zjutkz.net/2018/04/03/dubbo中的SpringMVC依赖注入/
正文到此结束
Loading...