This is the second day of my participation in the August Text Challenge.More challenges in August
>>>> 😜😜😜 Github: 👉 github.com/black-ant CASE Backup: 👉 gitee.com/antblack/ca…
A. The preface
First see summary more efficient!! 🎁 🎁 🎁
This article will take a look at how classes with the @dubboService annotation are scanned and used. The scanning logic is as follows:
// Dubbo uses PostProcessor to perform secondary processing during initialization
// Dubbo automatic configuration class
C- DubboAutoConfiguration
// A post-processor class that uses AbstractConfig#getId() to alias the Dubbo Config bean
C- DubboConfigAliasPostProcessor
// A BeanFactoryPostProcessor for handling @Service annotation classes and annotation beans in Java configuration classes
// It is also the base class for the XML BeanDefinitionParser on Dubbbo: Annotation
C- ServiceAnnotationPostProcessor
// Register some non-existent infrastructure beans
C- DubboInfraBeanRegisterPostProcessor
Copy the code
DubboServicer load
2.1 Basic Cases
@DubboService
public class DemoServiceImpl implements DemoService {
private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);
@Override
public String sayHello(String name) {
logger.info("Hello " + name + ", request from consumer: " + RpcContext.getServiceContext().getRemoteAddress());
return "Hello " + name + ", response from provider: " + RpcContext.getServiceContext().getLocalAddress();
}
@Override
public CompletableFuture<String> sayHelloAsync(String name) {
return null; }}Copy the code
2.2 Entry of scanning
Scanning is also handled BeanPostProcess of Dubbo, main processing class for ServiceAnnotationPostProcessor, when SpringBoot loading, Via refresh invokeBeanFactoryPostProcessors logic processing
/ / a scan - ServiceAnnotationPostProcessor C
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
this.registry = registry;
// Step 1: Obtain the scanned Package path
Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);
if(! CollectionUtils.isEmpty(resolvedPackagesToScan)) {// Step 2: Scan for services in the path
scanServiceBeans(resolvedPackagesToScan, registry);
} else{}}Copy the code
Supplement: postProcessBeanDefinitionRegistry method
The only way to postProcessBeanDefinitionRegistry method is BeanDefinitionRegistryPostProcessor interface, This method can be customized with the BeanDefinitionRegistry registration Bean.
2.3 the Bean’s scan (ServiceAnnotationPostProcessor)
// Step 2-1: Scan the Bean
private void scanServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
// PRO0001
DubboClassPathBeanDefinitionScanner scanner =
new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
// PRO0002
BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
scanner.setBeanNameGenerator(beanNameGenerator);
// PRO0003
for (Class<? extends Annotation> annotationType : serviceAnnotationTypes) {
scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
}
ScanExcludeFilter scanExcludeFilter = new ScanExcludeFilter();
scanner.addExcludeFilter(scanExcludeFilter);
for (String packageToScan : packagesToScan) {
// Avoid duplicate scans -> PRO0004
if (servicePackagesHolder.isPackageScanned(packageToScan)) {
continue;
}
// Scan all @service annotations (Spring)?
scanner.scan(packageToScan);
// Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.
Set<BeanDefinitionHolder> beanDefinitionHolders =
findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
if(! CollectionUtils.isEmpty(beanDefinitionHolders)) {// The serviceClasses is used to print the Service details for log processing
// PS: means that the detailed Service class can be printed by configuring this property here
if (logger.isInfoEnabled()) {
List<String> serviceClasses = new ArrayList<>(beanDefinitionHolders.size());
for(BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) { serviceClasses.add(beanDefinitionHolder.getBeanDefinition().getBeanClassName()); }}for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
// -> 2.4 BeanDefinition processing
processScannedBeanDefinition(beanDefinitionHolder, registry, scanner);
// Add and load beansservicePackagesHolder.addScannedClass(beanDefinitionHolder.getBeanDefinition().getBeanClassName()); }}else{}// Add the scanned packageservicePackagesHolder.addScannedPackage(packageToScan); }}Copy the code
PRO0001 complement 1: DubboClassPathBeanDefinitionScanner footprint
Core: DubboClassPathBeanDefinitionScanner inheritance in ClassPathBeanDefinitionScanner, on the basis of the added attributes:private final ConcurrentMap<String, Set<BeanDefinition>> beanDefinitionMap = newConcurrentHashMap<>(); The parent class findCandidateComponents is cached in this class when it is called.Copy the code
PRO0002 Supplement 2: The function of the resolveBeanNameGenerator
String CONFIGURATION_BEAN_NAME_GENERATOR = "org.springframework.context.annotation.internalConfigurationBeanNameGenerator"
private BeanNameGenerator resolveBeanNameGenerator(BeanDefinitionRegistry registry) {
BeanNameGenerator beanNameGenerator = null;
if (registry instanceof SingletonBeanRegistry) {
SingletonBeanRegistry singletonBeanRegistry = SingletonBeanRegistry.class.cast(registry);
beanNameGenerator = (BeanNameGenerator) singletonBeanRegistry.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
}
if (beanNameGenerator == null) {
beanNameGenerator = new AnnotationBeanNameGenerator();
}
return beanNameGenerator;
}
Copy the code
AnnotationTypeFilter: AnnotationTypeFilter
private final static List<Class<? extends Annotation>> serviceAnnotationTypes = asList(
/ / @ since 2.7.7 Add the @ DubboService, the issue: https://github.com/apache/dubbo/issues/6007
DubboService.class,
/ / @ since 2.7.0 the substitute @ com. Alibaba. Dubbo. Config. The annotation. Service
Service.class,
// @since 2.7.3 Add the compatibility for Legacy Dubbo's @service, the issue: https://github.com/apache/dubbo/issues/4330com.alibaba.dubbo.config.annotation.Service.class ); This is basically DubboService, down here2The official Service annotation is outdatedCopy the code
Add 4: The role of the ServicePackagesHolder
public class ServicePackagesHolder {
// As you can see, there are mainly two sets of sets to hold the loaded class and package
public static final String BEAN_NAME = "dubboServicePackagesHolder";
private final Set<String> scannedPackages = new HashSet<>();
private final Set<String> scannedClasses = new HashSet<>();
}
Copy the code
Add five: findServiceBeanDefinitionHolders processing Service
private Set<BeanDefinitionHolder> findServiceBeanDefinitionHolders( ClassPathBeanDefinitionScanner scanner, String packageToScan, BeanDefinitionRegistry registry, BeanNameGenerator beanNameGenerator) {
// Scan beans, which actually rely on Spring's architecture
Set<BeanDefinition> beanDefinitions = scanner.findCandidateComponents(packageToScan);
Set<BeanDefinitionHolder> beanDefinitionHolders = new LinkedHashSet<>(beanDefinitions.size());
/ / all of the Service class, the type of the org. Springframework. Context. The annotation. ScannedGenericBeanDefinition
for (BeanDefinition beanDefinition : beanDefinitions) {
String beanName = beanNameGenerator.generateBeanName(beanDefinition, registry);
BeanDefinitionHolder beanDefinitionHolder = new BeanDefinitionHolder(beanDefinition, beanName);
beanDefinitionHolders.add(beanDefinitionHolder);
}
return beanDefinitionHolders;
}
Copy the code
2.4 Processing of BeanDefinition
BeanDefinitionHolder is bean-handled earlier, and we saw that there are two sets of sets in this object
/ / load BeanDefinition main process C - ServiceAnnotationPostProcessor
private void processScannedBeanDefinition(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry, DubboClassPathBeanDefinitionScanner scanner) { Class<? > beanClass = resolveClass(beanDefinitionHolder);// Get Bean annotations and attributes
Annotation service = findServiceAnnotation(beanClass);
Map<String, Object> serviceAnnotationAttributes = AnnotationUtils.getAttributes(service, true);
// Return the interface classClass<? > interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass); String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();// ServiceBean Bean name
String beanName = generateServiceBeanName(serviceAnnotationAttributes, interfaceClass);
AbstractBeanDefinition serviceBeanDefinition =
buildServiceBeanDefinition(serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);
registerServiceBeanDefinition(beanName, serviceBeanDefinition, interfaceClass);
}
Copy the code
Step 1: Parse the interface name
This is attempted from three attributes: interfaceName/interfaceClass/Class
// Resolve the Service interface name from the @service annotation attribute
// Note: If it is a generic service, the service interface class may not be found locally
public static String resolveInterfaceName(Map
attributes, Class
defaultInterfaceClass)
,> {
Boolean generic = getAttribute(attributes, "generic");
// 1. get from DubboService.interfaceName()
String interfaceClassName = getAttribute(attributes, "interfaceName");
if (StringUtils.hasText(interfaceClassName)) {
if (GenericService.class.getName().equals(interfaceClassName) ||
com.alibaba.dubbo.rpc.service.GenericService.class.getName().equals(interfaceClassName)) {
throw newIllegalStateException... ; }return interfaceClassName;
}
// 2. get from DubboService.interfaceClass()Class<? > interfaceClass = getAttribute(attributes,"interfaceClass");
if (interfaceClass == null || void.class.equals(interfaceClass)) { // default or set void.class for purpose.
interfaceClass = null;
} else if (GenericService.class.isAssignableFrom(interfaceClass)) {
throw newIllegalStateException.... ; }// 3. get from annotation element type, ignore GenericService
if (interfaceClass == null&& defaultInterfaceClass ! =null && !GenericService.class.isAssignableFrom(defaultInterfaceClass)) {
/ / to get here for interface org. Apache. Dubbo. Demo. DemoServiceClass<? >[] allInterfaces = getAllInterfacesForClass(defaultInterfaceClass);if (allInterfaces.length > 0) {
interfaceClass = allInterfaces[0]; }}return interfaceClass.getName();
}
Copy the code
Step 2: build buildServiceBeanDefinition
// C- ServiceAnnotationPostProcessor
private AbstractBeanDefinition buildServiceBeanDefinition(Map
serviceAnnotationAttributes, Class
interfaceClass, String refServiceBeanName)
,> {
BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class);
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
String[] ignoreAttributeNames = of("provider"."monitor"."application"."module"."registry"."protocol"."interface"."interfaceName"."parameters");
propertyValues.addPropertyValues(new AnnotationPropertyValuesAdapter(serviceAnnotationAttributes, environment, ignoreAttributeNames));
//set config id, for ConfigManager cache key
//builder.addPropertyValue("id", beanName);
// References "ref" property to annotated-@Service Bean
addPropertyReference(builder, "ref", refServiceBeanName);
// Set interface
builder.addPropertyValue("interface", interfaceClass.getName());
// Convert parameters into map
builder.addPropertyValue("parameters", convertParameters((String[]) serviceAnnotationAttributes.get("parameters")));
// Add methods parameters
List<MethodConfig> methodConfigs = convertMethodConfigs(serviceAnnotationAttributes.get("methods"));
if(! methodConfigs.isEmpty()) { builder.addPropertyValue("methods", methodConfigs);
}
// convert provider to providerIds
String providerConfigId = (String) serviceAnnotationAttributes.get("provider");
if (StringUtils.hasText(providerConfigId)) {
addPropertyValue(builder, "providerIds", providerConfigId);
}
// Convert registry[] to registryIds
String[] registryConfigIds = (String[]) serviceAnnotationAttributes.get("registry");
if(registryConfigIds ! =null && registryConfigIds.length > 0) {
resolveStringArray(registryConfigIds);
builder.addPropertyValue("registryIds", StringUtils.join(registryConfigIds, ', '));
}
// Convert protocol[] to protocolIds
String[] protocolConfigIds = (String[]) serviceAnnotationAttributes.get("protocol");
if(protocolConfigIds ! =null && protocolConfigIds.length > 0) {
resolveStringArray(protocolConfigIds);
builder.addPropertyValue("protocolIds", StringUtils.join(protocolConfigIds, ', '));
}
// TODO Could we ignore these attributes: applicatin/monitor/module ? Use global config
// monitor reference
String monitorConfigId = (String) serviceAnnotationAttributes.get("monitor");
if (StringUtils.hasText(monitorConfigId)) {
addPropertyReference(builder, "monitor", monitorConfigId);
}
// application reference
String applicationConfigId = (String) serviceAnnotationAttributes.get("application");
if (StringUtils.hasText(applicationConfigId)) {
addPropertyReference(builder, "application", applicationConfigId);
}
// module reference
String moduleConfigId = (String) serviceAnnotationAttributes.get("module");
if (StringUtils.hasText(moduleConfigId)) {
addPropertyReference(builder, "module", moduleConfigId);
}
return builder.getBeanDefinition();
}
// This step is mainly the processing of attributes, nothing to see
Copy the code
Step 3: Register the Bean
// C- ServiceAnnotationPostProcessor
private void registerServiceBeanDefinition(String serviceBeanName, AbstractBeanDefinition serviceBeanDefinition, Class
interfaceClass) {
// check service bean
if (registry.containsBeanDefinition(serviceBeanName)) {
BeanDefinition existingDefinition = registry.getBeanDefinition(serviceBeanName);
if (existingDefinition.equals(serviceBeanDefinition)) {
// exist equipment bean definition
return;
}
/ /... Omitting exception throw
throw new BeanDefinitionStoreException(....)
}
registry.registerBeanDefinition(serviceBeanName, serviceBeanDefinition);
}
// [PRO] : generateServiceBeanName
private String generateServiceBeanName(Map
serviceAnnotationAttributes, Class
interfaceClass)
,> {
ServiceBeanNameBuilder builder = create(interfaceClass, environment)
.group((String) serviceAnnotationAttributes.get("group"))
.version((String) serviceAnnotationAttributes.get("version"));
return builder.build();
}
/ / PS: attention, here is the name of creating ServiceBean: org. Apache. Dubbo. Demo. DemoService
// This is very important and will be used later
Copy the code
3. The use of BeanDefinition
We have scanned the BeanDefinition above. This time we will take a quick look at how objects built by these scans are registered
Above when generateServiceBeanName generates a ServiceBean: org. Apache. The dubbo. Demo. DemoService Bean, the Bean is very important.
/ / in AbstractApplicationContext # refresh,
public void refresh(a) throws BeansException, IllegalStateException {
// The Bean scan logic
invokeBeanFactoryPostProcessors(beanFactory);
/ /... In the middle of processing
// The Bean's registration logic
finishBeanFactoryInitialization(beanFactory);
}
// Step 1: instantiate all beans
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
// Step 2: Get the Bean
for (String beanName : beanNames) {
/ /...
getBean(beanName)
}
// Step 3: Use -> Next Step
Copy the code
As you can see here, the Bean is already inside
conclusion
Don’t say anything. Look at the picture
In simple terms, after scanning beans, special beans are generated to be incorporated into the container architecture, and then loaded via Spring
With so many frameworks, Dubbo’s use of Spring has reached a very low level, just like looking at Spring source code.
Now that’s using a frame