ToB企服应用市场:ToB评测及商务社交产业平台

标题: 重看Spring聚焦BeanFactory分析 [打印本页]

作者: 知者何南    时间: 2024-12-20 18:05
标题: 重看Spring聚焦BeanFactory分析
目次
一、理解BeanFactory
(一)功能性理解
(二)BeanFactory和它的子接口
(三)BeanFactory的实现类
二、BeanFactory根接口
(一)源码展示和理解
(二)基础特性总结
三、HierarchicalBeanFactory(分层 Bean 工厂)
(一)源码展示
(二)理解设计
(三)实际操作分析
四、ListableBeanFactory(可列表的 Bean 工厂)
(一)源码展示
(二)设计选择性枚举的目标
(三)案例举例
五、AutowireCapableBeanFactory(可主动装配)
(一)源码展示
(二)设计目标和应用
六、ConfigurableBeanFactory(可配置的 Bean 工厂)
(一)源码展示
(二)思考使用
七、总结

干货分享,感谢您的阅读!
在当代软件开发中,随着应用程序复杂性的不断增长,如何有效地管理对象的创建与依赖关系已成为开发者面临的一大寻衅。Spring 框架作为 Java 领域最受欢迎的开源框架之一,其核心特性之一便是通过 Inversion of Control(IoC)实现了松耦合的对象管理。在这此中,BeanFactory 接口扮演着至关重要的脚色,作为 Spring IoC 容器的基础,它负责创建、管理和配置 Java 对象(Bean),为开发者提供了灵活而强大的对象生命周期管理本领。
本文将深入探讨 Spring 框架中的 BeanFactory 及其多种子接口,剖析其设计理念、功能实现及在实际开发中的应用。通过对源代码的分析和具体示例的展示,我们将揭示 BeanFactory 如何在实现高效的依赖注入和资源管理方面发挥重要作用。无论您是 Spring 框架的初学者还是盼望深入了解其内部机制的资深开发者,本文都将为您提供宝贵的知识和实用的指导。
让我们一同揭开 BeanFactory 的神秘面纱,探索其在构建高效、可维护的 Java 应用程序中的独特魅力。
一、理解BeanFactory

(一)功能性理解

BeanFactory 是 Spring 框架中的一个核心接口,用于管理应用程序中的对象(也称为 bean)。它提供了一种将应用程序组件(如服务、数据源、控制器等)解耦并配置到应用程序中的方式。对 BeanFactory 的初步理解可以简单从以下几个方面来说说:
总的来说,可以初步理解BeanFactory 为 Spring 框架中负责管理 bean 对象的核心组件,它通过 IoC 容器、延迟加载、依赖注入和作用域管理等机制,实现了对象之间的解耦和灵活配置,从而提高了应用程序的可维护性、可测试性和可扩展性。
(二)BeanFactory和它的子接口


BeanFactory(Bean 工厂)

HierarchicalBeanFactory(分层 Bean 工厂)

ListableBeanFactory(可列表的 Bean 工厂)

AutowireCapableBeanFactory(可主动装配的 Bean 工厂)

ApplicationContext(应用上下文)

ConfigurableBeanFactory(可配置的 Bean 工厂)

ConfigurableApplicationContext(可配置的应用上下文)

ConfigurableListableBeanFactory(可配置且可列表的 Bean 工厂)

(三)BeanFactory的实现类


BeanFactory的结构体系非常巨大,在 Spring 中,BeanFactory 的实现类是容器的实际实行者,负责剖析 bean 的定义、创建 bean 实例以及管理 bean 的生命周期。今天主要针对
二、BeanFactory根接口

BeanFactory 是 Spring 框架中的一个核心接口,用于管理应用程序中的对象(也称为 bean)。它提供了一种将应用程序组件(如服务、数据源、控制器等)解耦并配置到应用程序中的方式。BeanFactory 负责创建、组装、配置和管理 bean 对象。这些 bean 可以通过 XML 文件、注解或 Java 代码进行配置。BeanFactory 还支持延迟加载、依赖注入和作用域管理等功能,以提高应用程序的性能、可维护性和可扩展性。
(一)源码展示和理解

  1. package org.springframework.beans.factory;
  2. import org.springframework.beans.BeansException;
  3. /**
  4. * 根接口,用于定义访问 Spring bean 容器的基本功能。
  5. * 这是 Spring IoC 容器的基础接口。
  6. */
  7. public interface BeanFactory {
  8.    
  9.     /**
  10.      * 根据给定的名称获取一个 bean 实例。
  11.      * @param name 要获取的 bean 的名称
  12.      * @return bean 实例
  13.      * @throws BeansException 如果找不到 bean 或 bean 创建过程中发生异常,则抛出 BeansException 异常
  14.      */
  15.     Object getBean(String name) throws BeansException;
  16.     /**
  17.      * 检查容器中是否包含具有给定名称的 bean。
  18.      * @param name 要检查的 bean 的名称
  19.      * @return 如果容器中包含具有给定名称的 bean,则返回 true;否则返回 false
  20.      */
  21.     boolean containsBean(String name);
  22.     /**
  23.      * 检查具有给定名称的 bean 是否为 singleton。
  24.      * @param name 要检查的 bean 的名称
  25.      * @return 如果该 bean 是 singleton,则返回 true;否则返回 false
  26.      * @throws NoSuchBeanDefinitionException 如果找不到具有给定名称的 bean,则抛出 NoSuchBeanDefinitionException 异常
  27.      */
  28.     boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
  29.     /**
  30.      * 检查具有给定名称的 bean 是否为 prototype。
  31.      * @param name 要检查的 bean 的名称
  32.      * @return 如果该 bean 是 prototype,则返回 true;否则返回 false
  33.      * @throws NoSuchBeanDefinitionException 如果找不到具有给定名称的 bean,则抛出 NoSuchBeanDefinitionException 异常
  34.      */
  35.     boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
  36.     /**
  37.      * 获取具有给定名称的 bean 的类型。
  38.      * @param name 要获取类型的 bean 的名称
  39.      * @return bean 的类型
  40.      * @throws NoSuchBeanDefinitionException 如果找不到具有给定名称的 bean,则抛出 NoSuchBeanDefinitionException 异常
  41.      */
  42.     Class<?> getType(String name) throws NoSuchBeanDefinitionException;
  43.     /**
  44.      * 获取具有给定名称的 bean 的别名。
  45.      * @param name 要获取别名的 bean 的名称
  46.      * @return bean 的别名数组(如果存在),否则返回空数组
  47.      * @throws NoSuchBeanDefinitionException 如果找不到具有给定名称的 bean,则抛出 NoSuchBeanDefinitionException 异常
  48.      */
  49.     String[] getAliases(String name) throws NoSuchBeanDefinitionException;
  50. }
复制代码
方法形貌getBean(String name)从容器中获取指定名称(或 ID)的 bean 实例。根据名称查找相应的 bean 定义,并根据需要实例化和初始化 bean 对象。containsBean(String name)检查容器是否包含具有给定名称的 bean。在内部查找 bean 定义的注册表,并返回相应的效果。isSingleton(String name)检查具有给定名称的 bean 是否为 singleton。返回 true 表现单例,false 表现非单例。根据定义中的作用域信息确定 bean 是否为 singleton。isPrototype(String name)检查具有给定名称的 bean 是否为 prototype(原型)。返回 true 表现原型,false 表现非原型。根据定义中的作用域信息确定 bean 是否为 prototype。getType(String name)获取具有给定名称的 bean 的范例。查找 bean 的定义,并返回相应的 bean 范例信息。getAliases(String name)获取具有给定名称的 bean 的别名(如果存在)。别名是指在容器中注册的其他名称,可以用来引用相同的 bean 实例。查找 bean 的定义,并返回相应的别名列表。 (二)基础特性总结

特性形貌Bean 实例管理管理应用程序中的各种 bean 实例。提供获取、检查和操作 bean 实例的方法。Bean 实例化和初始化根据配置信息实例化和初始化 bean。根据配置信息找到相应的 bean 定义,并进行实例化和初始化。支持不同的作用域支持 singleton(单例)和 prototype(原型)等不同作用域。提供方法检查指定 bean 的作用域,并根据需要获取相应作用域的 bean 实例。范例信息获取提供方法获取 bean 的范例信息。允许获取指定名称的 bean 的范例。别名管理支持别名管理,允许为 bean 定义设置多个别名。提供方法获取指定 bean 的全部别名列表。 三、HierarchicalBeanFactory(分层 Bean 工厂)

(一)源码展示

HierarchicalBeanFactory 接口是 BeanFactory 接口的子接口之一,定义了一种层次化的 BeanFactory 结构,可以用于实现 bean 定义的继续和覆盖。以下是 接口的部分源码展示:
  1. public interface HierarchicalBeanFactory extends BeanFactory {
  2.    
  3.     // 获取该 BeanFactory 的父级 BeanFactory
  4.     @Nullable
  5.     BeanFactory getParentBeanFactory();
  6.    
  7.     // 判断该 BeanFactory 是否包含给定名称的本地 bean 定义
  8.     boolean containsLocalBean(@NonNull String name);
  9. }
复制代码
(二)理解设计

HierarchicalBeanFactory 接口设计的核生理念在于提供一种层次化的 bean 定义管理机制,允许在多个 BeanFactory 之间建立父子关系,实现 bean 定义的继续和覆盖。
HierarchicalBeanFactory 的设计充分表现了 Spring 框架的核生理念:依赖注入、松耦合、模块化和可扩展性。通过提供一种层次化的 bean 定义管理机制,使得可以更加灵活地管理和配置 bean,从而实现更加健壮和灵活的应用程序。
(三)实际操作分析

假设我们有一个 Web 应用程序,此中有两个配置文件 applicationContext.xml 和 webApplicationContext.xml,分别定义了应用程序的核心组件和 Web 相关的组件。

在这种环境下,我们可以使用 HierarchicalBeanFactory 来管理这两个配置文件中的 bean 定义。具体实现如下:
  1. public class MainApplicationContext implements HierarchicalBeanFactory {
  2.     private DefaultListableBeanFactory coreBeanFactory;
  3.     private DefaultListableBeanFactory webBeanFactory;
  4.     public MainApplicationContext() {
  5.         // 初始化核心 BeanFactory
  6.         coreBeanFactory = new DefaultListableBeanFactory();
  7.         XmlBeanDefinitionReader coreReader = new XmlBeanDefinitionReader(coreBeanFactory);
  8.         coreReader.loadBeanDefinitions("classpath:applicationContext.xml");
  9.         // 初始化 Web BeanFactory,设置核心 BeanFactory 为父级
  10.         webBeanFactory = new DefaultListableBeanFactory(coreBeanFactory);
  11.         XmlBeanDefinitionReader webReader = new XmlBeanDefinitionReader(webBeanFactory);
  12.         webReader.loadBeanDefinitions("classpath:webApplicationContext.xml");
  13.     }
  14.     @Override
  15.     public BeanFactory getParentBeanFactory() {
  16.         return coreBeanFactory;
  17.     }
  18.     @Override
  19.     public boolean containsLocalBean(String name) {
  20.         return coreBeanFactory.containsBeanDefinition(name) || webBeanFactory.containsBeanDefinition(name);
  21.     }
  22.     @Override
  23.     public Object getBean(String name) throws BeansException {
  24.         try {
  25.             return webBeanFactory.getBean(name);
  26.         } catch (NoSuchBeanDefinitionException e) {
  27.             return coreBeanFactory.getBean(name);
  28.         }
  29.     }
  30. }
复制代码
具体验证可以按实际写测试用例来打印自己相关的配置类。
四、ListableBeanFactory(可列表的 Bean 工厂)

ListableBeanFactory 是 Spring 框架中的另一个重要接口,它是 BeanFactory 接口的子接口之一。与 BeanFactory 不同的是,ListableBeanFactory 不仅可以获取单个的 bean 实例,还可以获取 bean 的列表,并提供了更丰富的检索功能。
(一)源码展示

ListableBeanFactory 接口通过源码中这些方法提供了一种便捷的方式来获取 bean 实例列表,使得应用程序可以更加灵活地管理和操作 bean 实例。通过调用这些方法,可以快速地获取符合条件的 bean 实例,从而方便地进行批量操作、批量注册等操作。
  1. public interface ListableBeanFactory extends BeanFactory {
  2.     // 根据类型获取所有匹配的 bean 实例
  3.     <T> Map<String, T> getBeansOfType(@Nullable Class<T> type) throws BeansException;
  4.     // 获取所有 bean 的名称
  5.     String[] getBeanDefinitionNames();
  6.     // 根据类型获取所有 bean 的名称
  7.     String[] getBeanNamesForType(@Nullable Class<?> type);
  8.     // 根据类型和包含的父级 BeanFactory 获取所有 bean 的名称
  9.     String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit);
  10. }
复制代码
(二)设计选择性枚举的目标

设计选择性枚举的目标是为了突出重点,集中于最具代表性或最为关键的设计方面,以便更清晰地阐明问题或理解设计。通过有选择地枚举设计特性,可以避免肴杂或过于复杂的形貌,使得可以或许更快速地理解和把握关键信息。实际应用中可能相对使用的较少,一般我只在不理解有些bean没有见效的时候才会使用下,但是对于其目标而言,不同看源码的人有不同的理解,这里不做区别!
(三)案例举例

简单定义一个服务接口 UserService , AdminUserService 和RegularUserService是其对应的两个实现类。通过 ListableBeanFactory 来验证 Spring 容器中的 bean 实例是否符合预期。
定义 UserService 接口及其两个实现类:
  1. public interface UserService {
  2.     String getUserType();
  3. }
  4. @Component
  5. public class AdminUserService implements UserService {
  6.     @Override
  7.     public String getUserType() {
  8.         return "Admin";
  9.     }
  10. }
  11. @Component
  12. public class RegularUserService implements UserService {
  13.     @Override
  14.     public String getUserType() {
  15.         return "Regular";
  16.     }
  17. }
复制代码
创建一个测试类来验证容器中的 bean 实例:
  1. @SpringBootTest
  2. public class UserServiceTest {
  3.     @Autowired
  4.     private ListableBeanFactory beanFactory;
  5.     @Test
  6.     public void testBeanDefinitionNames() {
  7.         // 获取容器中所有的 bean 名称
  8.         String[] beanNames = beanFactory.getBeanDefinitionNames();
  9.         // 验证容器中是否包含了我们定义的两个 bean 实例的名称
  10.         assertTrue(Arrays.asList(beanNames).contains("adminUserService"));
  11.         assertTrue(Arrays.asList(beanNames).contains("regularUserService"));
  12.     }
  13.     @Test
  14.     public void testGetBeansOfType() {
  15.         // 根据类型获取容器中所有的 UserService 实例
  16.         Map<String, UserService> userServiceMap =
  17.                     beanFactory.getBeansOfType(UserService.class);
  18.         // 验证容器中是否包含了我们定义的两个 UserService 实例
  19.         assertEquals(2, userServiceMap.size());
  20.         assertTrue(userServiceMap.containsKey("adminUserService"));
  21.         assertTrue(userServiceMap.containsKey("regularUserService"));
  22.     }
  23. }
复制代码
五、AutowireCapableBeanFactory(可主动装配)

AutowireCapableBeanFactory 是 BeanFactory 接口的子接口之一, 提供了更高级的功能,可以手动实现主动装配、实例化和初始化 bean,以及其他与 bean 相关的操作。
(一)源码展示

AutowireCapableBeanFactory 接口提供了一系列方法,允许手动实现主动装配、实例化和初始化 bean。
  1. public interface AutowireCapableBeanFactory extends BeanFactory {
  2.     // 将给定的现有 bean 实例应用 autowire bean 属性值
  3.     void autowireBean(Object existingBean) throws BeansException;
  4.     // 根据给定 bean 的类别自动装配其属性值
  5.     Object autowire(Class<?> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;
  6.     // 创建一个新的 bean 实例,并根据定义初始化该 bean
  7.     Object createBean(Class<?> beanClass) throws BeansException;
  8.     // 根据给定的 bean 类型和 AutowireMode 创建一个新的 bean 实例
  9.     Object autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck) throws BeansException;
  10.     // 初始化给定的 bean,应用所有 bean 后处理器
  11.     Object initializeBean(Object existingBean, String beanName) throws BeansException;
  12.     // 销毁给定的 bean 实例
  13.     void destroyBean(Object existingBean);
  14.     // 在 bean 工厂关闭时调用,用于销毁所有缓存的单例 bean
  15.     void destroySingletons();
  16. }
复制代码

(二)设计目标和应用

AutowireCapableBeanFactory 接口的设计目标是为了提供更高级的、更灵活的 bean 操作和管理功能。它主要用于那些需要在运行时动态创建、初始化或者主动装配 bean 实例的场景,以及需要更精细地控制 bean 创建过程的场景。
在 Spring 框架中,确实有很多环境下我们不需要直接操作 AutowireCapableBeanFactory 接口。通常环境下,通过配置文件或者注解来定义 bean,并由 Spring 容器负责管理和实例化这些 bean,而无需手动干预 bean 的创建过程。因此,AutowireCapableBeanFactory这个API一般不需要我们去操纵,因为正常开发中不会使用,但如果需要获取AutowireCapableBeanFactory,可以通过ApplicationContext间接获取。
六、ConfigurableBeanFactory(可配置的 Bean 工厂)

ConfigurableBeanFactory 接口提供了一些方法来配置 Bean 工厂的行为和属性,使得我们可以动态地管理和调解 Bean 工厂的配置,从而更好地满足应用程序的需求。通过对 Bean 工厂的配置,可以实现更灵活、更定制化的 Bean 管理方式。
(一)源码展示

ConfigurableBeanFactory 接口提供了一系列操作 Bean 工厂的方法,使得我们可以动态地管理和调解 Bean 工厂的配置。
  1. public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry {
  2.     // 设置父 Bean 工厂
  3.     void setParentBeanFactory(BeanFactory parentBeanFactory) throws IllegalStateException;
  4.     // 设置类加载器
  5.     void setBeanClassLoader(ClassLoader beanClassLoader);
  6.     // 获取类加载器
  7.     ClassLoader getBeanClassLoader();
  8.     // 设置是否缓存 bean 的元数据
  9.     void setCacheBeanMetadata(boolean cacheBeanMetadata);
  10.     // 是否缓存 bean 的元数据
  11.     boolean isCacheBeanMetadata();
  12.     // 设置 Bean 表达式解析器
  13.     void setBeanExpressionResolver(BeanExpressionResolver resolver);
  14.     // 获取 Bean 表达式解析器
  15.     BeanExpressionResolver getBeanExpressionResolver();
  16.     // 设置属性编辑器注册表
  17.     void setPropertyEditorRegistrar(PropertyEditorRegistrar registrar);
  18.     // 获取属性编辑器注册表
  19.     PropertyEditorRegistrar getPropertyEditorRegistrar();
  20.     // 添加 BeanPostProcessor 实例
  21.     void addBeanPostProcessor(BeanPostProcessor beanPostProcessor);
  22.     // 注册一个 Scope,用于管理作用域
  23.     void registerScope(String scopeName, Scope scope);
  24.     // 获取注册的 Scope
  25.     Scope getRegisteredScope(String scopeName);
  26.     // 获取当前 BeanFactory 使用的作用域集合
  27.     String[] getRegisteredScopeNames();
  28.     // 注册一个 BeanDefinition,将其添加到 BeanFactory 中
  29.     void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException;
  30.     // 移除指定名称的 BeanDefinition
  31.     void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
  32.     // 获取指定名称的 BeanDefinition
  33.     BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
  34.     // 判断是否包含指定名称的 BeanDefinition
  35.     boolean containsBeanDefinition(String beanName);
  36.     // 获取所有的 BeanDefinition 的名称
  37.     String[] getBeanDefinitionNames();
  38. }
复制代码
(二)思考使用

对之前的UserService 接口和两个实现类:AdminUserService 和 RegularUserService。使用 ConfigurableBeanFactory 来注册 Bean 定义,并配置 Bean 的作用域和属性。
创建一个配置类 BeanConfig,在此中使用 ConfigurableBeanFactory 注册 Bean 定义并配置作用域:
  1. import org.springframework.beans.factory.config.ConfigurableBeanFactory;
  2. import org.springframework.context.annotation.Bean;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.context.annotation.Scope;
  5. @Configuration
  6. public class BeanConfig {
  7.     @Bean
  8.     @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) // 设置作用域为原型
  9.     public UserService regularUserService() {
  10.         return new RegularUserService();
  11.     }
  12.     @Bean
  13.     @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) // 设置作用域为单例
  14.     public UserService adminUserService() {
  15.         return new AdminUserService();
  16.     }
  17. }
复制代码
regularUserService() 方法配置了作用域为原型,即每次请求该 Bean 时都会创建一个新的实例;而 adminUserService() 方法配置了作用域为单例,即整个应用程序中只会创建一个实例。创建一个测试类来验证配置是否见效:
  1. import org.junit.jupiter.api.Test;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.boot.test.context.SpringBootTest;
  4. @SpringBootTest
  5. public class UserServiceTest {
  6.     @Autowired
  7.     private UserService regularUserService;
  8.     @Autowired
  9.     private UserService adminUserService;
  10.     @Test
  11.     public void testBeanScope() {
  12.         UserService user1 = regularUserService;
  13.         UserService user2 = regularUserService;
  14.         UserService user3 = adminUserService;
  15.         UserService user4 = adminUserService;
  16.         // 验证作用域是否生效
  17.         assertNotSame(user1, user2); // 不同的请求应该返回不同的实例
  18.         assertSame(user3, user4); // 单例作用域应该返回相同的实例
  19.     }
  20. }
复制代码
其他临时先不做分析应用了。
七、总结

在本文中,我们深入探讨了 Spring 框架中的 BeanFactory 及其相关子接口的重要性。作为 Spring IoC 容器的基础,BeanFactory 不仅负责对象的创建与管理,还为开发者提供了灵活的依赖注入机制,从而促进了代码的可维护性和可测试性。
通过对 BeanFactory 接口及其实现类的分析,我们了解到其在资源管理和对象生命周期控制方面的关键作用。无论是通过 XmlBeanFactory、DefaultListableBeanFactory 还是 AbstractApplicationContext,Spring 提供了多种选择,以满足不同场景下的需求。这种灵活性使得开发者可以或许根据具体应用的复杂性和需求,选择最合适的容器实现。
同时,我们也认识到,在当代微服务架构和云原生应用中,理解和把握 Spring 框架的核心组件是必不可少的。BeanFactory 不仅是 Spring 生态系统的基石,也是实现高效、可扩展和可维护应用程序的关键地点。
总之,深入理解 BeanFactory 的工作原理与应用场景,将使开发者在构建和维护 Java 应用时更具优势。随着技能的不断进步,持续学习和应用 Spring 框架的最佳实践,将为开发者开辟更广阔的技能视野,推动软件开发的创新与发展。

参考文章
BeanFactory (Spring Framework 6.1.5 API)
Guide to the Spring BeanFactory | Baeldung
https://www.youtube.com/watch?v=tH6rd_OkClM
【Spring源码分析】带你正视一下Spring祖容器之BeanFactory的原理与功能分析(1)_spring_洛神灬殇_InfoQ写作社区
https://www.cnblogs.com/zrtqsk/p/4028453.html
BeanFactory体系结构分析 · 进击的java菜鸟
Spring源码分析之BeanFactory体系结构.md-阿里云开发者社区
Spring-BeanFactory源码分析(一)_setcurrentlyincreation-CSDN博客
Spring源码剖析:BeanFactory深入理解-CSDN博客
BeanFactory getBean源码分析 | 奕林的技能博客
深入剖析 Spring 核心数据结构:BeanFactory - "地瓜哥"博客网
Spring的IOC,你真的能表明清晰吗? - 掘金





免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4