New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
issue: Component scan two times for @Service #7477
Conversation
Repeated scan should be a problem in the implementation logic. There are two ways to register a scanner, one is to annotate |
Please check @kylixs 's comments. |
My project uses ServiceClassPostProcessor and only scans once for Component's(DubboService, org.apache.dubbo.config.annotation.Service, com.alibaba.dubbo.config.annotation.Service). org.apache.dubbo.config.spring.beans.factory.annotation.ServiceClassPostProcessor is used to run org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.findCandidateComponents(String basePackage) twice: |
Duplicated scanning is resolved in Dubbo 3 : Line 162 in bdb2d31
|
First, avoid scan duplicated packages or sub-package:
Then, exclude scanned classes before generate BeanDefinitions:
|
Oh, you're right, I miss it. The first scan is to register the bean definition of the service implementation class, and the second scan is to register the bean definition of the dubbo ServiceBean later. So, I think the second scan is unnecessary, just use the results of the first scan directly. Some thing like this: private void scanServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
...
for (String packageToScan : packagesToScan) {
// avoid duplicated scans
if (servicePackagesHolder.isPackageScanned(packageToScan)) {
if (logger.isInfoEnabled()) {
logger.info("Ignore package who has already bean scanned: " + packageToScan);
}
continue;
}
// Scan and registers as Spring @Service Bean first
Set<BeanDefinitionHolder> beanDefinitionHolders = scanner.doScan(packageToScan);
// Second, register bean definition of dubbo ServiceBean for each service
if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
...
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
processScannedBeanDefinition(beanDefinitionHolder, registry, scanner);
servicePackagesHolder.addScannedClass(beanDefinitionHolder.getBeanDefinition().getBeanClassName());
}
} else {
if (logger.isWarnEnabled()) {
logger.warn("No class annotated by Dubbo @Service was found under package ["
+ packageToScan + "], ignore re-scanned classes: " + scanExcludeFilter.getExcludedCount());
}
}
servicePackagesHolder.addScannedPackage(packageToScan);
}
} However, if the |
public Set<BeanDefinitionHolder> doScan(BeanDefinitionRegistry registry, BeanNameGenerator beanNameGenerator, String basePackage) { | ||
// Registers @Service Bean first | ||
super.doScan(basePackage); | ||
Set<BeanDefinition> beanDefinitions = findCandidateComponents(basePackage); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
super.doScan(basePackage)
: will do first scanning
findCandidateComponents()
: will do second scanning
This did not reduce scanning.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
super.doScan(basePackage): Call method findCandidateComponents() for first time run, this method will cache scan result.
findCandidateComponents(basePackage): this method run for the second time and will use cached results instead of scanning.
So this reduce scanning for the second time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I rewrite method findCandidateComponents for DubboClassPathBeanDefinitionScanner.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think just cache Set<BeanDefinitionHolder>
in findCandidateComponents
method is enough, do not need add new doScan
method
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Solve the cause of the problem
public Set<BeanDefinitionHolder> doScan(BeanDefinitionRegistry registry, BeanNameGenerator beanNameGenerator, String basePackage) { | ||
// Registers @Service Bean first | ||
super.doScan(basePackage); | ||
Set<BeanDefinition> beanDefinitions = findCandidateComponents(basePackage); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I rewrite method findCandidateComponents for DubboClassPathBeanDefinitionScanner.
Another way is to cache the public class DubboClassPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {
private Set<BeanDefinitionHolder> beanDefinitionHolders = new LinkedHashSet<>();
....
@Override
protected void postProcessBeanDefinition(AbstractBeanDefinition beanDefinition, String beanName) {
beanDefinitionHolders.add(new BeanDefinitionHolder(beanDefinition, beanName));
super.postProcessBeanDefinition(beanDefinition, beanName);
}
public Set<BeanDefinitionHolder> getBeanDefinitionHolders() {
return beanDefinitionHolders;
}
} // ServiceAnnotationPostProcessor private void scanServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
...
for (String packageToScan : packagesToScan) {
...
// Scan and registers as Spring @Service Bean first
// if the service class is already scanned and registered by @ComponentScan, the bean definitions will be ignored.
scanner.scan(packageToScan);
Set<BeanDefinitionHolder> beanDefinitionHolders = scanner.getBeanDefinitionHolders();
// Second, register bean definition of dubbo ServiceBean for each service
if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
....
....
} |
Yes, you're right.👍 |
@zhangjiezhang Please improve it and resubmit pr. |
Which branch to modify? |
Create two pull requests separately based on master and 3.0 branches, please use concise code whenever possible. |
Add cache to solve this issue