dubbo中的SpringMVC依赖注入

dubbo中有许多和SpringMVC相关的操作,这篇文章来探讨一下dubbo是如何利用SpringMVC的依赖注入机制完成bean的注入的。

首先我们知道,依赖注入是通过在配置文件中指定component-scan来确定的:

1
<context:component-scan base-package="com.xxxx.xxxx"/>

而SpringMVC里最终通过ContextNamespaceHandler这个类去注册了一个parse用来扫描“component-scan”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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这个类去解析。

1
2
3
4
5
6
7
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。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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方法比较长。

1
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);

其中这一句,就将basePackage下的类转换成了BeanDefinition。

至此,我们知道了SpringMVC是如何去扫描配置文件中的注入类的——通过定义对应的parser,将element转换成BeanDefinition。

dubbo中当然也有自己的parser:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
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);
}
}

方法很长,我们先看这一句:

1
instanceWrapper = this.createBeanInstance(beanName, mbd, args);

创建一个wrapper,这里如果我们打断点就会发现,对于dubbo的bean来说,如果是引用的服务,最终创建出来的就是一个ReferenceBean了。

下面再来看:

1
this.populateBean(beanName, mbd, instanceWrapper);

在这个方法中,SpringMVC遍历注册的BeanPostProcessor,并调用其postProcessPropertyValues方法将已经生成的wrapper注入其中。

1
2
3
4
5
6
7
8
9
10
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,都会这么做。