Skip to content

set target type property for reference bean definition#5710

Merged
mercyblitz merged 1 commit intoapache:masterfrom
mooseen:master
Apr 24, 2020
Merged

set target type property for reference bean definition#5710
mercyblitz merged 1 commit intoapache:masterfrom
mooseen:master

Conversation

@mooseen
Copy link
Copy Markdown
Contributor

@mooseen mooseen commented Feb 4, 2020

What is the purpose of the change

Set target type property of reference bean definition can avoid reference bean early initialization
when define reference bean in xml.
The reason is spring will try to create factory bean instance to look what factory bean create if spring can't get target type from bean definition.

Brief changelog

set target type property for reference bean definition when parse dubbo bean definition.

@codecov-io
Copy link
Copy Markdown

Codecov Report

Merging #5710 into master will decrease coverage by 0.04%.
The diff coverage is 83.33%.

Impacted file tree graph

@@             Coverage Diff              @@
##             master    #5710      +/-   ##
============================================
- Coverage     61.42%   61.38%   -0.05%     
  Complexity      491      491              
============================================
  Files           928      928              
  Lines         37997    37954      -43     
  Branches       5483     5452      -31     
============================================
- Hits          23341    23297      -44     
+ Misses        12129    12125       -4     
- Partials       2527     2532       +5
Impacted Files Coverage Δ Complexity Δ
...onfig/spring/schema/DubboBeanDefinitionParser.java 65.91% <83.33%> (+0.32%) 0 <0> (ø) ⬇️
...ache/dubbo/remoting/transport/AbstractChannel.java 75% <0%> (-12.5%) 0% <0%> (ø)
...va/org/apache/dubbo/remoting/TimeoutException.java 22.22% <0%> (-11.12%) 0% <0%> (ø)
.../remoting/transport/netty4/NettyServerHandler.java 59.09% <0%> (-9.1%) 0% <0%> (ø)
.../apache/dubbo/rpc/protocol/AsyncToSyncInvoker.java 70.83% <0%> (-8.34%) 0% <0%> (ø)
...mmon/threadpool/support/fixed/FixedThreadPool.java 80% <0%> (-7.5%) 0% <0%> (ø)
...moting/transport/netty4/NettyEventLoopFactory.java 30% <0%> (-6.37%) 0% <0%> (ø)
...dubbo/remoting/exchange/support/DefaultFuture.java 76.63% <0%> (-6.25%) 0% <0%> (ø)
.../threadpool/support/limited/LimitedThreadPool.java 83.33% <0%> (-5.56%) 0% <0%> (ø)
...ng/exchange/support/header/HeartbeatTimerTask.java 73.68% <0%> (-5.27%) 0% <0%> (ø)
... and 27 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update ce8b6e8...a984998. Read the comment docs.

@hengyunabc
Copy link
Copy Markdown
Contributor

这个commit会导致 sample失败:


在 2.7.5 里,会先执行spring 会 DubboBootstrap.start() ,然后会exporet services,然后会初始化InjvmProtocol ,再init ReferenceConfig 。

但在这个commit之后,spring直接执行了 init ReferenceConfig,然后因为没有 初始化 InjvmProtocol,然后找不到 provider 就会失败。


初步分析,原因可能是:

  1. 原来 2.7.5 里的逻辑是先执行 DubboBootstrap.start() ,里面会 exportServices。这样子是可以正常工作的。

  2. 增加了那段设置 BeanDefinition.setTargetType() 代码之后,spring会先初始化 ReferenceConfig ,然后因为没有执行DubboBootstrap.start() ,没有exportServices,然后找不到 injvm 的 provider ,调用失败。

  3. spring 对于 factory bean有一些神奇的逻辑,在找不到具体的 target type时,会在后面才初始化这个bean。所以在 2.7.5版本里,ReferenceConfig的初始化是比较靠后的。 在 2.7.5 里,估计提前设置好 target type也会有问题。

  4. 这个commit的本意是没问题的,可能 dubbo的整个spring生命周期要重新梳理下

hengyunabc added a commit that referenced this pull request May 11, 2020
@mooseen
Copy link
Copy Markdown
Contributor Author

mooseen commented May 11, 2020

这个commit会导致 sample失败:

在 2.7.5 里,会先执行spring 会 DubboBootstrap.start() ,然后会exporet services,然后会初始化InjvmProtocol ,再init ReferenceConfig 。

但在这个commit之后,spring直接执行了 init ReferenceConfig,然后因为没有 初始化 InjvmProtocol,然后找不到 provider 就会失败。

初步分析,原因可能是:

  1. 原来 2.7.5 里的逻辑是先执行 DubboBootstrap.start() ,里面会 exportServices。这样子是可以正常工作的。
  2. 增加了那段设置 BeanDefinition.setTargetType() 代码之后,spring会先初始化 ReferenceConfig ,然后因为没有执行DubboBootstrap.start() ,没有exportServices,然后找不到 injvm 的 provider ,调用失败。
  3. spring 对于 factory bean有一些神奇的逻辑,在找不到具体的 target type时,会在后面才初始化这个bean。所以在 2.7.5版本里,ReferenceConfig的初始化是比较靠后的。 在 2.7.5 里,估计提前设置好 target type也会有问题。
  4. 这个commit的本意是没问题的,可能 dubbo的整个spring生命周期要重新梳理下

嗯,排查了一下,发现ReferenceBean提前初始化的问题是由于当设置TargetType后,会导致Spring在判断是否为FactoryBean为false,从而执行getBean方法,spring 关键代码:

if (isFactoryBean(beanName)) {
	final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
	boolean isEagerInit;
	if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
		isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
			@Override
			public Boolean run() {
				return ((SmartFactoryBean<?>) factory).isEagerInit();
			}
		}, getAccessControlContext());
	}
	else {
		isEagerInit = (factory instanceof SmartFactoryBean &&
				((SmartFactoryBean<?>) factory).isEagerInit());
	}
	if (isEagerInit) {
		getBean(beanName);
	}
}
else {
	getBean(beanName);
}				

而在这个commit之前,由于没有设置TargetType,在Spring中会使用BeanDefinition的BeanClass作为TargetType类型,关键代码

protected Class<?> determineTargetType(String beanName, RootBeanDefinition mbd, Class<?>... typesToMatch) {
	Class<?> targetType = mbd.getTargetType();
	if (targetType == null) {
		targetType = (mbd.getFactoryMethodName() != null ?
				getTypeForFactoryMethod(beanName, mbd, typesToMatch) :
                                 //  解析TagetType
				resolveBeanClass(mbd, beanName, typesToMatch));
		if (ObjectUtils.isEmpty(typesToMatch) || getTempClassLoader() == null) {
			mbd.resolvedTargetType = targetType;
		}
	}
	return targetType;
}

protected Class<?> resolveBeanClass(final RootBeanDefinition mbd, String beanName, final Class<?>... typesToMatch)
			throws CannotLoadBeanClassException {
	try {
		if (mbd.hasBeanClass()) {
		    // 直接获取BeanClass作为TargetType
			return mbd.getBeanClass();
		}
		//  more
	}catch(Exception ex){
	    //  catch exception
	}
}

因此当不设置TargetType时,由于ReferenceBean的BeanClass就是ReferenceBean本身,所以第一步的isFactoryBean判断为true,且后续的isEagerInit为false,因而不会调用getBean初始化ReferenceBean。

当前想到的解决方法是将ReferenceBean的LazyInit设置为true,亲测有效。但还不确定是否有其他坑。

@CodeIngL
Copy link
Copy Markdown

CodeIngL commented Jul 7, 2020

RefernceBean本质上就是FactoryBean,你给他设定tagertType,逻辑上就不是FactoryBean,完全破坏了spring中本身结构,现在看解决方法是加lazy。是为了解决而解决么。从根源上就错了。

@Da-Hong
Copy link
Copy Markdown

Da-Hong commented Jul 7, 2020

按照这样改了之后

  1. 项目用纯xml方式配置,启动日志还是有警告
    [DUBBO] Expected single matching of application, but found 2 instances, will randomly pick the first one.
    断点之后发现多出来的application=org.apache.dubbo.config.ApplicationConfig#0
  2. 用dubbo.properties配置启动失败
    Invalid name="org.apache.dubbo.config.ApplicationConfig#0" contains illegal character, only digit, letter, '-', '_' or '.' is legal.

@CodeIngL
Copy link
Copy Markdown

CodeIngL commented Jul 7, 2020

这样改是错的,本质上没有解决问题

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants