您的位置:首页 > 汽车 > 新车 > 网页制作基础代码_深圳it外包服务_网站推广优化教程_百度指数电脑端查询

网页制作基础代码_深圳it外包服务_网站推广优化教程_百度指数电脑端查询

2025/1/10 3:21:13 来源:https://blog.csdn.net/ZEUS00456/article/details/144539725  浏览:    关键词:网页制作基础代码_深圳it外包服务_网站推广优化教程_百度指数电脑端查询
网页制作基础代码_深圳it外包服务_网站推广优化教程_百度指数电脑端查询

INDEX

      • §1 接入方式
      • §2 原生注解方式接入
        • 入口 `@EnableDubbo`
        • 扫描 `BeanDefinition` 阶段
        • 生成 `ServiceBean`
        • 服务导出
      • §3 原生 xml 方式接入
        • 入口 `xmlns:dubbo`
        • 扫描 BeanDefinition 阶段
        • 生成 ServiceBean
        • 服务导出

§1 接入方式

dubbo 与 springboot 整合后的接入方式可以概括为 3 种

  • Dubbo api 方式,通过 @Bean 的方式手动完成 dubbo 相关对象的声明
    • ApplicationConfig
    • RegistryConfig
    • ProtocolConfig
    • ServiceBean
  • xml,原始方式
  • 注解,常用方式,使用下述注解,配合
    • 原生 dubbo2:@Service/@Reference
    • 原生 dubbo3:@DubboService/@DubboReference

不管使用哪种方式,整体思路应该是一致的

  • 让 spring 扫描到相关类,注册 BeanDefinition(后简称为 BD)
  • Spring 容器 context.refresh() 阶段通过 BeanDefinition 生成 SpringBean
  • 最后由这些 SpringBean 完成 dubbo 的相关动作

单说服务导出这个事,其核心类是 ServiceBean,dubbo 相关动作即导出,即 export() 方法


§2 原生注解方式接入

入口 @EnableDubbo

dubbo2 原生注解生效的入口其实是 @EnableDubbo

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@EnableDubboConfig
@DubboComponentScan //1
public @interface EnableDubbo {}

其上注解 @DubboComponentScan 导入了 DubboComponentScanRegistrar

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DubboComponentScanRegistrar.class) //1
public @interface DubboComponentScan {}

扫描 BeanDefinition 阶段

DubboComponentScanRegistrar 用于负责 BeanDefinition 级的注册,与本帖讨论话题相关的代码如下

public class DubboComponentScanRegistrar implements ImportBeanDefinitionRegistrar {@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);//1registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);registerCommonBeans(registry);}private void registerServiceAnnotationBeanPostProcessor(Set<String> packagesToScan, BeanDefinitionRegistry registry) {//2BeanDefinitionBuilder builder = rootBeanDefinition(ServiceAnnotationBeanPostProcessor.class);builder.addConstructorArgValue(packagesToScan);builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);}

DubboComponentScanRegistrar 注册了一个 ServiceAnnotationBeanPostProcessor,可以看到它是 BeanFactoryPostProcessor 的实现
其关键方法 registerServiceBeans 就是字面意思的功能,负责扫描并注册 ServiceBean 的 BD

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {}public class ServiceAnnotationBeanPostProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware, ResourceLoaderAware, BeanClassLoaderAware {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {registerBeans(registry, DubboBootstrapApplicationListener.class);  Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {//1registerServiceBeans(resolvedPackagesToScan, registry);} else {if (logger.isWarnEnabled()) {logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");}}}
}  

registerServiceBeans 进行了如下 3 步

  • //1:声明对 @Service 注解的扫描
  • //2:执行扫描,这里已经获取了相关的 BeanDefinition,但是这是原始类的 BeanDefinition
  • //3:生成并注册 ServiceBeanBeanDefinition
private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {DubboClassPathBeanDefinitionScanner scanner = new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);scanner.setBeanNameGenerator(beanNameGenerator);//1scanner.addIncludeFilter(new AnnotationTypeFilter(Service.class));scanner.addIncludeFilter(new AnnotationTypeFilter(com.alibaba.dubbo.config.annotation.Service.class));for (String packageToScan : packagesToScan) {scanner.scan(packageToScan);//2Set<BeanDefinitionHolder> beanDefinitionHolders =findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {//3registerServiceBean(beanDefinitionHolder, registry, scanner); }// ......}
}

//2 处,会扫描指定包,按注解过滤其下所有 /**/*.class 文件,包装成 BeanDefinition 进而包装成 BeanDefinitionHolder 集合,如下

private Set<BeanDefinitionHolder> findServiceBeanDefinitionHolders(ClassPathBeanDefinitionScanner scanner, String packageToScan, BeanDefinitionRegistry registry,BeanNameGenerator beanNameGenerator) {//2.1Set<BeanDefinition> beanDefinitions = scanner.findCandidateComponents(packageToScan);Set<BeanDefinitionHolder> beanDefinitionHolders = new LinkedHashSet<>(beanDefinitions.size());for (BeanDefinition beanDefinition : beanDefinitions) {String beanName = beanNameGenerator.generateBeanName(beanDefinition, registry);BeanDefinitionHolder beanDefinitionHolder = new BeanDefinitionHolder(beanDefinition, beanName);beanDefinitionHolders.add(beanDefinitionHolder);}return beanDefinitionHolders;
}

//2.1 处,就已经扫描出了所有 BD,其核心逻辑如下
即遍历 registerServiceBeans //1 处声明的注解过滤器,逐个匹配 //2 处指定包下的所有类型

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {//...for (TypeFilter tf : this.includeFilters) {if (tf.match(metadataReader, this.metadataReaderFactory)) {return isConditionMatch(metadataReader);}}return false;
}

//3 处,registerServiceBean 方法负责注册 ServiceBean 的 BD

为什么会有两种BD ?
因为这俩是针对不同类的 BD。
假设有个类叫 A,它被标注了 @Service 注解。则,前者是 A 的实例定义信息,而后者是对应 A 的 ServiceBean 的实例定义信息


BD 是对 spring 里的 bean 的描述,其抽象实现 AbstractBeanDefinition 中,有一个成员 Object beanClass; 用于描述被定义的 Bean 的类型
这两个 BD 之间的逻辑说白了就是通过输入流读取指定包下 class 文件,然后筛选出带有对应注解的,包装成第一种 BD
这些 BD,都需要让 Spring 声明为 dubbo 的提供者,于是用原 BD 声明出 dubbo 使用的 ServiceBean 的 BD(第二种),并注册到 Spring 容器
最后在 Spring 的 refresh 环节完成 BD 到 SpringBean 的转换

private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,DubboClassPathBeanDefinitionScanner scanner) {Class<?> beanClass = resolveClass(beanDefinitionHolder);Annotation service = findServiceAnnotation(beanClass);AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes(service, false, false);Class<?> interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass);String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();//3.1AbstractBeanDefinition serviceBeanDefinition =buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);String beanName = generateServiceBeanName(serviceAnnotationAttributes, interfaceClass);if (scanner.checkCandidate(beanName, serviceBeanDefinition)) { // check duplicated candidate bean//3.2registry.registerBeanDefinition(beanName, serviceBeanDefinition);//...}
}

//3.1 组装 ServiceBean 的 BD,组装用的信息从原 BD 及其注解上获取
//3.2 将新的 BD 注册到 Spring 容器,默认由 DefaultListableBeanFactory 实现,核心代码就是下面两句
会同时加入两个集合:beanDefinitionMap / beanDefinitionNames

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {@Overridepublic void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {//...this.beanDefinitionMap.put(beanName, beanDefinition);this.beanDefinitionNames.add(beanName); // 可以视为 beanDefinitionMap 的索引//...}
}

生成 ServiceBean

生成 ServiceBean 是在 applicationContext.refresh() 中实现的,具体流程可以参考下面的脉络

  • 入口是 Springboot 启动类 SpringApplication.run(XxxApplication.class, args);
  • 触发容器刷新的方法也在 SpringApplication
    • refreshContext(context);
    • applicationContext.refresh();
  • 容器刷新的功能默认由 AbstractApplicationContext 实现
    • finishBeanFactoryInitialization(beanFactory);
    • beanFactory.preInstantiateSingletons();
  • preInstantiateSingletons 的实现在 DefaultListableBeanFactory

这个类刚刚提到过,最终 ServiceBean 的 BD 就在这个类的成员集合中
//1 处,就是上文提到的集合之一, preInstantiateSingletons 会遍历此集合
//2 处,beanDefinitionNames 中记录的每个 BD 都会通过此方法创建对象,本文讨论的话题中,最终生成的就是 ServiceBean

public void preInstantiateSingletons() throws BeansException {  List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); //1for (String beanName : beanNames) {RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {if (isFactoryBean(beanName)) { //... } else {getBean(beanName); //2}}}// smart 初始化逻辑,忽略
}

getBean 中相关的核心逻辑如下

// 从前文所属集合获取 BD,此方法最终是从 beanDefinitionMap 获取 BD
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// ...
//处理依赖(递归 getBean)
String[] dependsOn = mbd.getDependsOn();
// ... 
// 真正创建实例
if (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, () -> {  //在这里实际创建,会使用前面生成的 beanDefinition return createBean(beanName, mbd, args);});bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

服务导出

服务导出最终由 ServiceConfig.export() 实现(ServiceBeanServiceConfig 的子类),这就好办了,打个断点,可见如下
在这里插入图片描述
原生注解的服务导出由 DubboBootstrapApplicationListener 监听(实现了 ApplicationListener),由 Spring 的 ContextRefreshedEvent 事件触发

public class DubboBootstrapApplicationListener extends OneTimeExecutionApplicationContextEventListener implements Ordered {@Overridepublic void onApplicationContextEvent(ApplicationContextEvent event) {if (event instanceof ContextRefreshedEvent) {// 事件触发onContextRefreshedEvent((ContextRefreshedEvent) event);} else if (event instanceof ContextClosedEvent) {onContextClosedEvent((ContextClosedEvent) event);}}private void onContextRefreshedEvent(ContextRefreshedEvent event) {dubboBootstrap.start(); // dubbo 启动}
}

比较关键的代码摘要如下

public DubboBootstrap start() {if (started.compareAndSet(false, true)) {initialize();if (logger.isInfoEnabled()) {logger.info(NAME + " is starting...");}exportServices(); //从这里导出服务//...}return this;
}

里面的逻辑很好找重点,从 configManager 遍历,挨个执行 ServiceConfig.export()

private void exportServices() {//1 遍历configManager.getServices().forEach(sc -> {ServiceConfig serviceConfig = (ServiceConfig) sc;serviceConfig.setBootstrap(this);if (exportAsync) {//...挨个异步导出} else {//挨个同步导出sc.export();exportedServices.add(sc);}});
}

唯一的问题是 configManager 里面的东西是怎么来的。
答案是在 ServiceConfig/ServiceBean 的父类 AbstractConfig 上定义的初始化方法(继承关系有点复杂,如下图)
ServiceBean 初始化完成后会通过此方法把自己加入 ConfigManager,至于为什么这里叫做 ConfigManager,这是因为 dubbo 的各种对象都叫 xxxConfig

@PostConstruct
public void addIntoConfigManager() {ApplicationModel.getConfigManager().addConfig(this);
}

在这里插入图片描述



§3 原生 xml 方式接入

xml 方式接入时,可以推测其整个流程的最大不同在 BeanDefinition 阶段
原生注解接入时,dubbo 的服务是由注解标注生效,服务也是由注解标注声明,因此我们从注解开始找入口(如果找不到应该结合 Spring 的机制找相关的类)
xml 方式接入时,服务是从 xml 里声明的,入口也应该从 xml 开始找,尤其是其对应的解析器

入口 xmlns:dubbo

xml 方式入口位置很容易忽略,其实是 xml 父标签的 xmlns。
xmlns 用于声明 xml 的 namespace,用于定义其中的标签,以及其解析器,如下

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" (here)xmlns="http://www.springframework.org/schema/beans"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsdhttp://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
</beans>

http://dubbo.apache.org/schema/dubbo 直接访问是访问不了的,它实际上定义在 dubbo-config-spring 项目下的 spring.handlers

http\://dubbo.apache.org/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandler
http\://code.alibabatech.com/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandle

可见对应的是 DubboNamespaceHandler,作为 Dubbo 标签的解析器,解析器中明显可见如下代码
其初始化方法中注册了 BD 解析器,针对每种标签注册了一种,以 registry 为例,就对应 <dubbo:registry /> 标签
从下面代码可见,xml 依赖的解析器基本都是 DubboBeanDefinitionParser

从这里也可以看到,dubbo 解析后的类,基本全叫 xxxConfig

public class DubboNamespaceHandler extends NamespaceHandlerSupport implements ConfigurableSourceBeanMetadataElement {@Overridepublic void init() {registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));registerBeanDefinitionParser("config-center", new DubboBeanDefinitionParser(ConfigCenterBean.class, true));registerBeanDefinitionParser("metadata-report", new DubboBeanDefinitionParser(MetadataReportConfig.class, true));registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));registerBeanDefinitionParser("metrics", new DubboBeanDefinitionParser(MetricsConfig.class, true));registerBeanDefinitionParser("ssl", new DubboBeanDefinitionParser(SslConfig.class, true));registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));//1registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());}
}

扫描 BeanDefinition 阶段

DubboBeanDefinitionParser 见名知意,是用于将标签解析为 dubbo 的 BD 的解析器,既然是解析器其核心方法必然是 parse,入口断点调试可见
在这里插入图片描述

解析由 spring 触发 XmlBeanDefinitionReader, 在 DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions() 中执行
此方法的有效逻辑最终由 DubboBeanDefinitionParser.parse() 全权实现:解析标签为 BD 并完成注册

本帖相关核心代码如下
//1:这里的 beanClass 直接使用的成员变量,即 DubboBeanDefinitionParser 实例化时传来的 ServiceBean.class
//2:直接定义结果 BD,parse 方法的作用就是将标签 <dubbo:service/> 解析为 ServiceBean 的 BD,并完成 BD 的注册
//3:按 Dubbo 的规则生成 id,这个 id 会作为 ServiceBean 的 BD 的名字注册
//4:注册 ServiceBean 的 BD,parserContext.getRegistry() 实际返回的是 DefaultListableBeanFactory,与注解方式接入时一致
//5/6:与注解方式接入的一个区别在于此,xml 方式是现有 ServiceBean 的 BD,在注册时再去生成实际服务的 BD,而注解方式是相反的

public BeanDefinition parse(Element element, ParserContext parserContext) {registerDubboConfigAliasPostProcessor(parserContext.getRegistry());//1return parse(element, parserContext, beanClass, required); 
}private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) {//2RootBeanDefinition beanDefinition = new RootBeanDefinition(); beanDefinition.setBeanClass(beanClass); beanDefinition.setLazyInit(false);String id = element.getAttribute("id");//3if (StringUtils.isEmpty(id) && required) {//...} if (StringUtils.isNotEmpty(id)) {if (parserContext.getRegistry().containsBeanDefinition(id)) {throw new IllegalStateException("Duplicate spring bean id " + id);}//4parserContext.getRegistry().registerBeanDefinition(id, beanDefinition); beanDefinition.getPropertyValues().addPropertyValue("id", id);}if (ProtocolConfig.class.equals(beanClass)) {} else if (ServiceBean.class.equals(beanClass)) {String className = element.getAttribute("class");if (StringUtils.isNotEmpty(className)) {//5RootBeanDefinition classDefinition = new RootBeanDefinition(); classDefinition.setBeanClass(ReflectUtils.forName(className));classDefinition.setLazyInit(false);parseProperties(element.getChildNodes(), classDefinition);//6beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl")); }//...}//...
}

生成 ServiceBean

与原生注解方式一致


服务导出

与原生注解方式一致

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com